(fix): broken connections
Some checks failed
/ build-web (push) Failing after 6m34s
/ build-desktop (linux) (push) Failing after 6m44s
/ build-mobile (push) Failing after 8m36s
/ release (push) Has been skipped

This commit is contained in:
Alois 2026-07-27 21:37:56 +02:00
commit 0dff15cc28
Signed by: alois
SSH key fingerprint: SHA256:GBzT2DXvAuGV9XIV5W3WrzVpjU54FThmxHXdbz95J24
5 changed files with 79 additions and 47 deletions

@ -1 +1 @@
Subproject commit 6d97b5b9935b15d957444d38609378c66a24262a
Subproject commit 594646ac39d986f0787aa614a99d580035a67318

View file

@ -144,7 +144,7 @@ export function Provider(props: {
children: ReactNode;
blockConnection?: boolean;
}) {
const { load } = useStorage();
const { load, save } = useStorage();
const [readyState, setReadyState] = useState<number>(
ConnectionState.Disconnected,
@ -300,13 +300,19 @@ export function Provider(props: {
await MTPClient.init();
const [userId, keyring] = await Promise.all([
const [userId, keyring, storedSessionId] = await Promise.all([
load("user_id"),
load("mtp_keyring"),
load("session_id"),
]);
if (!userId || !keyring) {
throw new Error("Missing login credentials");
}
let sessionId = storedSessionId;
if (!Number.isSafeInteger(sessionId) || sessionId <= 0) {
sessionId = Date.now();
await save("session_id", sessionId);
}
const forcedOmikronUrl = await load("forced_omikron_url");
const forcedOmikronPublicKey = await load("forced_omikron_public_key");
@ -404,27 +410,6 @@ export function Provider(props: {
clientRef.current = client;
setReadyState(client.state);
const authPayload = new Promise<
ProtocolMessage<"IdentificationResponse">
>((resolve, reject) => {
const unsubscribe = client.subscribe(
"IdentificationResponse",
(message) => {
try {
unsubscribe();
if (message.type.startsWith("Error")) {
reject(new Error(`Authentication failed: ${message.type}`));
return;
}
resolve(validateResponse("IdentificationResponse", message));
} catch (authPayloadError) {
unsubscribe();
reject(authPayloadError);
}
},
);
});
clearReconnectTimer();
// Schedule reconnect reset
@ -438,7 +423,31 @@ export function Provider(props: {
setIdentifying(true);
await client.auth();
const finalResponse = await authPayload;
const finalResponse = validateResponse(
"ClientStateSync",
await client.request("ClientConnected", {
SessionId: sessionId,
VersionNumber: 0,
CacheValid: false,
CacheSchemaVersion: 0,
}),
);
if (finalResponse.type.startsWith("Error")) {
throw new Error(
`State synchronization failed: ${finalResponse.type}`,
);
}
const acknowledgement = await client.request("ClientStateAck", {
SessionId: sessionId,
VersionNumber: finalResponse.data.VersionNumber,
});
if (acknowledgement.type.startsWith("Error")) {
throw new Error(
`State acknowledgement failed: ${acknowledgement.type}`,
);
}
if (disposed || clientRef.current !== client) return;
@ -451,11 +460,15 @@ export function Provider(props: {
} catch (connectError) {
if (disposed) return;
cleanup();
const connectErrorMessage =
connectError instanceof Error
? connectError.message
: String(connectError ?? "Unknown error");
log(
0,
"mtp",
"red",
"Connection/authentication attempt failed",
`Connection/authentication attempt failed: ${connectErrorMessage}`,
getProtocolErrorDetails(connectError) ?? connectError,
);
@ -465,10 +478,7 @@ export function Provider(props: {
log(0, "mtp", "red", "Reconnection attempts exhausted", connectError);
sonnerToast.error("Connection failed", {
id: "mtp-connection-toast",
description:
connectError instanceof Error
? connectError.message.split(":")[0]
: String(connectError ?? "Unknown error"),
description: connectErrorMessage.split(":")[0],
icon: null,
duration: Infinity,
closeButton: true,
@ -531,7 +541,7 @@ export function Provider(props: {
setIdentifying(false);
sonnerToast.dismiss("mtp-connection-toast");
};
}, [mtpUrl, props.blockConnection, load]);
}, [mtpUrl, props.blockConnection, load, save]);
// No Iota check
useEffect(() => {

View file

@ -106,7 +106,7 @@ const authPayload = z.object({
Contacts: z
.array(
z.object({
LastMessageAt: z.number(),
LastMessageAt: z.number().default(0),
UserId: z.number(),
LastMessage: z
.object({
@ -114,7 +114,7 @@ const authPayload = z.object({
SenderId: z.number(),
})
.optional(),
Messages: z.array(Message),
Messages: z.array(Message).default([]),
}),
)
.default([]),
@ -129,6 +129,16 @@ const authPayload = z.object({
.default([]),
});
const clientStateSync = authPayload.extend({
SessionId: z.number().int().positive(),
VersionNumber: z.number().int().nonnegative(),
CacheSchemaVersion: z.number().int().nonnegative(),
SyncMode: z.enum(["full", "delta"]),
Messages: z.array(Message).default([]),
DeletedMessageIds: z.array(z.number()).default([]),
DeletedContactIds: z.array(z.number()).default([]),
});
export type Contacts = z.infer<typeof authPayload.shape.Contacts>;
export type Communities = z.infer<typeof authPayload.shape.Communities>;
export type Calls = z.infer<typeof authPayload.shape.Calls>;
@ -184,6 +194,26 @@ export const mtp = {
request: z.object({}).optional(),
response: authPayload,
},
ClientConnected: {
request: z.object({
SessionId: z.number().int().positive(),
VersionNumber: z.number().int().nonnegative(),
CacheValid: z.boolean(),
CacheSchemaVersion: z.number().int().nonnegative(),
}),
response: clientStateSync,
},
ClientStateSync: {
request: z.object({}).optional(),
response: clientStateSync,
},
ClientStateAck: {
request: z.object({
SessionId: z.number().int().positive(),
VersionNumber: z.number().int().nonnegative(),
}),
response: z.object({}),
},
GetUserData: {
request: z.object({
UserId: z.number().optional(),

18
pnpm-lock.yaml generated
View file

@ -6,7 +6,7 @@ settings:
overrides:
'@methanium/ui': https://git.methanium.net/methanium/ui/releases/download/0.0.8/methanium-ui.tgz
mtp: https://git.methanium.net/methanium/mtp/releases/download/0.2.0-3919eb46fd/mtp-0.2.0.tgz
mtp: link:../mtp
importers:
@ -16,8 +16,8 @@ importers:
specifier: https://git.methanium.net/methanium/ui/releases/download/0.0.8/methanium-ui.tgz
version: https://git.methanium.net/methanium/ui/releases/download/0.0.8/methanium-ui.tgz(@date-fns/tz@1.5.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(date-fns@4.4.0)(react-dom@19.2.7(react@19.2.7))(react-is@19.2.7)(react@19.2.7)(redux@5.0.1)(typescript@6.0.3)
mtp:
specifier: https://git.methanium.net/methanium/mtp/releases/download/0.2.0-3919eb46fd/mtp-0.2.0.tgz
version: https://git.methanium.net/methanium/mtp/releases/download/0.2.0-3919eb46fd/mtp-0.2.0.tgz
specifier: link:../mtp
version: link:../mtp
sonner:
specifier: ^2.0.7
version: 2.0.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
@ -700,8 +700,8 @@ importers:
specifier: ^1.14.0
version: 1.23.0(react@19.2.7)
mtp:
specifier: https://git.methanium.net/methanium/mtp/releases/download/0.2.0-3919eb46fd/mtp-0.2.0.tgz
version: https://git.methanium.net/methanium/mtp/releases/download/0.2.0-3919eb46fd/mtp-0.2.0.tgz
specifier: link:../../../mtp
version: link:../../../mtp
react:
specifier: ^19.2.0
version: 19.2.7
@ -4006,10 +4006,6 @@ packages:
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
mtp@https://git.methanium.net/methanium/mtp/releases/download/0.2.0-3919eb46fd/mtp-0.2.0.tgz:
resolution: {integrity: sha512-Adfwu1N48pSNZXaLpBW//uAeFaDCHYMOjWg+APG4TxEwCyQjXygCxBuEdUOjOxVZP9cK8L/K5Mf5HBO1+bd4HA==, tarball: https://git.methanium.net/methanium/mtp/releases/download/0.2.0-3919eb46fd/mtp-0.2.0.tgz}
version: 0.2.0
nanoid@3.3.15:
resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@ -8254,10 +8250,6 @@ snapshots:
ms@2.1.3: {}
mtp@https://git.methanium.net/methanium/mtp/releases/download/0.2.0-3919eb46fd/mtp-0.2.0.tgz:
dependencies:
yaml: 2.9.0
nanoid@3.3.15: {}
nanoid@3.3.16: {}

View file

@ -7,4 +7,4 @@ allowBuilds:
esbuild: true
overrides:
"@methanium/ui": "https://git.methanium.net/methanium/ui/releases/download/0.0.8/methanium-ui.tgz"
mtp: "https://git.methanium.net/methanium/mtp/releases/download/0.2.0-3919eb46fd/mtp-0.2.0.tgz"
mtp: "link:../mtp"