mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-29 16:12:33 +00:00
ios, android, desktop: online search asks before Load more and retry while Directory search is off, clears results on any profile switch, and its consent alert claims only what holds
- The load and retry rows send the search text again, so they now go through confirmDirectorySearch like the first search: a user who turned the switch off after searching is asked before anything more is sent. - The resets on a text change and a profile switch move from the search bar to ChatListView, which owns the text and the results. The search bar is a different view in onboarding and in the list (and a lazy row there), so a profile switch that also changed which one shows, as a notification tap can, left the previous profile's results and Load more on screen, and Load more would have sent its text and cursor through the new profile's servers. - The alert no longer says the directory "can't build your search history": each search's reply queue is on one of the user's servers, which for a user of their own server alone, or with a server of the directory's own operator, can link searches. The rest of the message is unchanged.
This commit is contained in:
1 parent
5cd3a2575d
commit
d57abd904d
11 files changed
+36
-34
No files matched your search
@@ -236,6 +236,13 @@ struct ChatListView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
// here, not in the search bar: a profile switch can replace the search bar, and the new one never sees the change
|
||||
.onChange(of: searchText) { _ in
|
||||
onlineSearch.reset()
|
||||
}
|
||||
.onChange(of: chatModel.currentUser?.userId) { _ in
|
||||
onlineSearch.reset()
|
||||
}
|
||||
.environmentObject(chatTagsModel)
|
||||
}
|
||||
|
||||
@@ -911,12 +918,6 @@ struct ChatListSearchBar: View {
|
||||
.onChange(of: searchFocussed) { sf in
|
||||
withAnimation { searchMode = sf }
|
||||
}
|
||||
.onChange(of: searchText) { _ in
|
||||
onlineSearch.reset()
|
||||
}
|
||||
.onChange(of: m.currentUser?.userId) { _ in
|
||||
onlineSearch.reset()
|
||||
}
|
||||
.onChange(of: searchText) { t in
|
||||
if ignoreSearchTextChange {
|
||||
ignoreSearchTextChange = false
|
||||
|
||||
@@ -250,7 +250,7 @@ struct DirectoryLoadMoreRow: View {
|
||||
|
||||
var body: some View {
|
||||
Button {
|
||||
Task { await onlineSearch.loadMore() }
|
||||
confirmDirectorySearch { Task { await onlineSearch.loadMore() } }
|
||||
} label: {
|
||||
HStack(spacing: 4) {
|
||||
Image(systemName: "plus.magnifyingglass")
|
||||
@@ -276,7 +276,7 @@ struct DirectoryRetryRow: View {
|
||||
|
||||
var body: some View {
|
||||
Button {
|
||||
Task { await onlineSearch.retry() }
|
||||
confirmDirectorySearch { Task { await onlineSearch.retry() } }
|
||||
} label: {
|
||||
HStack(spacing: 4) {
|
||||
Image(systemName: "arrow.clockwise")
|
||||
@@ -329,7 +329,7 @@ func confirmDirectorySearch(_ search: @escaping () -> Void) {
|
||||
private func showDirectorySearchAlert(onSearch: @escaping () -> Void) {
|
||||
showAlert(
|
||||
NSLocalizedString("Enable Directory search?", comment: "alert title"),
|
||||
message: NSLocalizedString("Search text is sent to SimpleX Directory, each search separately and without your profile, so it can't build your search history.", comment: "alert message"),
|
||||
message: NSLocalizedString("Search text is sent to SimpleX Directory, each search separately and without your profile.", comment: "alert message"),
|
||||
actions: {[
|
||||
UIAlertAction(title: NSLocalizedString("Cancel", comment: "alert action"), style: .cancel),
|
||||
UIAlertAction(
|
||||
|
||||
@@ -153,7 +153,7 @@
|
||||
|
||||
### RULE-24: Online results are never saved
|
||||
**Rule:** Directory results MUST NOT be stored, and MUST live only as long as the search text and the active profile: any text change or profile switch clears them, and a reply that arrives after that (or after the search was cancelled) MUST NOT be shown.
|
||||
**Enforced by:** `DirectorySearchView.swift` (`OnlineSearchModel.reset()` bumps `generation`; every publish checks it); `ChatListView.swift` (`ChatListSearchBar` resets on `searchText` and `currentUser?.userId` changes); `NewChatMenuButton.swift` (`NewChatSheet` resets on the same changes).
|
||||
**Enforced by:** `DirectorySearchView.swift` (`OnlineSearchModel.reset()` bumps `generation`; every publish checks it); `ChatListView.swift` (`ChatListView`, which owns the search text and the model, resets on `searchText` and `currentUser?.userId` changes); `NewChatMenuButton.swift` (`NewChatSheet` resets on the same changes).
|
||||
**Spec:** [spec/client/chat-list.md](../spec/client/chat-list.md#11-online-search)
|
||||
|
||||
### RULE-25: Only group and channel links are accepted from the directory
|
||||
@@ -173,5 +173,5 @@
|
||||
|
||||
### RULE-28: Consent before the first online search
|
||||
**Rule:** The search text MUST NOT be sent online while the "Directory search" switch in Privacy settings is off, unless the user confirms the "Enable Directory search?" alert, which says the text goes to SimpleX Directory and turns the switch on. Cancel MUST send nothing. The search never resolves SimpleX names, so the directory is its only recipient.
|
||||
**Enforced by:** `DirectorySearchView.swift` (`confirmDirectorySearch`, `DEFAULT_PRIVACY_DIRECTORY_SEARCH`); `PrivacySettings.swift` (the switch); both triggers in `ChatListView.swift` and `NewChatMenuButton.swift` go through it.
|
||||
**Enforced by:** `DirectorySearchView.swift` (`confirmDirectorySearch`, `DEFAULT_PRIVACY_DIRECTORY_SEARCH`); `PrivacySettings.swift` (the switch); both triggers in `ChatListView.swift` and `NewChatMenuButton.swift` go through it, and so do the load and retry rows (`DirectoryLoadMoreRow`, `DirectoryRetryRow`).
|
||||
**Spec:** [spec/client/chat-list.md](../spec/client/chat-list.md#11-online-search)
|
||||
@@ -46,7 +46,7 @@ Searching beyond the user's own chats happens only when the user asks, and the r
|
||||
| Keyboard Search key | The same directory search. It never looks up SimpleX names |
|
||||
| "Connect to …" row | Shown for a SimpleX name (e.g. `alice` or `#news.testing`), and stays next to the results; it is the only way to look up a name |
|
||||
| "Directory search" switch | In Settings › Privacy › Chats, after "Send link previews"; off by default |
|
||||
| "Enable Directory search?" alert | Every search asks while the switch is off: search text is sent to SimpleX Directory, each search separately and without the user's profile, so it can't build a search history. Cancel sends nothing; Enable turns the switch on and searches. Once it is on, searches don't ask |
|
||||
| "Enable Directory search?" alert | Every search asks while the switch is off: search text is sent to SimpleX Directory, each search separately and without the user's profile. Cancel sends nothing; Enable turns the switch on and searches. Once it is on, searches don't ask |
|
||||
| Results | One list without headers below the local chats. First your chats among the results that the list above doesn't show (for example because the name doesn't match the typed text), as their regular chat rows; then the other results, with the same avatar size as chat rows: avatar, `#` name, the directory's SimpleX name, description, then the member or subscriber count and "Open" on the right. A join still in progress without a chat shows "Already joining" in place of the count and no "Open" |
|
||||
| Open | Tapping a result (or its "Open") loads the group's or channel's profile, with the spinner in the search field, and opens the chat straight away, with its Join button and no "Open channel?" question; a channel's banner shows its subscriber count. The user joins from there. If the link's signature was checked or its name also leads to another chat, the question shows first, as for a pasted link, with the signature result or the other chat's button. A result that is already joining shows the same sheet or alert as pasting its link would |
|
||||
| Long press | On iOS 16 and later, a long press on a result shows a menu with Open, previewing the row; iOS 15 has no menu |
|
||||
|
||||
@@ -324,7 +324,7 @@ Tapping the card opens [`GetStakeView`](../../Shared/Views/Onboarding/WhatsNewVi
|
||||
|
||||
**Files**: `Shared/Views/ChatList/DirectorySearchView.swift` (model and rows), `Shared/Model/DirectorySearch.swift` (response and result types), `Shared/Views/ChatList/ChatListView.swift` (triggers and rendering)
|
||||
|
||||
The search text can be sent online on request to SimpleX Directory, to find public groups and channels. Results are shown below the local chats as one list without headers: your chats first, then the other results. They are never saved and live only as long as the search text and the active profile: [`ChatListSearchBar`](../../Shared/Views/ChatList/ChatListView.swift#L823) calls `reset()` on every change of `searchText` ([L921](../../Shared/Views/ChatList/ChatListView.swift#L914)) and of `currentUser?.userId` ([L924](../../Shared/Views/ChatList/ChatListView.swift#L917)). The New chat sheet uses the same model and rows (see [Navigation, New Chat Online Search](navigation.md#12-new-chat-online-search)).
|
||||
The search text can be sent online on request to SimpleX Directory, to find public groups and channels. Results are shown below the local chats as one list without headers: your chats first, then the other results. They are never saved and live only as long as the search text and the active profile: [`ChatListView`](../../Shared/Views/ChatList/ChatListView.swift#L154), which owns `searchText` and the model, calls `reset()` from `viewBody`, which is shown in every state, on every change of `searchText` ([L240](../../Shared/Views/ChatList/ChatListView.swift#L240)) and of `currentUser?.userId` ([L243](../../Shared/Views/ChatList/ChatListView.swift#L243)). The search bar is a different instance in the onboarding branch and in the list, so a profile switch that also switches between them would replace the bar before it could see the change. The New chat sheet uses the same model and rows (see [Navigation, New Chat Online Search](navigation.md#12-new-chat-online-search)).
|
||||
|
||||
### Triggers
|
||||
|
||||
@@ -333,7 +333,7 @@ The search text can be sent online on request to SimpleX Directory, to find publ
|
||||
| Keyboard Search key (`.submitLabel(.search)`) | [`runDirectorySearch()`](../../Shared/Views/ChatList/ChatListView.swift#L1019) | `searchDirectory(text)` |
|
||||
| "Search in Directory" row ([`SearchInDirectoryRow`](../../Shared/Views/ChatList/DirectorySearchView.swift#L163)) | [`runDirectorySearch()`](../../Shared/Views/ChatList/ChatListView.swift#L1019) | `searchDirectory(text)` |
|
||||
|
||||
Both triggers run the same directory search, which never resolves SimpleX names: a name is looked up only by "Connect to …" (`ConnectByNameRow`). The field neither autocorrects nor capitalizes, so the Search key searches what was typed. Both triggers do nothing for empty (trimmed) text or a pasted SimpleX link, and both go through [`confirmDirectorySearch()`](../../Shared/Views/ChatList/DirectorySearchView.swift#L321), which runs the search at once when the "Directory search" switch is on (`DEFAULT_PRIVACY_DIRECTORY_SEARCH`, default false, in Privacy settings › Chats after "Send link previews") and otherwise shows the alert [`showDirectorySearchAlert`](../../Shared/Views/ChatList/DirectorySearchView.swift#L329): "Enable Directory search?", with the message "Search text is sent to SimpleX Directory, each search separately and without your profile, so it can't build your search history." and two actions, Cancel (sends nothing) and Enable (turns the switch on, then searches). So every search asks while the switch is off. The key is not in `hintDefaults`: "Reset all hints" leaves the switch as it is.
|
||||
Both triggers run the same directory search, which never resolves SimpleX names: a name is looked up only by "Connect to …" (`ConnectByNameRow`). The field neither autocorrects nor capitalizes, so the Search key searches what was typed. Both triggers do nothing for empty (trimmed) text or a pasted SimpleX link, and both go through [`confirmDirectorySearch()`](../../Shared/Views/ChatList/DirectorySearchView.swift#L321), which runs the search at once when the "Directory search" switch is on (`DEFAULT_PRIVACY_DIRECTORY_SEARCH`, default false, in Privacy settings › Chats after "Send link previews") and otherwise shows the alert [`showDirectorySearchAlert`](../../Shared/Views/ChatList/DirectorySearchView.swift#L329): "Enable Directory search?", with the message "Search text is sent to SimpleX Directory, each search separately and without your profile." and two actions, Cancel (sends nothing) and Enable (turns the switch on, then searches). So every search asks while the switch is off. The key is not in `hintDefaults`: "Reset all hints" leaves the switch as it is.
|
||||
|
||||
The rows above or below the search field follow the toolbar mode. "Search in Directory" hides while results for the current text show or load (`!onlineSearch.showResults`); "Connect to …" stays next to them:
|
||||
|
||||
@@ -421,8 +421,8 @@ So nothing is shown twice: a result that is a chat the list shows is hidden, a c
|
||||
|-----|------|-----|
|
||||
| Your chat | Each loaded chat of `rows.yourChats`, drawn with the chat list's own row, [`chatListNavLink(_:)`](../../Shared/Views/ChatList/ChatListView.swift#L637) (`ChatListNavLink` with the local chats' modifiers) | as a local chat: opens it |
|
||||
| Result | [`DirectorySearchRow`](../../Shared/Views/ChatList/DirectorySearchView.swift#L185) with `imageSize: dynamicSize(userFont).profileImageSize`, the chat rows' avatar size (63 pt at the default text size): avatar, `#` + name, the directory's SimpleX name claim as plain text, description (2 lines), then the caption: the member or subscriber count, or "Already joining" for `.joining` (secondary colour); trailing "Open" (primary colour, one line, fixed size), except for `.joining` | [`openResult(_:)`](../../Shared/Views/ChatList/ChatListView.swift#L503): `openDirectoryResult(result, dismiss: false)`, `cleanup`: `refreshStates()` (see Opening a result) |
|
||||
| Load more | [`DirectoryLoadMoreRow`](../../Shared/Views/ChatList/DirectorySearchView.swift#L247) via [`directoryLoadMoreRow()`](../../Shared/Views/ChatList/ChatListView.swift#L655): `plus.magnifyingglass` and "Load more · %d left" when `moreCount` is above 0, else "Load more"; disabled and in the secondary colour while `loading` | `loadMore()` |
|
||||
| Retry | [`DirectoryRetryRow`](../../Shared/Views/ChatList/DirectorySearchView.swift#L273) via [`directoryRetryRow()`](../../Shared/Views/ChatList/ChatListView.swift#L661): `arrow.clockwise` and "Search failed, tap to retry" | `retry()` |
|
||||
| Load more | [`DirectoryLoadMoreRow`](../../Shared/Views/ChatList/DirectorySearchView.swift#L247) via [`directoryLoadMoreRow()`](../../Shared/Views/ChatList/ChatListView.swift#L655): `plus.magnifyingglass` and "Load more · %d left" when `moreCount` is above 0, else "Load more"; disabled and in the secondary colour while `loading` | `loadMore()`, through `confirmDirectorySearch` |
|
||||
| Retry | [`DirectoryRetryRow`](../../Shared/Views/ChatList/DirectorySearchView.swift#L273) via [`directoryRetryRow()`](../../Shared/Views/ChatList/ChatListView.swift#L661): `arrow.clockwise` and "Search failed, tap to retry" | `retry()`, through `confirmDirectorySearch` |
|
||||
|
||||
On iOS 16 and later a long press on a result shows a context menu, [`DirectoryResultMenu`](../../Shared/Views/ChatList/DirectorySearchView.swift#L290), with one action, Open (`message` icon), which does what a tap does. Its preview is the row without its Open, padded, the screen width less 32 pt, on the theme background. The modifier is applied before the row's flip, so in the one-hand UI the preview is drawn upright (checked in the iOS simulator tests). iOS 15 has no menu; a tap still opens.
|
||||
|
||||
|
||||
+7
-6
@@ -244,6 +244,13 @@ fun ChatListView(chatModel: ChatModel, userPickerState: MutableStateFlow<Animate
|
||||
val searchChatFilteredBySimplexLink = remember { mutableStateOf<Set<String>>(emptySet()) }
|
||||
val connectNameCandidate = remember { mutableStateOf<String?>(null) }
|
||||
val onlineSearch = remember { OnlineSearchModel() }
|
||||
// here, not in the search bar: a profile switch can replace the search bar, and the new one never sees the change
|
||||
KeyChangeEffect(searchText.value.text) {
|
||||
onlineSearch.reset()
|
||||
}
|
||||
KeyChangeEffect(chatModel.currentUser.value?.userId) {
|
||||
onlineSearch.reset()
|
||||
}
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
if (oneHandUI.value) {
|
||||
ChatListWithLoadingScreen(searchText, listState, searchShowingSimplexLink, searchChatFilteredBySimplexLink, connectNameCandidate, onlineSearch)
|
||||
@@ -904,12 +911,6 @@ private fun ChatListSearchBar(listState: LazyListState, searchText: MutableState
|
||||
focusManager.clearFocus()
|
||||
}
|
||||
}
|
||||
KeyChangeEffect(searchText.value.text) {
|
||||
onlineSearch.reset()
|
||||
}
|
||||
KeyChangeEffect(chatModel.currentUser.value?.userId) {
|
||||
onlineSearch.reset()
|
||||
}
|
||||
val view = LocalMultiplatformView()
|
||||
LaunchedEffect(Unit) {
|
||||
snapshotFlow { searchText.value.text }
|
||||
|
||||
+2
-2
@@ -262,7 +262,7 @@ fun DirectoryLoadMoreRow(onlineSearch: OnlineSearchModel) {
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(enabled = !onlineSearch.loading) {
|
||||
withApi { onlineSearch.loadMore() }
|
||||
confirmDirectorySearch { withApi { onlineSearch.loadMore() } }
|
||||
}
|
||||
.padding(horizontal = DEFAULT_PADDING, vertical = DEFAULT_PADDING_HALF),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
@@ -282,7 +282,7 @@ fun DirectoryRetryRow(onlineSearch: OnlineSearchModel) {
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
withApi { onlineSearch.retry() }
|
||||
confirmDirectorySearch { withApi { onlineSearch.retry() } }
|
||||
}
|
||||
.padding(horizontal = DEFAULT_PADDING, vertical = DEFAULT_PADDING_HALF),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
|
||||
@@ -516,7 +516,7 @@
|
||||
<string name="search_or_paste_simplex_link">Search or paste SimpleX link</string>
|
||||
<string name="search_in_directory">Search in Directory</string>
|
||||
<string name="enable_directory_search_question">Enable Directory search?</string>
|
||||
<string name="enable_directory_search_alert_desc">Search text is sent to SimpleX Directory, each search separately and without your profile, so it can\'t build your search history.</string>
|
||||
<string name="enable_directory_search_alert_desc">Search text is sent to SimpleX Directory, each search separately and without your profile.</string>
|
||||
<string name="enable_verb">Enable</string>
|
||||
<string name="directory_search_n_members">%1$d members</string>
|
||||
<string name="directory_search_already_joining">Already joining</string>
|
||||
|
||||
@@ -261,11 +261,11 @@ This document specifies invariants enforced by the Android and Desktop (Kotlin/C
|
||||
|
||||
**Invariant:** Directory results MUST NOT be stored, and MUST live only as long as the search text and the active profile: any text change or profile switch clears them, and a reply that arrives after that (or after the search was cancelled) MUST NOT be shown.
|
||||
|
||||
**Enforcement:** `OnlineSearchModel.reset()` bumps `generation`, and every publish in `request` and `refreshStates` checks it; `cancelRequest()` bumps it too. `ChatListSearchBar` and `NewChatSheetLayout` call `reset()` from `KeyChangeEffect`s on the search text and on `chatModel.currentUser.value?.userId`. The model is kept in Compose `remember` only, so it is also dropped when its screen leaves composition (e.g. an Android rotation).
|
||||
**Enforcement:** `OnlineSearchModel.reset()` bumps `generation`, and every publish in `request` and `refreshStates` checks it; `cancelRequest()` bumps it too. `ChatListView` (which owns the search text and the model, not the search bar) and `NewChatSheetLayout` call `reset()` from `KeyChangeEffect`s on the search text and on `chatModel.currentUser.value?.userId`. The model is kept in Compose `remember` only, so it is also dropped when its screen leaves composition (e.g. an Android rotation).
|
||||
|
||||
**Location:**
|
||||
- `common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt` -- `OnlineSearchModel`
|
||||
- `common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt` -- `ChatListSearchBar`
|
||||
- `common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt` -- `ChatListView`
|
||||
- `common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt` -- `NewChatSheetLayout`
|
||||
|
||||
**Spec:** [spec/client/chat-list.md](../spec/client/chat-list.md#11-online-search)
|
||||
@@ -317,10 +317,10 @@ This document specifies invariants enforced by the Android and Desktop (Kotlin/C
|
||||
|
||||
**Invariant:** The search text MUST NOT be sent online while the "Directory search" switch in Privacy settings is off, unless the user confirms the "Enable Directory search?" alert, which says the text goes to SimpleX Directory and turns the switch on. Cancel MUST send nothing. The search never resolves SimpleX names, so the directory is its only recipient.
|
||||
|
||||
**Enforcement:** Both triggers (the "Search in Directory" row and the Search key / Enter) on the chat list and in New chat go through `confirmDirectorySearch()`, which runs the search at once only when `AppPreferences.privacyDirectorySearch` is on and otherwise shows the alert; Enable sets the preference, then searches. The preference is a setting, not a hint, so "Reset all hints" leaves it as it is.
|
||||
**Enforcement:** Both triggers (the "Search in Directory" row and the Search key / Enter) on the chat list and in New chat go through `confirmDirectorySearch()`, and so do the load and retry rows (`DirectoryLoadMoreRow`, `DirectoryRetryRow`), shared by both screens; it runs the search at once only when `AppPreferences.privacyDirectorySearch` is on and otherwise shows the alert; Enable sets the preference, then searches. The preference is a setting, not a hint, so "Reset all hints" leaves it as it is.
|
||||
|
||||
**Location:**
|
||||
- `common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt` -- `confirmDirectorySearch()`, `showDirectorySearchAlert()`
|
||||
- `common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt` -- `confirmDirectorySearch()`, `showDirectorySearchAlert()`, `DirectoryLoadMoreRow()`, `DirectoryRetryRow()`
|
||||
- `common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt` -- `AppPreferences.privacyDirectorySearch`
|
||||
- `common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/PrivacySettings.kt` -- the switch
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ Searching beyond the user's own chats happens only when the user asks, and the r
|
||||
| Keyboard Search key | On Android the keyboard's Search action, on desktop Enter. The same directory search; it never looks up SimpleX names |
|
||||
| "Connect to …" row | Shown for a SimpleX name (e.g. `alice` or `#news.testing`; on Android with the top bar it takes the tags' place), and stays next to the results; it is the only way to look up a name |
|
||||
| "Directory search" switch | In Settings › Privacy › Chats, after "Send link previews"; off by default |
|
||||
| "Enable Directory search?" alert | Every search asks while the switch is off: search text is sent to SimpleX Directory, each search separately and without the user's profile, so it can't build a search history. Enable turns the switch on and searches; Cancel sends nothing. Once it is on, searches don't ask |
|
||||
| "Enable Directory search?" alert | Every search asks while the switch is off: search text is sent to SimpleX Directory, each search separately and without the user's profile. Enable turns the switch on and searches; Cancel sends nothing. Once it is on, searches don't ask |
|
||||
| Results | One list without headers below the local chats. First your chats among the results that the list above doesn't show (for example because the name doesn't match the typed text), as their regular chat rows; then the other results, laid out as chat rows with the same avatar size: avatar, `#` name, the directory's SimpleX name, description, then the member or subscriber count and "Open" on the right. A join still in progress without a chat shows "Already joining" in place of the count and no "Open" |
|
||||
| Open | Tapping a result loads the group's or channel's profile and opens the chat straight away, with its Join button and no "Open channel?" question; a channel's banner shows its subscriber count. The user joins from there. If the link's signature was checked or its name also leads to another chat, the question shows first, as for a pasted link, with the signature result or the other chat's button. A result that is already joining shows the same alert as pasting its link would |
|
||||
| Result menu | A long press (or a right click on desktop) on a result shows a menu with Open |
|
||||
|
||||
@@ -374,7 +374,7 @@ Tapping the card runs `openGetStake()`, which opens `GetStakeView(showFirstImage
|
||||
|
||||
**Files:** `views/chatlist/DirectorySearchView.kt` (model and rows), `model/DirectorySearch.kt` (response and result types), `views/chatlist/ChatListView.kt` (triggers and rendering), `views/helpers/SearchTextField.kt` (Search key)
|
||||
|
||||
The search text can be sent online on request to SimpleX Directory, to find public groups and channels. Results are shown below the local chats as one list without headers: your chats first, then the other results. They are never saved and live only as long as the search text and the active profile: [`ChatListSearchBar`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt#L863-L966) calls `reset()` from a `KeyChangeEffect` on `searchText.value.text` and another on `chatModel.currentUser.value?.userId` ([source](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt#L907-L912)). The model is a plain `remember` in [`ChatListView`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt#L246), so an Android rotation (which recreates the Activity) drops the results while the `rememberSaveable` search text stays. The New chat sheet uses the same model and rows (see [Navigation, New Chat Online Search](navigation.md#10-new-chat-online-search)).
|
||||
The search text can be sent online on request to SimpleX Directory, to find public groups and channels. Results are shown below the local chats as one list without headers: your chats first, then the other results. They are never saved and live only as long as the search text and the active profile: [`ChatListView`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt#L218), which owns the search text and the model, calls `reset()` from a `KeyChangeEffect` on `searchText.value.text` and another on `chatModel.currentUser.value?.userId` ([source](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt#L248-L253)). The search bar is a different instance in the onboarding branch and in the list, and in the list a lazy item that leaves composition off screen, so a profile switch could replace or skip the bar before it saw the change. The model is a plain `remember` in [`ChatListView`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt#L246), so an Android rotation (which recreates the Activity) drops the results while the `rememberSaveable` search text stays. The New chat sheet uses the same model and rows (see [Navigation, New Chat Online Search](navigation.md#10-new-chat-online-search)).
|
||||
|
||||
### Triggers
|
||||
|
||||
@@ -385,7 +385,7 @@ The search text can be sent online on request to SimpleX Directory, to find publ
|
||||
|
||||
`SearchTextField` handles the key per platform: on Android through `KeyboardActions(onSearch)` ([source](../../common/src/commonMain/kotlin/chat/simplex/common/views/helpers/SearchTextField.kt#L116-L121)), on desktop through `onPreviewKeyEvent` for `Key.Enter` / `Key.NumPadEnter` on `KeyUp` ([source](../../common/src/commonMain/kotlin/chat/simplex/common/views/helpers/SearchTextField.kt#L95-L102)), so one key press runs the search once. Every caller keeps `ImeAction.Search`; a caller without `onSearch` gets no handler. The field neither autocorrects nor capitalizes (`KeyboardOptions(capitalization = None, autoCorrect = false)`), so the Search key searches what was typed; being shared, this reaches every `SearchTextField`, unlike iOS, which changed only the chat list and New chat fields.
|
||||
|
||||
Both triggers run the same directory search, which never resolves SimpleX names: a name is looked up only by "Connect to …" (`ConnectByNameRow`). Both do nothing for empty (trimmed) text or a pasted SimpleX link ([`searchTrimmed`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt#L981-L982), `searchShowingSimplexLink`), and both go through [`confirmDirectorySearch()`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L326-L332), which runs the search at once when the "Directory search" switch is on (`appPrefs.privacyDirectorySearch`, key "PrivacyDirectorySearch", default false, in Privacy settings › Chats after "Send link previews") and otherwise shows the alert [`showDirectorySearchAlert`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L334-L356) (`showAlertDialogButtonsColumn` with `parseHtml = false`): "Enable Directory search?", with the message "Search text is sent to SimpleX Directory, each search separately and without your profile, so it can't build your search history." and two buttons, Enable (turns the switch on, then searches) and Cancel (sends nothing), in that order, as this alert's column already placed Cancel last. So every search asks while the switch is off. The preference is not in `hintPreferences`: "Reset all hints" leaves the switch as it is. Each trigger launches the model method with `withApi`.
|
||||
Both triggers run the same directory search, which never resolves SimpleX names: a name is looked up only by "Connect to …" (`ConnectByNameRow`). Both do nothing for empty (trimmed) text or a pasted SimpleX link ([`searchTrimmed`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt#L981-L982), `searchShowingSimplexLink`), and both go through [`confirmDirectorySearch()`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L326-L332), which runs the search at once when the "Directory search" switch is on (`appPrefs.privacyDirectorySearch`, key "PrivacyDirectorySearch", default false, in Privacy settings › Chats after "Send link previews") and otherwise shows the alert [`showDirectorySearchAlert`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L334-L356) (`showAlertDialogButtonsColumn` with `parseHtml = false`): "Enable Directory search?", with the message "Search text is sent to SimpleX Directory, each search separately and without your profile." and two buttons, Enable (turns the switch on, then searches) and Cancel (sends nothing), in that order, as this alert's column already placed Cancel last. So every search asks while the switch is off. The preference is not in `hintPreferences`: "Reset all hints" leaves the switch as it is. Each trigger launches the model method with `withApi`.
|
||||
|
||||
The rows next to the search field share one slot with the tags, laid out by [`TagsOrConnectByName`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt#L1291-L1347) and [`ChatListSearchBarWithTags`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt#L1208-L1237). "Search in Directory" hides while results for the current text show or load (`!onlineSearch.showResults`); "Connect to …" stays next to them:
|
||||
|
||||
@@ -483,8 +483,8 @@ The onboarding `Column` takes the toolbar and navigation-bar paddings and consum
|
||||
|-----|------|-----|
|
||||
| Your chat | Each loaded chat of `rows.yourChats`, drawn with the chat list's own row, `ChatListNavLinkView(chat, remember { mutableStateOf(false) })` | as a local chat: opens it |
|
||||
| Result | [`DirectoryResultNavLink`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L297-L318)`(result, imageSize = 72.dp * fontSizeSqrtMultiplier, close = null, cleanup = { withApi { refreshStates() } })`: `ChatListNavLinkLayout` with [`DirectorySearchRow`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L204-L254) as its preview, so the row gets the chat rows' padding and divider and the chat rows' avatar size. The row: avatar, `#` + name, the directory's SimpleX name claim as plain text (truncates first), description (2 lines), then the caption (`body2`, secondary colour): the member or subscriber count, or "Already joining" for `Joining`; trailing "Open" (`open_verb`, primary colour, one line, never wrapped), except for `Joining` | `hideKeyboard(view)`, then [`openDirectoryResult`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L321-L324) (see Opening a result) |
|
||||
| Load more | [`DirectoryLoadMoreRow`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L258-L276): `ic_zoom_in` (the magnifier with a plus, as iOS's `plus.magnifyingglass`) and "Load more · %1$d left" (`directory_search_load_more_n_left`) when `moreCount` is above 0, else "Load more"; not clickable and in the secondary colour while `loading` | `withApi { loadMore() }` |
|
||||
| Retry | [`DirectoryRetryRow`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L280-L292): `ic_refresh` and "Search failed, tap to retry" | `withApi { retry() }` |
|
||||
| Load more | [`DirectoryLoadMoreRow`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L258-L276): `ic_zoom_in` (the magnifier with a plus, as iOS's `plus.magnifyingglass`) and "Load more · %1$d left" (`directory_search_load_more_n_left`) when `moreCount` is above 0, else "Load more"; not clickable and in the secondary colour while `loading` | `confirmDirectorySearch { withApi { loadMore() } }` |
|
||||
| Retry | [`DirectoryRetryRow`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L280-L292): `ic_refresh` and "Search failed, tap to retry" | `confirmDirectorySearch { withApi { retry() } }` |
|
||||
|
||||
A result's menu is `ChatListNavLinkLayout`'s dropdown, opened by a long press, or by a right click on desktop, with one item, `ItemAction(open_verb, ic_chat_bubble)`, which does what a tap does. It has no lifted preview, unlike the iOS context menu, so `DirectorySearchRow` has no `showsOpen` parameter. Text from the directory is rendered with `Text(String)` (never parsed as markdown) with line limits.
|
||||
|
||||
|
||||
Reference in new issue
Block a user