* ci: reduced build on pushes to master and stable in forks
* ci: test retry message only when retrying
* ci: pull request changes against the current base
* ci: deploy the website only from simplex-chat/simplex-chat
* ci: reduced build of changed scopes on drafts and manual runs
* ci: tests only when selected in reduced builds
* ci: test selection and retries in scripts/ci/run-tests.sh
* plan: UI improvements to improve LLM usage via SimpleX
* core, ui: types for chat layout
* core, ui: settings for alternative terminal-like chat layout for managing LLMs or other service bots
* plan: message priority
* fixes
* query plan
* update plans
* remove silent messages plan
* rename migrations
* implement layout
* amend
* fix
* fix
* fix
* improve
* time on the right, sender name in blue
* no avatar in direct chats
* better previews
* update, fixes
* update
* time gap
* changes
* ui: terminal layout line heights, gaps and time placement
* ui: simplify terminal meta layout, correct its docs and tests
* ui: right-to-left emoji edge in terminal rows, time placement tests
* android, desktop: raise Kotlin compiler memory to 4 GB
* ui: terminal captions as follow-up text, classic event separators
* ui: terminal media edge and overlay time, plan decisions
* ui: simplify terminal row separation, correct doc line references
* ui: keep the media time inside terminal rows, restore caption conditions
* ui: terminal heading capitals at large font scales, idle live message time
* ui: rename the classic voice-with-text flag, correct the meta copy docs
* ui: smaller terminal layout diff, sender gap and meta names
* ui: terminal row as tall as the media time
* docs: remove the chat layout section from the bots readme
* docs: shorten the terminal layout section of the chat view
* core: address settings API as on master, /auto_accept reads the stored layout
* ui: smaller terminal headings
* ui: classic sender rows and avatar rule as on master
* ui: classic reply quote text colour as on master
* ios: keep the chat layout setting with other UI settings
* ui: go-to button at the end of terminal rows, no gutter in direct chats
* ui: forwarded arrow on the header line of terminal rows, above the time
* ui: forwarded arrow centred above the time line of terminal rows
---------
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
* tests: fix group tests
* fix test
* fix
* fix delivery worker fix
* watch ci
* debug
* fix delivery worker not forwarding to moderators during host acceptance
* add script, update simplexmq
* debug lines
* more debug
* rerun
* debug
* fix, debug
* simplexmq
* simplexmq
* fix
* debug: log member-not-found call sites, restore hang diagnostics, use simplexmq fork
* tests: do not prompt for migration confirmation in strict tables test
* ci: run tests with one capability; simplexmq: end inactive sessions without closing under readers
* ci: run tests on all capabilities
* remote: close the HTTP2 client before its TLS connection
* cabal: use warp that kills timed out connection threads without closing their sockets
* remove diagnostics: member-not-found log, thread dump, CI watchdog; update simplexmq
* badge-service: prevent interleaving of concurrent log lines
* tests: wait for badge group broadcasts before closing members
* tests: log contact connected events received twice
* tests: log member connected events received twice
* core: use simplexmq that logs connections emitting CON twice
* core: keep the team contact above notes in a new user's chat list
* ci: skip macOS and Windows builds while hunting a Linux-only flake
* core: use simplexmq that logs the history of connections emitting CON twice
* tests: remove the logging of connected events received twice
* ci: run macOS and Windows builds again
* core: use simplexmq that runs one thread per agent worker
* core: use the merged simplex-chat/wai warp and the updated simplexmq
* core: use the merged simplexmq
* core: update simplexmq
* core, badge service: comment single writes of terminal segments and log lines
* desktop: stop the gsettings theme monitor when the app exits
On Linux the theme detector library starts `gsettings monitor` to
follow the system theme and never stops it. The process outlived the
app, one per run, each holding a D-Bus connection until the next
theme change. A shutdown hook now stops it.
* plans: gsettings theme monitor left running after the app exits
* desktop: install the gsettings shutdown hook on Linux only
* plans: correct the Ctrl-C note for the gsettings monitor
* plans: gsettings ignores SIGPIPE; the monitor lives until killed or logout
* desktop: open a pending connection's "Learn more" and "Incognito" pages once
On desktop the connection view stays clickable in the center pane, so each
click on these rows pushed another modal onto the right pane. Close the
right-pane modals first, as ChatView does when the connection opens. The page
is closed and shown again in one event, so its content is keyed by its
ModalData to start fresh.
Desktop only: on Android the view can itself be the open modal of the shared
manager ("Set contact name"), and the new page already covers the row.
Fixes#6270
* ui: open a modal once when its button is double-clicked
A modal slides in over 250 ms, and until it covers the button that opened it the button still takes clicks, so a double click opened two copies (left-pane pages and most stacked center and right-pane pages on desktop, animated modals on Android). When a modal opens, the page it covers draws an empty pointerInput box over itself while it slides out; when a pane's first modal slides in, the empty page sliding out covers the view under the pane. The page sliding in already takes clicks, and nothing is blocked when a page closes.
* ui: add isTopModalOpen, which ignores modals that are sliding out
A modal closed with animation stays in modalViews until it has slid out, and isLastModalOpen counts it. A check-then-closeModal() on it would close the modal under it. Uses the same index as isLastModal; isLastModalOpen and its callers are unchanged.
* desktop: don't open a second badges page or profile form
These buttons stay clickable on desktop while their page is shown in another pane, so each click opened another copy:
- "Support SimpleX" in the chat's large-file and badge alerts (Badges on the right): the alerts skip while Badges is open, on desktop only, as on Android the page covers the button;
- "+ Add profile" in Your chat profiles and "Create chat profile" in the picker (form in the center): skip while the form is on top, keeping what was typed, before asking for authentication; the picker row still hides the picker.
* desktop: replace a page of the same kind on top instead of stacking another
These buttons stay clickable on desktop while their page is shown in another pane, so each click pushed another copy:
- the chat banner's bio "Read more" (right pane);
- a markdown file clicked in a support chat (viewer in the center);
- message "Info" in the main chat while a support or reports chat is open on the right.
Their content depends on what was clicked and the pane can hold other pages (a support chat), so a page of the same kind on top is replaced. The new page is drawn where the old one was, so its content is keyed by its ModalData to start fresh. The markdown file and Info load on a background thread; their close and show now run on the main thread. The markdown check is desktop only, as on Android the viewer covers its file once it has loaded.
* plans: a page opens again on every click of its button (#6270)
* ui: create one group on a double click of Create group
"Create group" creates the group, opens it and only then closes its
page, and the core does not deduplicate, so a double click created two
groups. The page now ignores a click while its create is in progress,
on desktop and Android.
* plans: a double click on "create group" creates two groups
* plans: D2 is valid at 80 ms only
* ui: ignore a click on Create group until its page has closed after a create
The summary now covers the whole lifecycle: buying, renewing, changing
tier, moving to another device or profile, and ending. The badge retires
as soon as the store says the subscription ended, a lapsed iOS subscriber
can resume the retired badge, an App Store raise is granted at once and
a lower tier at the renewal, and a Play plan change is a new purchase.
The known limits and the before-sale list follow the code.
The seven diagrams are updated, with stale file and line references
dropped, and two are added: a tier change and a restore. The page now
points at the diagram files instead of carrying copies of them.
The RPC document splits a store tier change by store and direction: the
app never starts one, an App Store raise seen within a day of its charge
is sent as upgradeBadgeSubscription, every other change waits for the
renewal check, and a Play plan change is a new token, a new purchase.
transferBadge and its limits are checked against the handlers, and the
upgrades that are not built are marked so.
The overview admits what Restore discloses: the service links the two
purchase keys, a store receipt moves its subscription to whoever presents
it within the move limit, and a new badge or tier is shown as soon as it
is issued. The Android gap names what remains of Restore.
A Supporter charge handed over while Legend is shown is answered from the record and sends nothing, and the renewal check grants Supporter, shown once the Legend credential expires; a record an older build held for a lower tier settles with no request. Both fail if every change of tier is sent at once again.
Run: 016-tier-change-direction
Apple keeps charging the old plan to the end of the period, so applying a downgrade at once took away a badge the subscriber had already paid for. A charge of another tier is now held and sent at once only from supporter to legend; a lower tier, or one that is not ranked (investor, unknown), is answered from the record and the renewal check grants it, as badges-rpc.md says. A record already held for such a change settles silently.
Run: 016-tier-change-direction
- R3-1: a tier change whose answer says the subscription ended is not credited, as a resume is not, so the credit's clearing of a stored ended never drops the one this answer carries.
- R3-2: the changeTier comment in two lines.
Run: 015-badge-fixes, review round 3
* android, desktop: fix chat preview and unread counter updating wrong chat
While "Chat with admins", a member support chat or the reports view was
open, ChatsContext.addChatItem in the secondary context looked up the
chat's index in its own list but read and wrote the main chat list at
that index (chatsContext.chats[i]), so an unrelated chat in the main
list got the message as its preview and its unread count went up.
Use the context's own list at all four sites again. The main-list
qualifier had also made a pending invitee's sent support messages reach
the group's preview in the main list (at a secondary-list index), so
send that update to the main list explicitly: addSentChatItem passes a
sent item to the main chat list context as well when it is sent from a
secondary view and belongs in the main list (inMainChatList, the same
rule addChatItem uses for previews). Count unread stats only in the
main chat list context, so the revert does not make the open secondary
view count main-list messages in its own list.
* plans: wrong-chat preview and unread fix
* desktop: open a pending connection's "Learn more" and "Incognito" pages once
On desktop the connection view stays clickable in the center pane, so each
click on these rows pushed another modal onto the right pane. Close the
right-pane modals first, as ChatView does when the connection opens. The page
is closed and shown again in one event, so its content is keyed by its
ModalData to start fresh.
Desktop only: on Android the view can itself be the open modal of the shared
manager ("Set contact name"), and the new page already covers the row.
Fixes#6270
* ui: open a modal once when its button is double-clicked
A modal slides in over 250 ms, and until it covers the button that opened it the button still takes clicks, so a double click opened two copies (left-pane pages and most stacked center and right-pane pages on desktop, animated modals on Android). When a modal opens, the page it covers draws an empty pointerInput box over itself while it slides out; when a pane's first modal slides in, the empty page sliding out covers the view under the pane. The page sliding in already takes clicks, and nothing is blocked when a page closes.
* ui: add isTopModalOpen, which ignores modals that are sliding out
A modal closed with animation stays in modalViews until it has slid out, and isLastModalOpen counts it. A check-then-closeModal() on it would close the modal under it. Uses the same index as isLastModal; isLastModalOpen and its callers are unchanged.
* desktop: don't open a second badges page or profile form
These buttons stay clickable on desktop while their page is shown in another pane, so each click opened another copy:
- "Support SimpleX" in the chat's large-file and badge alerts (Badges on the right): the alerts skip while Badges is open, on desktop only, as on Android the page covers the button;
- "+ Add profile" in Your chat profiles and "Create chat profile" in the picker (form in the center): skip while the form is on top, keeping what was typed, before asking for authentication; the picker row still hides the picker.
* desktop: replace a page of the same kind on top instead of stacking another
These buttons stay clickable on desktop while their page is shown in another pane, so each click pushed another copy:
- the chat banner's bio "Read more" (right pane);
- a markdown file clicked in a support chat (viewer in the center);
- message "Info" in the main chat while a support or reports chat is open on the right.
Their content depends on what was clicked and the pane can hold other pages (a support chat), so a page of the same kind on top is replaced. The new page is drawn where the old one was, so its content is keyed by its ModalData to start fresh. The markdown file and Info load on a background thread; their close and show now run on the main thread. The markdown check is desktop only, as on Android the viewer covers its file once it has loaded.
* plans: a page opens again on every click of its button (#6270)
* plans: D2 is valid at 80 ms only
- R2-1: a granted tier change clears an ended state the device reported before it. A renewing report never replaces ended, and a charge of the new tier granted proves the subscription has not ended, so without this the worker retired the new tier's badge at its paid-through while the subscription renewed.
- R1-B3: test a tier change whose credential does not verify: the failure stays on the receipt record, not the purchase, and Supporter stays shown.
- R2-3: the refused tier change's test buys Supporter through an invoice, so it shows the refusal announces nothing.
- R2-2: the changeTier comment says what follows each way the record settles.
Run: 015-badge-fixes, review round 2
The App Store tier-change hold, reviewed for the first time:
- R1-B2: a tier change's credit now shows its purchase again (shown_badge_id) in the same transaction, as a resume's does, so a retire that ran while the change was with the service cannot leave a paid purchase unshown and never renewed.
- R1-B5: a tier change of a shown purchase is held whatever other receipt is held, as the worker serves it while the badge is shown; only a resume waits for other held receipts.
- R1-B3: test a tier change the store does not vouch for: refused in silence, the badge kept, and answered from the record after.
- R1-B8: the changeTier comment names when the record settles.
Run: 015-badge-fixes, review round 1