mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-22 17:31:55 +00:00
android, desktop: document online search in spec and product docs
apps/multiplatform/CODE.md requires spec and product updates with every code change. Mirror the iOS online search docs in the Kotlin docs' own formats and numbering, stating where Android and desktop differ, and fix the ConnectProgressManager signatures the branch's owner parameter had made stale. Run: 003-kotlin-directory-search, slice 6
This commit is contained in:
1 parent
d65c68c075
commit
33bae2658a
14 files changed
+426
-42
No files matched your search
@@ -89,9 +89,9 @@ Common Module (commonMain)
|
||||
| Database | [spec/database.md](database.md) | SQLite database files, migrations, encryption, backup/restore |
|
||||
| Impact | [spec/impact.md](impact.md) | Source file → product concept mapping for change impact analysis |
|
||||
| Chat View | [spec/client/chat-view.md](client/chat-view.md) | ChatView, ChatItemView, message rendering, item interactions |
|
||||
| Chat List | [spec/client/chat-list.md](client/chat-list.md) | ChatListView, ChatPreviewView, filtering, search, tags |
|
||||
| Chat List | [spec/client/chat-list.md](client/chat-list.md) | ChatListView, ChatPreviewView, filtering, search, tags, online search (SimpleX Directory and names) |
|
||||
| Compose | [spec/client/compose.md](client/compose.md) | ComposeView, SendMsgView, ComposeState, attachments, mentions |
|
||||
| Navigation | [spec/client/navigation.md](client/navigation.md) | App screen routing, onboarding, settings, new chat flows |
|
||||
| Navigation | [spec/client/navigation.md](client/navigation.md) | App screen routing, onboarding, settings, new chat flows, New chat online search |
|
||||
| Calls | [spec/services/calls.md](services/calls.md) | WebRTC call lifecycle, signaling, platform-specific call views |
|
||||
| Files | [spec/services/files.md](services/files.md) | File transfer (SMP inline / XFTP), CryptoFile encryption, platform file paths |
|
||||
| Notifications | [spec/services/notifications.md](services/notifications.md) | NtfManager, SimplexService, notification channels, background delivery |
|
||||
|
||||
@@ -15,10 +15,12 @@
|
||||
- 2.9 [Chat Tags](#29-chat-tags)
|
||||
- 2.10 [Server Operators](#210-server-operators)
|
||||
- 2.11 [Archive](#211-archive)
|
||||
- 2.12 [Service Requests](#212-service-requests)
|
||||
3. [Response Types](#3-response-types)
|
||||
4. [Event Types](#4-event-types)
|
||||
5. [Error Types](#5-error-types)
|
||||
6. [Source Files](#6-source-files)
|
||||
7. [Directory Search and Connection Plans](#7-directory-search-and-connection-plans)
|
||||
|
||||
---
|
||||
|
||||
@@ -180,7 +182,7 @@ All functions below are `suspend fun` members of `ChatController` ([SimpleXAPI.k
|
||||
| `apiAddContact` | `rh: Long?, incognito: Boolean` | Create a one-time invitation link for a new contact | [L1444](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L1444) |
|
||||
| `apiSetConnectionIncognito` | `rh: Long?, connId: Long, incognito: Boolean` | Toggle incognito on a pending connection | [L1455](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L1455) |
|
||||
| `apiChangeConnectionUser` | `rh: Long?, connId: Long, userId: Long` | Change the user profile on a pending connection | [L1464](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L1464) |
|
||||
| `apiConnectPlan` | `rh: Long?, connLink: String, inProgress: MutableState<Boolean>` | Analyze a connection link before connecting | [L1474](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L1474) |
|
||||
| `apiConnectPlan` | `rh: Long?, connLink: String, resolveMode: PlanResolveMode = PRMUnknown, linkOwnerSig: LinkOwnerSig? = null, inProgress: MutableState<Boolean>, onFailure: ConnectPlanFailure = Alert` | Analyze a connection link (or resolve a SimpleX name) before connecting; returns `ConnectionPlanResult?`. Failure handling: see [section 7](#7-directory-search-and-connection-plans) | [L1654](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L1654-L1668) |
|
||||
| `apiConnect` | `rh: Long?, incognito: Boolean, connLink: CreatedConnLink` | Connect via an invitation or address link | [L1482](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L1482) |
|
||||
| `apiPrepareContact` | `rh, connLink, contactShortLinkData` | Prepare a contact chat from a short link (before connecting) | [L1546](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L1546) |
|
||||
| `apiPrepareGroup` | `rh, connLink, groupShortLinkData` | Prepare a group chat from a short link (before connecting) | [L1555](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L1555) |
|
||||
@@ -288,6 +290,12 @@ class ArchiveConfig(
|
||||
)
|
||||
```
|
||||
|
||||
### 2.12 Service Requests
|
||||
|
||||
| Command | Parameters | Description | Line |
|
||||
|---------|-----------|-------------|------|
|
||||
| `apiSearchDirectory` | `rh: Long?, text: String, cursor: JsonObject?` | Sends [`CC.APISendServiceRequest`](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L4069) (`userId, target, timeoutSec: Double?, request: JsonObject`; `/_service_request`) to SimpleX Directory and waits up to the timeout for its response, `CRServiceResponse`; see [section 7](#7-directory-search-and-connection-plans) | [L1645](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L1645-L1652) |
|
||||
|
||||
---
|
||||
|
||||
<a id="API"></a>
|
||||
@@ -322,6 +330,7 @@ The `CR` sealed class ([SimpleXAPI.kt#L6114](../common/src/commonMain/kotlin/cha
|
||||
| Remote ctrl | `RemoteCtrlList`, `RemoteCtrlFound`, `RemoteCtrlConnected`, `RemoteCtrlStopped` | L6264-L6269 |
|
||||
| Encryption | `ContactPQAllowed`, `ContactPQEnabled` | L6271-L6272 |
|
||||
| Misc | `CmdOk`, `ArchiveExported`, `ArchiveImported`, `AppSettingsR`, `VersionInfo` | L6274-L6283 |
|
||||
| Service | `CRServiceResponse` (`user, responseData: JsonObject`): a service's response to `APISendServiceRequest`, left undecoded for the caller | [L6791](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L6791) |
|
||||
| Fallback | `Response` (unknown type + raw JSON), `Invalid` (unparseable) | L6282-L6283 |
|
||||
|
||||
Each `CR` subclass is annotated with `@Serializable @SerialName("jsonTag")` for polymorphic JSON deserialization.
|
||||
@@ -432,5 +441,20 @@ sealed class ArchiveError {
|
||||
| Cryptor.kt | Platform-expect encryption interface | `common/src/commonMain/kotlin/chat/simplex/common/platform/Cryptor.kt` |
|
||||
| Cryptor.android.kt | Android: AndroidKeyStore AES-GCM encryption | `common/src/androidMain/kotlin/chat/simplex/common/platform/Cryptor.android.kt` |
|
||||
| Cryptor.desktop.kt | Desktop: placeholder (no-op) encryption | `common/src/desktopMain/kotlin/chat/simplex/common/platform/Cryptor.desktop.kt` |
|
||||
| DirectorySearch.kt | Directory request and response, online search result types | `common/src/commonMain/kotlin/chat/simplex/common/model/DirectorySearch.kt` |
|
||||
|
||||
---
|
||||
|
||||
## 7. Directory Search and Connection Plans
|
||||
|
||||
`ChatController` functions in [`SimpleXAPI.kt`](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt) used by the online search (see [Chat List, Online Search](client/chat-list.md#11-online-search)):
|
||||
|
||||
| Function | Description | Line |
|
||||
|----------|-------------|------|
|
||||
| `apiSearchDirectory(rh, text, cursor)` | Sends `CC.APISendServiceRequest` to [`DIRECTORY_SERVICE_LINK`](../common/src/commonMain/kotlin/chat/simplex/common/model/DirectorySearch.kt#L16) with [`DIRECTORY_SEARCH_TIMEOUT_SEC`](../common/src/commonMain/kotlin/chat/simplex/common/model/DirectorySearch.kt#L19) and the request built by [`directorySearchRequest`](../common/src/commonMain/kotlin/chat/simplex/common/model/DirectorySearch.kt#L70-L74); returns [`parseDirectorySearchResponse`](../common/src/commonMain/kotlin/chat/simplex/common/model/DirectorySearch.kt#L78-L87)`(responseData)`, or null on any error (logged with `responseType` and `details`). Blocks for up to the timeout; uses plain `sendCmd`, so a timeout gives a retry row rather than the alert `sendCmdWithRetry` would show | [L1645](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L1645-L1652) |
|
||||
| `apiConnectPlan(rh, connLink, resolveMode, linkOwnerSig, inProgress, onFailure)` | Plans a connection; returns `ConnectionPlanResult?`. `NotResolvedLocally` (a `PRMNever` lookup that matched nothing) returns null silently; any other failure is handled by `onFailure` | [L1654](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L1654-L1668) |
|
||||
| `ConnectPlanFailure` | `Alert` (default, every existing caller): `apiConnectResponseAlert`, only while `inProgress.value` is true, as before; `Log`: a debug log only, used by the online search so name resolution and per-entry lookups never raise alerts | [L7522](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L7522) |
|
||||
|
||||
A service response is carried as a kotlinx `JsonObject` (any JSON), so the API layer does not depend on a service's own types; `parseDirectorySearchResponse` reads its fields and decodes each entry with `json.decodeFromJsonElement`.
|
||||
|
||||
All paths are relative to `apps/multiplatform/`.
|
||||
@@ -1,6 +1,6 @@
|
||||
# Chat List Specification
|
||||
|
||||
Source: `common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt`
|
||||
Source: `common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt`, `common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt`, `common/src/commonMain/kotlin/chat/simplex/common/model/DirectorySearch.kt`
|
||||
|
||||
---
|
||||
|
||||
@@ -16,6 +16,7 @@ Source: `common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatLis
|
||||
8. [UserPicker](#8-userpicker)
|
||||
9. [Source Files](#9-source-files)
|
||||
10. [Crowdfunding Banner](#10-crowdfunding-banner)
|
||||
11. [Online Search](#11-online-search)
|
||||
|
||||
---
|
||||
|
||||
@@ -152,6 +153,7 @@ The `filteredChats` function (line ~1474) applies filters in this order:
|
||||
- Text input with search icon.
|
||||
- SimpleX link detection: When a pasted string contains a single SimpleX link, it triggers `planAndConnect` for connection, suppressing normal search.
|
||||
- Unread filter toggle button (right side, when search is empty).
|
||||
- Searching SimpleX Directory and resolving SimpleX names online happen only on request, below the local results: see [Online Search](#11-online-search).
|
||||
|
||||
---
|
||||
|
||||
@@ -314,6 +316,8 @@ Uses `AnimatedViewState` (`GONE`, `VISIBLE`, `HIDING`) with a `MutableStateFlow`
|
||||
| `TagListView.kt` | Tag management and assignment view |
|
||||
| `UserPicker.kt` | User switching side panel |
|
||||
| `GetStakeBanner.kt` | Crowdfunding banner and the shared banner card chrome |
|
||||
| `DirectorySearchView.kt` | Online search model, Names and Directory rows, first-search alert |
|
||||
| `model/DirectorySearch.kt` | Directory response parsing and online result types |
|
||||
|
||||
---
|
||||
|
||||
@@ -358,3 +362,121 @@ Tapping the card runs `openGetStake()`, which opens `GetStakeView(showFirstImage
|
||||
### Shared card chrome
|
||||
|
||||
`Modifier.bannerCard(onTap)` (size state, gradient brush, `heightIn`, `clip`, `background`, `clickable`) and `BannerDismissButton` are declared separately from `GetStakeBanner` so other banners can adopt the same chrome; the paddings stay with the caller. The gradient reuses `gradientPoints`, `lightStops` and `darkStops` from `views/newchat/OnboardingCards.kt`.
|
||||
|
||||
---
|
||||
|
||||
<a id="OnlineSearch"></a>
|
||||
|
||||
## 11. Online Search
|
||||
|
||||
**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, and, when the text is a SimpleX name, to the user's name-resolving server. Results are shown below the local chats in two parts, Names then Directory. 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#L866-L970) 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#L911-L916)). 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
|
||||
|
||||
| Trigger | Handler | Runs |
|
||||
|---------|---------|------|
|
||||
| Search key: the keyboard's Search action on Android, Enter (key up) on desktop, through [`SearchTextField`](../../common/src/commonMain/kotlin/chat/simplex/common/views/helpers/SearchTextField.kt#L33-L43)`(onSearch)` | [`runOnlineSearch()`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt#L995-L1000) | `search(text, name = connectNameCandidate.value)`: the directory's first page, and name resolution when the text is a name candidate |
|
||||
| "Search in Directory" row ([`SearchInDirectoryRow`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L202-L217)) | [`runDirectorySearch()`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt#L988-L993) | `searchDirectory(text)`: the directory only |
|
||||
|
||||
`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.
|
||||
|
||||
Both triggers do nothing for empty (trimmed) text or a pasted SimpleX link ([`searchTrimmed`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt#L985-L986), `searchShowingSimplexLink`), and both go through [`confirmDirectorySearch()`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L327-L333), which runs the search at once when `appPrefs.directorySearchAlertShown` is set and otherwise shows the three-button alert ([`showDirectorySearchAlert`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L335-L363), `showAlertDialogButtonsColumn` with `parseHtml = false` so the paragraph break shows): Search / Search and don't show again / Cancel. Its message names both recipients: the directory, and, for a SimpleX name, the server that resolves names. The preference is in `hintPreferences`, so "Reset all hints" brings the alert back. 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#L1298-L1352) and [`ChatListSearchBarWithTags`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt#L1219-L1244):
|
||||
|
||||
| Search text | Android, one-hand UI (slot above the field) | Android, top bar (slot below the field) | Desktop (either mode) |
|
||||
|-------------|-----------------------------------|------------------------------|------|
|
||||
| Empty | Tags | Tags | Tags |
|
||||
| Plain text | "Search in Directory" | Tags, then "Search in Directory" | Tags and "Search in Directory" |
|
||||
| Name candidate, no name results yet | "Connect to …" and "Search in Directory" | "Connect to …" (in the tags' place), then "Search in Directory" | Tags, "Connect to …" and "Search in Directory" |
|
||||
| Name candidate with name results | "Search in Directory" | Tags, then "Search in Directory" | Tags and "Search in Directory" |
|
||||
| Pasted SimpleX link | Tags | Tags | Tags |
|
||||
|
||||
On desktop the rows are passed to the caller's `desktopView`, which places them between the tags and the field: above the field with the bottom toolbar, below it with the top toolbar.
|
||||
|
||||
<a id="OnlineSearchModel"></a>
|
||||
|
||||
### OnlineSearchModel
|
||||
|
||||
**Location:** [`OnlineSearchModel`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L31-L199)
|
||||
|
||||
A plain class holding Compose state, owned by `ChatListView` as `remember { OnlineSearchModel() }` and passed through `ChatListWithLoadingScreen` to `ChatList`, `ChatListSearchBarWithTags`, `ChatListSearchBar` and `TagsOrConnectByName`. Every method runs on the main thread: the triggers and rows launch them with `withApi` (`Dispatchers.Main`), and the spinner's `onCancel` hops back with `withApi { cancelRequest() }`; the API calls move the FFI work to `Dispatchers.IO` themselves. This keeps every read and write of `generation` on one thread.
|
||||
|
||||
| State | Description |
|
||||
|-------|-------------|
|
||||
| `names: List<NameSearchResult>` | Name results for the current text |
|
||||
| `directory: List<DirectoryResult>` | Directory entries shown so far, each with the local chat its link refers to |
|
||||
| `loading` | A directory request is in flight |
|
||||
| `resolvingNames` | Name resolution is in flight |
|
||||
| `failed` | The last directory request failed |
|
||||
| `searched` | A directory request for the current text completed, successfully or not (tells "not searched" from "found nothing") |
|
||||
| `cursor` (private) | Opaque `JsonObject?` from the last page, echoed back for the next one; null when there are no more. Snapshot state, because `footer` reads it |
|
||||
| `generation` (private) | Bumped by `reset()` and `cancelRequest()`; a reply whose captured generation differs never writes model state |
|
||||
|
||||
The public state has private setters; `searchedText`, `generation` and `spinnerShown` are plain fields.
|
||||
|
||||
| Computed | Definition |
|
||||
|----------|------------|
|
||||
| [`searching`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L56) | `loading \|\| resolvingNames` |
|
||||
| [`showResults`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L57) | `searching \|\| searched \|\| names.isNotEmpty()` |
|
||||
| [`footer`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L58) | `DirectoryFooter.Retry` when `failed`, else `ShowMore` when a cursor exists, else null |
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| [`reset()`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L60-L70) | Clears all results and flags, bumps `generation` (text change, profile switch) |
|
||||
| [`cancelRequest()`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L73-L77) | The spinner's `onCancel`: bumps `generation` and clears `loading` and `resolvingNames`; results already shown stay |
|
||||
| [`search(text, name)`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L79-L96) | Search key: ignored for empty text or while `searching` for the same text; otherwise `reset()`, then the first directory page and, with a name, name resolution concurrently (`coroutineScope { async { resolveNames(name, gen) } }`, with the generation captured before the child coroutine starts) |
|
||||
| [`searchDirectory(text)`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L98-L111) | The row: ignored while `loading` for the same text; for the same text it clears only the directory state (name results stay), for new text it resets; then the first page |
|
||||
| [`loadMore()`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L113-L116) | "Show more": the next page, appended |
|
||||
| [`retry()`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L119-L125) | The retry row: resumes the page when a cursor exists, else repeats the first page; no reset |
|
||||
| [`rows(shownChatIds)`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L127-L139) | The rows to render, given the ids of the local chats shown (see De-duplication) |
|
||||
|
||||
**Spinner.** The private setters of `loading` and `resolvingNames` call [`updateSpinner()`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L185-L198) on every write, which starts the "Searching directory…" progress with owner `ConnectProgressOwner.DirectorySearch` when `searching` becomes true and stops it when it becomes false (`spinnerShown` keeps the last state, so repeated writes do nothing). The chat list shows it in the toolbar ([`ChatListToolbar`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt#L639-L645)) once it has run for a second, not in the search field. The spinner is shared with connections: [`ConnectProgressOwner`](../../common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt#L50) records whose it is, [`startConnectProgress`](../../common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt#L60-L69) does not let a search take it from a running connection, and [`stopConnectProgress`](../../common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt#L72-L78) ignores a stop from another owner. Tapping a result calls [`planAndConnect`](../../common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ConnectPlan.kt#L24-L55), whose `cancelConnectProgress()` ([source](../../common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt#L81-L88)) cancels the search through its `onCancel` before the connection takes the spinner. Clearing the search text does the same through the typing path.
|
||||
|
||||
### Directory requests
|
||||
|
||||
[`request(append)`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L159-L183) reads `chatModel.remoteHostId()` and calls [`apiSearchDirectory`](../../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L1645-L1652), which sends `APISendServiceRequest` to [`DIRECTORY_SERVICE_LINK`](../../common/src/commonMain/kotlin/chat/simplex/common/model/DirectorySearch.kt#L16) with a [10-second timeout](../../common/src/commonMain/kotlin/chat/simplex/common/model/DirectorySearch.kt#L19) and the request built by [`directorySearchRequest`](../../common/src/commonMain/kotlin/chat/simplex/common/model/DirectorySearch.kt#L70-L74) (`searchText`, and `searchCursor` for later pages). [`parseDirectorySearchResponse`](../../common/src/commonMain/kotlin/chat/simplex/common/model/DirectorySearch.kt#L78-L87) accepts only a `searchResults` response and decodes its entries one at a time inside `runCatching`, so an entry the app cannot decode is dropped and the rest of the page is kept.
|
||||
|
||||
[`DirectorySearchEntry`](../../common/src/commonMain/kotlin/chat/simplex/common/model/DirectorySearch.kt#L33-L57) keeps the wire field `groupLink` private; its [`link`](../../common/src/commonMain/kotlin/chat/simplex/common/model/DirectorySearch.kt#L47-L56) is a `@Transient` property whose initializer, run by the generated decoder, takes `groupLink.connShortLink ?: groupLink.connFullLink` and throws `SerializationException` unless [`simplexLinkType`](../../common/src/commonMain/kotlin/chat/simplex/common/model/DirectorySearch.kt#L59-L62) classifies it as `SimplexLinkType.group` or `SimplexLinkType.channel`. Being `@Transient`, `link` cannot be set by a `link` key in the JSON. `simplexLinkType` returns the link type only when [`strConnectTarget`](../../common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatView.kt#L834) finds exactly one SimpleX link whose text is the whole string. `link` is also the entry's `id`. The directory sends only the short link when an entry has one.
|
||||
|
||||
For each new entry of a page (not already shown, not repeated in the page), the request looks up the local chat its link refers to with `apiConnectPlan(rh, entry.link, resolveMode = PRMNever, inProgress = mutableStateOf(false), onFailure = ConnectPlanFailure.Log)` and [`planLocalChatId`](../../common/src/commonMain/kotlin/chat/simplex/common/model/DirectorySearch.kt#L89-L109), building a [`DirectoryResult`](../../common/src/commonMain/kotlin/chat/simplex/common/model/DirectorySearch.kt#L172-L177). The link-type check makes this lookup store-only: the core plans contact-mode links under `PRMNever` from the store, but would fetch an invitation link from its server. The page is published, appended or replacing, only if the generation did not change.
|
||||
|
||||
### Name resolution
|
||||
|
||||
[`resolveNames(name, gen)`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L141-L157) resolves `[name]` for a candidate starting with `@` or `#`, else `["@$name", "#$name"]` (the candidate comes from [`nameSearchCandidate`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt#L1261-L1272), as on the typing path). Each target is planned with `apiConnectPlan(rh, target, resolveMode = PRMUnknown, inProgress = mutableStateOf(false), onFailure = ConnectPlanFailure.Log)`; it stops before a target once the generation changed, so a cancelled or replaced search sends no further names. [`NameSearchResult(target, result)`](../../common/src/commonMain/kotlin/chat/simplex/common/model/DirectorySearch.kt#L111-L170) (a private constructor behind a companion `operator fun invoke` that returns null for no row) maps the plan with one exhaustive `when`: a contact address or group link gives a row (profile from the link data or the known chat), the user's own contact address, an invitation link and an error give none. `simplexName` is the plan's `planSimplexName?.shortStr`, `localChatId` comes from `planLocalChatId`, `iconName` is an `ImageResource`, and for a group link `resolvedLink` is the link the core resolved (`connLink.connShortLink`). Failures are logged at debug level and never shown as alerts; the false `inProgress` also keeps `sendCmdWithRetry` from showing its network retry alert.
|
||||
|
||||
### De-duplication
|
||||
|
||||
[`rows(shownChatIds)`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L127-L139) returns an [`OnlineSearchRows`](../../common/src/commonMain/kotlin/chat/simplex/common/model/DirectorySearch.kt#L179-L185) at composition time, so pages added by "Show more" are filtered too:
|
||||
|
||||
- a name result is hidden when its `localChatId` is among the shown local chats;
|
||||
- a directory result is hidden when its `localChatId` is among the shown local chats or any name result's `localChatId`, or when its `entry.link` equals any name result's `resolvedLink` (all name results, shown or not);
|
||||
- `footer` passes through.
|
||||
|
||||
Only store-derived chat ids and the link the core resolved can hide a row, never a field the directory or a name's owner wrote. A mismatch in link encoding can only produce a duplicate row.
|
||||
|
||||
[GAP] A regular group the user joined through a different link, or through the directory's full link, is not found by the store lookup and still appears as a Directory row; tapping it goes through `planAndConnect`. See [GAP-08](../../product/gaps.md#gap-08-online-search-groups-joined-through-another-link-still-appear-as-directory-results).
|
||||
|
||||
### Rendering
|
||||
|
||||
| Where | Rows | Empty state |
|
||||
|-------|------|-------------|
|
||||
| [`ChatList`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt#L1045-L1217) | `rows(shownChatIds)` with the ids of the shown chats ([source](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt#L1088)); after the local chats and before `ChatListFeatureCards`, [`nameRows`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt#L509-L523) then [`directoryRows`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt#L525-L544) | `NoChatsView` overlay only when no chat is shown, the model is not `searching` and `rows.isEmpty` ([source](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt#L1195-L1200)); the `you_have_no_chats` overlay of `ChatListWithLoadingScreen` (no chats at all) is hidden while `showResults` |
|
||||
| Onboarding branch of [`ChatListWithLoadingScreen`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt#L459-L498) | A `Column` with `ChatListSearchBarWithTags` first (top bar) or last (one-hand UI), and between them a `Box(Modifier.weight(1f))` holding, while `showResults`, a `LazyColumnWithScrollBarNoAppBar` of the same parts with `shownChatIds = emptySet()` (reversed in one-hand mode, `imePadding()` in top-bar mode), otherwise `ConnectOnboardingView()` on Android | A centred "No chats found" item when not searching and `rows.isEmpty` |
|
||||
|
||||
The onboarding `Column` takes the toolbar and navigation-bar paddings and consumes the navigation-bar insets, so the results list's `imePadding()` does not count them twice. On desktop the onboarding pages are drawn in the centre pane, so the chat list pane shows only the search bar and the results. When a joined result adds a chat and the onboarding state ends, `ChatList` enters composition and its `LaunchedEffect(activeFilter.value)` ([source](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt#L1214-L1216)) clears the search text, which resets the model; the iOS app keeps the search there.
|
||||
|
||||
Each part renders only when it has rows: the Names part when `rows.names` is not empty, the Directory part when `rows.directory` is not empty or `rows.footer` is set. `nameRows` and `directoryRows` are `LazyListScope` builders; each part starts with a header `item` ([`SearchPartHeader`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt#L546-L554): `body2`, secondary colour, `semantics { heading() }`) rather than a `stickyHeader`, and its rows are `items` keyed by `id`. In the reversed one-hand list a header therefore renders below its rows.
|
||||
|
||||
| Row | View | Tap |
|
||||
|-----|------|-----|
|
||||
| Name | [`NameSearchRow`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L266-L294): avatar, profile name in bold, the SimpleX name in parentheses (secondary, one `AnnotatedString`, one line), description (2 lines) | `planAndConnect(rhId, result.target, close = null, cleanup = null)` |
|
||||
| Directory entry | [`DirectorySearchRow`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L219-L264): avatar, `#` + name, the directory's SimpleX name claim as plain text (truncates first), description (2 lines), member or subscriber count, and the verb "Join channel" / "Join group" (one line, never wrapped) | `planAndConnect(rhId, result.entry.link, close = null, cleanup = null)` |
|
||||
| Retry | [`DirectoryRetryRow`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L313-L325): "Search failed, tap to retry" | `withApi { retry() }` |
|
||||
| Show more | [`DirectoryShowMoreRow`](../../common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/DirectorySearchView.kt#L296-L311): "Show more", disabled (secondary colour) while `loading` | `withApi { loadMore() }` |
|
||||
|
||||
A name or directory row tap first hides the keyboard (`hideKeyboard(view)`), then runs `planAndConnect` with `withBGApi`. Text from the directory and from name owners is rendered with `Text(String)` (never parsed as markdown) with line limits.
|
||||
|
||||
[GAP] Resolving a SimpleX name with `PRMUnknown` also loads the resolved address's link data from the server that hosts it; the alert names only the directory and the name-resolving server. See [GAP-09](../../product/gaps.md#gap-09-online-search-first-search-alert-does-not-mention-the-server-hosting-a-resolved-address).
|
||||
@@ -15,6 +15,7 @@ Source: `common/src/commonMain/kotlin/chat/simplex/common/App.kt` (470 lines)
|
||||
7. [Authentication Gate](#7-authentication-gate)
|
||||
8. [Onboarding Flow](#8-onboarding-flow)
|
||||
9. [Source Files](#9-source-files)
|
||||
10. [New Chat Online Search](#10-new-chat-online-search)
|
||||
|
||||
---
|
||||
|
||||
@@ -376,3 +377,31 @@ The stage value is stored in `appPrefs.onboardingStage` and persisted across app
|
||||
| `views/SplashView.kt` | Splash / loading screen |
|
||||
| `views/call/CallView.kt` | In-call fullscreen view (ActiveCallView) |
|
||||
| `views/localauth/PasswordEntry.kt` | Column divider utility (contains VerticalDivider) |
|
||||
| `views/newchat/NewChatSheet.kt` | New chat sheet: action buttons, contacts list, online search sections; Archived contacts (`DeletedContactsView`) |
|
||||
|
||||
---
|
||||
|
||||
<a id="NewChatOnlineSearch"></a>
|
||||
|
||||
## 10. New Chat Online Search
|
||||
|
||||
**Location:** [`NewChatSheetLayout`](../../common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt#L110-L535)
|
||||
|
||||
`NewChatSheetLayout` offers the chat list's online search (see [Chat List, Online Search](chat-list.md#11-online-search)) with its own [`OnlineSearchModel`](../../common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt#L141) (`remember`), reset from a `KeyChangeEffect` on `searchText.value.text` and another on `chatModel.currentUser.value?.userId` ([source](../../common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt#L182-L187)). A profile switch also closes all modals on Kotlin, so the sheet is disposed anyway. The sheet's `DisposableEffect` `onDispose` ([`NewChatSheet`](../../common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt#L42-L48)) calls `cancelConnectProgress()`, which cancels a search still in flight. The model is also dropped while a modal opened from the sheet covers it (`ModalManager` composes only the top modal) and on an Android rotation.
|
||||
|
||||
| Element | Where | Behavior |
|
||||
|---------|-------|----------|
|
||||
| Search key | [`ContactsSearchBar`](../../common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt#L555-L657) with `onSearch` ([source](../../common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt#L563)), passed to `SearchTextField` | the keyboard's Search action (Android) or Enter (desktop) runs [`runOnlineSearch()`](../../common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt#L237-L242): `search(text, name = connectNameCandidate.value)` |
|
||||
| "Connect to …" row | the sticky header, below the field, in both `OneHandLazyColumn` ([source](../../common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt#L387-L391)) and `NonOneHandLazyColumn` ([source](../../common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt#L485-L489)) | shown for a name candidate until name results arrive (`candidate != null && onlineSearch.names.isEmpty()`) |
|
||||
| "Search in Directory" row | after the "Connect to …" row, behind a `Divider` ([source](../../common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt#L392-L395), [source](../../common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt#L490-L493)) | shown for non-empty, non-link text; runs [`runDirectorySearch()`](../../common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt#L231-L235): `searchDirectory(text)` |
|
||||
| Names and Directory sections | after `itemsIndexed(filteredContactChats)` in both columns, [`onlineSearchSections(rows)`](../../common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt#L195-L227) | each part starts with an [`OnlineSearchHeader`](../../common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt#L189-L193) item styled as "Your contacts" (`SectionDividerSpaced` then the `SectionView` title, not upper-cased); rows are `items` keyed by `id`; the Directory part ends with the retry or "Show more" row; rows tap `hideKeyboard(view)` then `withBGApi { planAndConnect(rhId, …, close = close, cleanup = null) }`, which closes the sheet once connected |
|
||||
|
||||
Both triggers do nothing for empty text or a pasted link ([`searchTrimmed()`](../../common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt#L229), `searchShowingSimplexLink`) and go through `confirmDirectorySearch`, launching the model with `withApi`. `onlineSearchSections`, `OnlineSearchHeader`, `searchTrimmed`, `runDirectorySearch` and `runOnlineSearch` are local functions of `NewChatSheetLayout`. In the reversed one-hand column a header item renders below its rows, as the contacts header does.
|
||||
|
||||
**De-duplication against the contacts list.** `filteredContactChats` (the contacts the list shows, computed in the layout's scope) gives the shown ids: `rows = onlineSearch.rows(shownChatIds = filteredContactChats.map { it.id }.toSet())` ([source](../../common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt#L180)), so no extra helper is needed.
|
||||
|
||||
**Empty state.** `showsEmptyState = !onlineSearch.searching && rows.isEmpty` ([source](../../common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt#L181)); [`NoFilteredContactsItem`](../../common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt#L324-L338) shows "No filtered contacts" only when it holds. Kotlin has no "No contacts" state.
|
||||
|
||||
**Archived contacts.** [`DeletedContactsView`](../../common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt#L750-L864) passes no `onSearch` to `ContactsSearchBar`: its field keeps the Search IME label (every `SearchTextField` uses `ImeAction.Search`), the key does nothing, and it shows no online rows; its empty state is unchanged.
|
||||
|
||||
[GAP] New chat does not resolve a typed name locally while typing (the `ContactsSearchBar` collector resolves a name only when its "Connect to …" row is tapped), so a known contact whose name does not contain the typed text is not in "Your contacts" and appears as a Names row; tapping it opens the existing chat. See [GAP-10](../../product/gaps.md#gap-10-online-search-new-chat-does-not-narrow-contacts-by-name-while-typing).
|
||||
@@ -41,6 +41,7 @@
|
||||
| PC29 | User Address |
|
||||
| PC30 | Member Support Chat |
|
||||
| PC31 | Channels (Relays) |
|
||||
| PC32 | Online Search (Directory and Names) |
|
||||
|
||||
---
|
||||
|
||||
@@ -52,13 +53,14 @@ Path prefix: `common/src/commonMain/kotlin/chat/simplex/common/`
|
||||
|
||||
| Source File | Product Concepts Affected | Risk Level | Notes |
|
||||
|-------------|--------------------------|------------|-------|
|
||||
| `App.kt` | PC1 through PC31 | High | Root composable — navigation scaffold for all features |
|
||||
| `App.kt` | PC1 through PC32 | High | Root composable — navigation scaffold for all features |
|
||||
| `AppLock.kt` | PC22 | Medium | App lock state and authorization lifecycle |
|
||||
| `model/ChatModel.kt` | PC1 through PC31 | High | Central state object — every feature reads or writes here |
|
||||
| `model/SimpleXAPI.kt` | PC1 through PC31 | High | FFI bridge to Haskell core — all commands and responses |
|
||||
| `model/ChatModel.kt` | PC1 through PC32 | High | Central state object — every feature reads or writes here |
|
||||
| `model/SimpleXAPI.kt` | PC1 through PC32 | High | FFI bridge to Haskell core — all commands and responses |
|
||||
| `model/DirectorySearch.kt` | PC32 | High | Directory response parsing and link-type validation of untrusted entries; online result types |
|
||||
| `model/CryptoFile.kt` | PC10, PC23 | Medium | Encrypted file read/write helpers |
|
||||
| `platform/Core.kt` | PC1 through PC31 | High | Native FFI declarations (`chatMigrateInit`, `chatSendCmd`, etc.) — all API traffic |
|
||||
| `platform/AppCommon.kt` | PC1 through PC31 | Medium | Shared app initialization logic |
|
||||
| `platform/Core.kt` | PC1 through PC32 | High | Native FFI declarations (`chatMigrateInit`, `chatSendCmd`, etc.) — all API traffic |
|
||||
| `platform/AppCommon.kt` | PC1 through PC32 | Medium | Shared app initialization logic |
|
||||
| `platform/Files.kt` | PC10, PC23, PC26 | Medium | File path resolution, temp dirs, encryption utilities |
|
||||
| `platform/NtfManager.kt` | PC18 | High | Notification manager expect declarations |
|
||||
| `platform/Notifications.kt` | PC18 | Medium | Notification channel and permission abstractions |
|
||||
@@ -68,7 +70,7 @@ Path prefix: `common/src/commonMain/kotlin/chat/simplex/common/`
|
||||
| `platform/Cryptor.kt` | PC23 | Medium | Keystore encryption expect declarations |
|
||||
| `platform/Share.kt` | PC10, PC12 | Low | Share sheet abstractions |
|
||||
| `platform/Images.kt` | PC10, PC19 | Low | Image processing utilities |
|
||||
| `platform/Platform.kt` | PC1 through PC31 | Low | Platform detection and capability flags |
|
||||
| `platform/Platform.kt` | PC1 through PC32 | Low | Platform detection and capability flags |
|
||||
| `platform/PlatformTextField.kt` | PC4 | Low | Native text input expect declarations |
|
||||
| `platform/Back.kt` | PC1 | Low | Back navigation handling |
|
||||
| `platform/UI.kt` | PC24 | Low | UI density and locale helpers |
|
||||
@@ -91,8 +93,9 @@ Path prefix: `common/src/commonMain/kotlin/chat/simplex/common/`
|
||||
|
||||
| Source File | Product Concepts Affected | Risk Level | Notes |
|
||||
|-------------|--------------------------|------------|-------|
|
||||
| `views/chatlist/ChatListView.kt` | PC1, PC28 | High | Main screen — chat list rendering and search |
|
||||
| `views/chatlist/ChatListView.kt` | PC1, PC28, PC32 | High | Main screen — chat list rendering and search; online search triggers, result parts and the onboarding search |
|
||||
| `views/chatlist/GetStakeBanner.kt` | PC1 | Low | Crowdfunding banner and shared banner card chrome |
|
||||
| `views/chatlist/DirectorySearchView.kt` | PC1, PC32 | Medium | Online search model (generation, spinner, de-duplication), result rows, consent alert |
|
||||
| `views/chatlist/ChatListNavLinkView.kt` | PC1, PC2, PC3 | Medium | Navigation from chat list item to chat |
|
||||
| `views/chatlist/ChatPreviewView.kt` | PC1, PC2, PC3, PC11 | Medium | Chat row preview rendering |
|
||||
| `views/chatlist/TagListView.kt` | PC28 | Medium | Chat tag filter UI |
|
||||
@@ -190,7 +193,7 @@ Path prefix: `common/src/commonMain/kotlin/chat/simplex/common/`
|
||||
| Source File | Product Concepts Affected | Risk Level | Notes |
|
||||
|-------------|--------------------------|------------|-------|
|
||||
| `views/newchat/NewChatView.kt` | PC12, PC29 | High | New connection creation — onramp for all contacts |
|
||||
| `views/newchat/NewChatSheet.kt` | PC12 | Medium | Bottom sheet with connection options |
|
||||
| `views/newchat/NewChatSheet.kt` | PC12, PC32 | Medium | Bottom sheet with connection options, contacts list and online search sections; search bar shared with archived contacts |
|
||||
| `views/newchat/ConnectPlan.kt` | PC12, PC15 | Medium | Link parsing and connection plan resolution |
|
||||
| `views/newchat/AddGroupView.kt` | PC3, PC14 | Medium | New group creation flow |
|
||||
| `views/newchat/AddChannelView.kt` | PC31 | Medium | Public channel creation, channel link card, `RelayStatusIndicator` |
|
||||
@@ -270,9 +273,9 @@ Path prefix: `common/src/commonMain/kotlin/chat/simplex/common/`
|
||||
|
||||
| Source File | Product Concepts Affected | Risk Level | Notes |
|
||||
|-------------|--------------------------|------------|-------|
|
||||
| `views/helpers/AlertManager.kt` | PC1 through PC31 | Medium | Modal alert system used across all features |
|
||||
| `views/helpers/ModalView.kt` | PC1 through PC31 | Medium | Modal navigation stack |
|
||||
| `views/helpers/Utils.kt` | PC1 through PC31 | Low | Shared formatting, clipboard, and utility functions |
|
||||
| `views/helpers/AlertManager.kt` | PC1 through PC32 | Medium | Modal alert system used across all features |
|
||||
| `views/helpers/ModalView.kt` | PC1 through PC32 | Medium | Modal navigation stack |
|
||||
| `views/helpers/Utils.kt` | PC1 through PC32 | Low | Shared formatting, clipboard, and utility functions |
|
||||
| `views/helpers/DatabaseUtils.kt` | PC23 | Medium | Keystore passphrase and database helpers |
|
||||
| `views/helpers/LinkPreviews.kt` | PC11 | Medium | Link preview fetching and rendering |
|
||||
| `views/helpers/LocalAuthentication.kt` | PC22 | Medium | Biometric/passcode authentication expect |
|
||||
@@ -282,7 +285,7 @@ Path prefix: `common/src/commonMain/kotlin/chat/simplex/common/`
|
||||
| `views/helpers/ChooseAttachmentView.kt` | PC10 | Low | Attachment picker |
|
||||
| `views/helpers/GetImageView.kt` | PC10, PC19 | Low | Image capture and crop |
|
||||
| `views/helpers/TextEditor.kt` | PC4 | Low | Rich text editor helpers |
|
||||
| `views/helpers/SearchTextField.kt` | PC1 | Low | Search bar composable |
|
||||
| `views/helpers/SearchTextField.kt` | PC1, PC32 | Low | Search bar composable; optional Search key handler (`onSearch`: Android IME action, desktop Enter) |
|
||||
| `views/helpers/CustomTimePicker.kt` | PC8 | Low | Time picker for timed messages |
|
||||
| `views/helpers/DragAndDrop.kt` | PC10 | Low | Drag-and-drop file handling |
|
||||
| `views/helpers/ProcessedErrors.kt` | — | Low | Error aggregation |
|
||||
@@ -325,8 +328,8 @@ Path prefix: `android/src/main/java/chat/simplex/app/`
|
||||
|
||||
| Source File | Product Concepts Affected | Risk Level | Notes |
|
||||
|-------------|--------------------------|------------|-------|
|
||||
| `SimplexApp.kt` | PC1 through PC31 | High | Application class — initializes core, preferences, and notification channels |
|
||||
| `MainActivity.kt` | PC1 through PC31 | High | Single-activity host — intent handling, lifecycle, deep links |
|
||||
| `SimplexApp.kt` | PC1 through PC32 | High | Application class — initializes core, preferences, and notification channels |
|
||||
| `MainActivity.kt` | PC1 through PC32 | High | Single-activity host — intent handling, lifecycle, deep links |
|
||||
| `SimplexService.kt` | PC18 | High | Foreground service — keeps message receiver alive |
|
||||
| `CallService.kt` | PC17 | Medium | Foreground service for active calls |
|
||||
| `MessagesFetcherWorker.kt` | PC18 | Medium | WorkManager periodic message fetch |
|
||||
@@ -340,7 +343,7 @@ Path prefix: `common/src/androidMain/kotlin/chat/simplex/common/`
|
||||
|
||||
| Source File | Product Concepts Affected | Risk Level | Notes |
|
||||
|-------------|--------------------------|------------|-------|
|
||||
| `platform/AppCommon.android.kt` | PC1 through PC31 | Medium | Android app initialization actual declarations |
|
||||
| `platform/AppCommon.android.kt` | PC1 through PC32 | Medium | Android app initialization actual declarations |
|
||||
| `platform/SimplexService.android.kt` | PC18 | Medium | Android foreground service actual implementation |
|
||||
| `platform/Files.android.kt` | PC10, PC23, PC26 | Medium | Android file paths and content-URI resolution |
|
||||
| `platform/Cryptor.android.kt` | PC23 | Medium | Android Keystore encryption actual implementation |
|
||||
@@ -406,7 +409,7 @@ Path prefix: `desktop/src/jvmMain/kotlin/chat/simplex/desktop/`
|
||||
|
||||
| Source File | Product Concepts Affected | Risk Level | Notes |
|
||||
|-------------|--------------------------|------------|-------|
|
||||
| `Main.kt` | PC1 through PC31 | High | JVM entry point — Haskell init, migrations, app launch |
|
||||
| `Main.kt` | PC1 through PC32 | High | JVM entry point — Haskell init, migrations, app launch |
|
||||
|
||||
### 3.2 Desktop Platform Implementations (desktopMain)
|
||||
|
||||
@@ -417,7 +420,7 @@ Path prefix: `common/src/desktopMain/kotlin/chat/simplex/common/`
|
||||
| `DesktopApp.kt` | PC1, PC2, PC3 | High | Desktop Compose window — window lifecycle, crash recovery |
|
||||
| `StoreWindowState.kt` | — | Low | Window position/size persistence |
|
||||
| `model/NtfManager.desktop.kt` | PC18 | Medium | Desktop system tray notification display |
|
||||
| `platform/AppCommon.desktop.kt` | PC1 through PC31 | Medium | Desktop app initialization actual declarations |
|
||||
| `platform/AppCommon.desktop.kt` | PC1 through PC32 | Medium | Desktop app initialization actual declarations |
|
||||
| `platform/SimplexService.desktop.kt` | PC18 | Low | Desktop background receiver (no foreground service) |
|
||||
| `platform/Files.desktop.kt` | PC10, PC23, PC26 | Medium | Desktop file path resolution |
|
||||
| `platform/Cryptor.desktop.kt` | PC23 | Medium | Desktop keystore encryption actual implementation |
|
||||
@@ -480,13 +483,13 @@ The Haskell core is compiled as a shared native library (`libsimplex.so` / `libs
|
||||
|
||||
| Source File | Product Concepts Affected | Risk Level | Notes |
|
||||
|-------------|--------------------------|------------|-------|
|
||||
| `src/Simplex/Chat.hs` | PC1 through PC31 | High | Main chat module — top-level orchestration |
|
||||
| `src/Simplex/Chat/Controller.hs` | PC1 through PC31 | High | Command processor — all API commands dispatched here |
|
||||
| `src/Simplex/Chat/Types.hs` | PC1 through PC31 | High | Core data types shared across all features |
|
||||
| `src/Simplex/Chat/Core.hs` | PC1 through PC31 | High | Chat engine lifecycle (start, stop, subscribe) |
|
||||
| `src/Simplex/Chat/Library/Commands.hs` | PC1 through PC31 | High | API command handler implementations |
|
||||
| `src/Simplex/Chat/Library/Internal.hs` | PC1 through PC31 | High | Internal helpers for command processing |
|
||||
| `src/Simplex/Chat/Library/Subscriber.hs` | PC1 through PC31 | High | Event subscriber — incoming message routing |
|
||||
| `src/Simplex/Chat.hs` | PC1 through PC32 | High | Main chat module — top-level orchestration |
|
||||
| `src/Simplex/Chat/Controller.hs` | PC1 through PC32 | High | Command processor — all API commands dispatched here |
|
||||
| `src/Simplex/Chat/Types.hs` | PC1 through PC32 | High | Core data types shared across all features |
|
||||
| `src/Simplex/Chat/Core.hs` | PC1 through PC32 | High | Chat engine lifecycle (start, stop, subscribe) |
|
||||
| `src/Simplex/Chat/Library/Commands.hs` | PC1 through PC32 | High | API command handler implementations |
|
||||
| `src/Simplex/Chat/Library/Internal.hs` | PC1 through PC32 | High | Internal helpers for command processing |
|
||||
| `src/Simplex/Chat/Library/Subscriber.hs` | PC1 through PC32 | High | Event subscriber — incoming message routing |
|
||||
| `src/Simplex/Chat/Protocol.hs` | PC2, PC3, PC4, PC5, PC6, PC7 | High | Chat-level message protocol (x-events) |
|
||||
| `src/Simplex/Chat/Messages.hs` | PC2, PC3, PC4, PC5, PC6, PC7, PC8, PC9 | High | Message types and content |
|
||||
| `src/Simplex/Chat/Messages/CIContent.hs` | PC4, PC5, PC6, PC7, PC8, PC9, PC11 | Medium | Chat item content variants |
|
||||
@@ -496,8 +499,8 @@ The Haskell core is compiled as a shared native library (`libsimplex.so` / `libs
|
||||
| `src/Simplex/Chat/Files.hs` | PC10 | Medium | File transfer orchestration |
|
||||
| `src/Simplex/Chat/Delivery.hs` | PC2, PC3 | Medium | Message delivery engine |
|
||||
| `src/Simplex/Chat/Markdown.hs` | PC4 | Low | Markdown parsing for message formatting |
|
||||
| `src/Simplex/Chat/Store.hs` | PC1 through PC31 | High | Database store interface |
|
||||
| `src/Simplex/Chat/Store/Shared.hs` | PC1 through PC31 | Medium | Shared store utilities |
|
||||
| `src/Simplex/Chat/Store.hs` | PC1 through PC32 | High | Database store interface |
|
||||
| `src/Simplex/Chat/Store/Shared.hs` | PC1 through PC32 | Medium | Shared store utilities |
|
||||
| `src/Simplex/Chat/Store/Messages.hs` | PC4, PC5, PC6, PC7, PC8 | High | Message persistence |
|
||||
| `src/Simplex/Chat/Store/Groups.hs` | PC3, PC14, PC15, PC16, PC30 | High | Group persistence |
|
||||
| `src/Simplex/Chat/Store/Direct.hs` | PC2, PC12, PC13 | High | Contact persistence |
|
||||
@@ -526,11 +529,11 @@ The Haskell core is compiled as a shared native library (`libsimplex.so` / `libs
|
||||
| `src/Simplex/Chat/Operators/Presets.hs` | PC25 | Low | Preset server operators |
|
||||
| `src/Simplex/Chat/Operators/Conditions.hs` | PC25 | Low | Operator usage conditions |
|
||||
| `src/Simplex/Chat/AppSettings.hs` | PC25 | Low | App settings sync types |
|
||||
| `src/Simplex/Chat/Mobile.hs` | PC1 through PC31 | High | C FFI exports — JNI bridge target |
|
||||
| `src/Simplex/Chat/Mobile.hs` | PC1 through PC32 | High | C FFI exports — JNI bridge target |
|
||||
| `src/Simplex/Chat/Mobile/File.hs` | PC10 | Medium | Mobile file read/write FFI |
|
||||
| `src/Simplex/Chat/Mobile/Shared.hs` | PC1 through PC31 | Medium | Shared FFI helpers |
|
||||
| `src/Simplex/Chat/Mobile/Shared.hs` | PC1 through PC32 | Medium | Shared FFI helpers |
|
||||
| `src/Simplex/Chat/Mobile/WebRTC.hs` | PC17 | Low | WebRTC FFI helpers |
|
||||
| `src/Simplex/Chat/View.hs` | PC1 through PC31 | Low | Terminal view rendering (not used by mobile/desktop UI) |
|
||||
| `src/Simplex/Chat/View.hs` | PC1 through PC32 | Low | Terminal view rendering (not used by mobile/desktop UI) |
|
||||
| `src/Simplex/Chat/Stats.hs` | PC25 | Low | Server statistics tracking |
|
||||
| `src/Simplex/Chat/Util.hs` | — | Low | General Haskell utilities |
|
||||
| `src/Simplex/Chat/Styled.hs` | — | Low | Terminal styled text (not used by mobile/desktop UI) |
|
||||
|
||||
@@ -451,6 +451,14 @@ val appPrefs: AppPreferences by lazy { AppPreferences() }
|
||||
| `oneHandUI` | `Boolean` | `true` | One-hand mode |
|
||||
| `chatBottomBar` | `Boolean` | `true` | Bottom bar in chat |
|
||||
|
||||
#### Hints
|
||||
|
||||
Preferences listed in [`hintPreferences`](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L279-L297) are restored to their defaults by "Reset all hints" (`resetHintPreferences()` in `views/usersettings/SettingsView.kt`).
|
||||
|
||||
| Key | Type | Default | Purpose |
|
||||
|---|---|---|---|
|
||||
| [`directorySearchAlertShown`](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L196) (`"DirectorySearchAlertShown"`) | `Boolean` | `false` | The user chose "Search and don't show again" in the online search alert; in `hintPreferences` (see [Chat List, Online Search](client/chat-list.md#11-online-search)) |
|
||||
|
||||
#### Remote Access (lines 238-243)
|
||||
|
||||
| Key | Type | Default | Purpose |
|
||||
|
||||
Reference in new issue
Block a user