Files
dms-chat/forms/MatrixConnectForm.qml
SalastilandClaude Sonnet 5 0ad9b7b719 Add Matrix backend with full end-to-end encryption
- New chatd-rs backend/matrix module: m.login.password auth, /sync-based
  multi-room connect loop, and full E2EE via matrix-sdk-crypto (device key
  upload, Megolm session establishment, encrypt/decrypt) - verified live
  against real homeservers with two independent devices exchanging an
  encrypted message neither one decrypted itself.
- Reactions, edits, redactions, replies, and media upload/download, mapped
  onto the existing Reaction/Embed/ReplyPreview model.
- Frontend wiring: MatrixConnectForm.qml, AccountsPanel/ChatView hookup,
  and the real addMatrixAccount RPC handler (replacing the stub).
- Fix messages doubling in the UI: the Matrix backend never called
  Runtime::set_own_identity, so every message - including the account's
  own - was stored with is_own=false, defeating the frontend's optimistic-
  send reconciliation.
- Fix accounts getting stuck showing "connecting" forever: the Connected
  state transition was gated on this being the account's very first-ever
  sync (next_batch.is_none()), which is never true again once a cursor is
  persisted across a restart - a real account could sync successfully
  forever and never have its state announced as connected.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-12 22:57:54 -04:00

72 lines
2.1 KiB
QML

import QtQuick
import QtQuick.Layouts
import qs.Common
import qs.Widgets
// Matrix login is homeserver URL + username + password (m.login.password) -
// no SSO/QR flow in v1, see backend/matrix/auth.rs. Login itself (resolving
// the homeserver, authenticating, then bootstrapping this device's E2EE
// identity) can take a moment, so this only ever renders a status string
// fed by matrixLoginStatus/matrixLoginResult events - see ChatView.qml's
// handling of those, mirrored from the Sneedchat/Discord equivalents.
ColumnLayout {
id: root
signal connectRequested(string homeserverUrl, string username, string password)
property string statusText: ""
property bool pending: false
onStatusTextChanged: root.pending = false
spacing: Theme.spacingXS
DankTextField {
id: homeserverField
Layout.fillWidth: true
placeholderText: "homeserver (e.g. https://matrix.org)"
text: "https://matrix.org"
}
RowLayout {
Layout.fillWidth: true
spacing: Theme.spacingXS
DankTextField {
id: usernameField
Layout.fillWidth: true
placeholderText: "username"
}
DankTextField {
id: passwordField
Layout.preferredWidth: 160
placeholderText: "password"
echoMode: TextInput.Password
}
}
DankButton {
text: root.pending ? "Connecting..." : "Connect"
enabled: !root.pending
onClicked: {
if (!homeserverField.text || !usernameField.text || !passwordField.text) {
root.statusText = "Homeserver, username and password are required.";
return;
}
root.pending = true;
root.statusText = "";
root.connectRequested(homeserverField.text, usernameField.text, passwordField.text);
passwordField.text = "";
}
}
StyledText {
Layout.fillWidth: true
visible: root.statusText !== ""
text: root.statusText
wrapMode: Text.WordWrap
color: Theme.surfaceVariantText
}
}