(fix): connections (again)
Some checks failed
/ build-web (push) Failing after 58s
/ build-mobile (push) Failing after 1m11s
/ build-desktop (linux) (push) Failing after 1m11s
/ release (push) Has been skipped

(qol): update todo
This commit is contained in:
Alois 2026-07-28 23:53:53 +02:00
commit 3666902ac9
Signed by: alois
SSH key fingerprint: SHA256:GBzT2DXvAuGV9XIV5W3WrzVpjU54FThmxHXdbz95J24
6 changed files with 88 additions and 57 deletions

1
TODO
View file

@ -2,5 +2,6 @@
- Add packages/hotkeys/ - Add packages/hotkeys/
- Full accessability - Full accessability
- Add settings saving & update onboarding to use it - Add settings saving & update onboarding to use it
- Add onboarding page to load one profile during onboarding
-> Most folders in packages/ have specific todos -> Most folders in packages/ have specific todos

View file

@ -27,6 +27,7 @@ export function Basic({
return ( return (
<Button <Button
render={<div />} render={<div />}
nativeButton={false}
variant="outline" variant="outline"
className="animate-in fade-in duration-300 h-auto w-full justify-start gap-2.5 rounded-xl min-h-12.5!" className="animate-in fade-in duration-300 h-auto w-full justify-start gap-2.5 rounded-xl min-h-12.5!"
> >

@ -1 +1 @@
Subproject commit fee0a8fa9994b6a2d04e0966387c713c47c39b7c Subproject commit 11a1d79409857b734e948dd8c3e28e6ba721d15f

View file

@ -237,6 +237,7 @@ export function Provider(props: {
let reconnectScheduled = false; let reconnectScheduled = false;
let disposed = false; let disposed = false;
let resumeListenerRegistered = false; let resumeListenerRegistered = false;
let connectionGeneration = 0;
const clearReconnectTimer = () => { const clearReconnectTimer = () => {
if (!reconnectTimer) return; if (!reconnectTimer) return;
@ -251,16 +252,52 @@ export function Provider(props: {
reconnectResetTimer = null; reconnectResetTimer = null;
}; };
const scheduleReconnect = (error: unknown) => {
if (disposed || reconnectScheduled) return;
if (attempts >= RECONNECT_TRIES) {
log(0, "mtp", "red", "Reconnection attempts exhausted", error);
sonnerToast.error("Connection failed", {
id: "mtp-connection-toast",
description:
error instanceof Error ? error.message.split(":")[0] : "Connection lost",
icon: null,
duration: Infinity,
closeButton: true,
promise: null,
} as unknown as Parameters<typeof sonnerToast.error>[1]);
return;
}
attempts += 1;
sonnerToast.loading(
`Reconnecting to server... (attempt ${attempts} of ${RECONNECT_TRIES})`,
{ id: "mtp-connection-toast" },
);
reconnectScheduled = true;
reconnectTimer = setTimeout(() => {
reconnectScheduled = false;
reconnectTimer = null;
void connect();
}, RETRY_INTERVAL);
};
async function connect() { async function connect() {
if (disposed || props.blockConnection) return; if (disposed || props.blockConnection) return;
const generation = ++connectionGeneration;
let client: Awaited<ReturnType<typeof MTPClient.create>> | null = null;
let failed = false;
const cleanup = () => { const cleanup = () => {
clientRef.current?.disconnect(); client?.disconnect();
clientRef.current = null; if (clientRef.current === client) {
clientRef.current = null;
}
clearReconnectResetTimer(); clearReconnectResetTimer();
setReadyState(ConnectionState.Disconnected); if (generation === connectionGeneration) {
setIdentified(false); setReadyState(ConnectionState.Disconnected);
setIdentifying(false); setIdentified(false);
setIdentifying(false);
}
}; };
try { try {
setIdentified(false); setIdentified(false);
@ -324,7 +361,7 @@ export function Provider(props: {
log(2, "mtp", "green", "Connecting to: " + url); log(2, "mtp", "green", "Connecting to: " + url);
const client = await MTPClient.create({ client = await MTPClient.create({
url, url,
credentials: { credentials: {
clientId: userId, clientId: userId,
@ -335,9 +372,20 @@ export function Provider(props: {
pings: true, pings: true,
logger: (event) => { logger: (event) => {
if (event.type === "state") { if (event.type === "state") {
setReadyState( if (generation !== connectionGeneration) return;
clientRef.current?.state ?? ConnectionState.Disconnected, const state = client?.state ?? ConnectionState.Disconnected;
); setReadyState(state);
if (
state === ConnectionState.Disconnected &&
clientRef.current === client &&
!failed
) {
failed = true;
clientRef.current = null;
setIdentified(false);
setIdentifying(false);
scheduleReconnect(new Error("MTP connection lost"));
}
} }
if (event.type !== "Pong" && event.type !== "Ping") { if (event.type !== "Pong" && event.type !== "Ping") {
@ -364,13 +412,14 @@ export function Provider(props: {
}, },
}); });
if (disposed) { if (disposed || generation !== connectionGeneration) {
client.disconnect(); client.disconnect();
return; return;
} }
const activeClient = client;
clientRef.current = client; clientRef.current = activeClient;
setReadyState(client.state); setReadyState(activeClient.state);
clearReconnectTimer(); clearReconnectTimer();
@ -381,7 +430,7 @@ export function Provider(props: {
reconnectResetTimer = null; reconnectResetTimer = null;
}, RECONNECT_RESET * 1_000); }, RECONNECT_RESET * 1_000);
setReadyState(client.state); setReadyState(activeClient.state);
setIdentifying(true); setIdentifying(true);
const stateSync = new Promise<ProtocolMessage<"ClientStateSync">>( const stateSync = new Promise<ProtocolMessage<"ClientStateSync">>(
@ -390,7 +439,7 @@ export function Provider(props: {
unsubscribe(); unsubscribe();
reject(new Error("Initial state synchronization timed out")); reject(new Error("Initial state synchronization timed out"));
}, 120_000); }, 120_000);
const unsubscribe = client.subscribe( const unsubscribe = activeClient.subscribe(
"ClientStateSync", "ClientStateSync",
(message) => { (message) => {
clearTimeout(timeout); clearTimeout(timeout);
@ -404,7 +453,10 @@ export function Provider(props: {
); );
}, },
); );
const [, finalResponse] = await Promise.all([client.auth(), stateSync]); const [, finalResponse] = await Promise.all([
activeClient.auth(),
stateSync,
]);
if (finalResponse.type.startsWith("Error")) { if (finalResponse.type.startsWith("Error")) {
throw new Error( throw new Error(
@ -412,7 +464,7 @@ export function Provider(props: {
); );
} }
const acknowledgement = await client.request("ClientStateAck", { const acknowledgement = await activeClient.request("ClientStateAck", {
SessionId: finalResponse.data.SessionId, SessionId: finalResponse.data.SessionId,
VersionNumber: finalResponse.data.VersionNumber, VersionNumber: finalResponse.data.VersionNumber,
}); });
@ -422,7 +474,7 @@ export function Provider(props: {
); );
} }
if (disposed || clientRef.current !== client) return; if (disposed || clientRef.current !== activeClient) return;
setFreshContacts(finalResponse.data.Contacts); setFreshContacts(finalResponse.data.Contacts);
setFreshCommunities(finalResponse.data.Communities); setFreshCommunities(finalResponse.data.Communities);
@ -431,7 +483,11 @@ export function Provider(props: {
setIdentified(true); setIdentified(true);
resolveConnectionRef.current?.(); resolveConnectionRef.current?.();
} catch (connectError) { } catch (connectError) {
if (disposed) return; if (disposed || generation !== connectionGeneration) {
client?.disconnect();
return;
}
failed = true;
cleanup(); cleanup();
const connectErrorMessage = const connectErrorMessage =
connectError instanceof Error connectError instanceof Error
@ -445,41 +501,14 @@ export function Provider(props: {
getProtocolErrorDetails(connectError) ?? connectError, getProtocolErrorDetails(connectError) ?? connectError,
); );
// Schedule reconnect scheduleReconnect(connectError);
if (disposed || reconnectScheduled) return;
if (attempts >= RECONNECT_TRIES) {
log(0, "mtp", "red", "Reconnection attempts exhausted", connectError);
sonnerToast.error("Connection failed", {
id: "mtp-connection-toast",
description: connectErrorMessage.split(":")[0],
icon: null,
duration: Infinity,
closeButton: true,
promise: null,
} as unknown as Parameters<typeof sonnerToast.error>[1]);
return;
}
attempts += 1;
// Show loading toast
sonnerToast.loading(
`Reconnecting to server... (attempt ${attempts} of ${RECONNECT_TRIES})`,
{ id: "mtp-connection-toast" },
);
reconnectScheduled = true;
reconnectTimer = setTimeout(() => {
reconnectScheduled = false;
reconnectTimer = null;
void connect();
}, RETRY_INTERVAL);
} }
} }
async function reconnectAfterResume() { async function reconnectAfterResume() {
if (disposed) return; if (disposed) return;
connectionGeneration += 1;
clientRef.current?.disconnect(); clientRef.current?.disconnect();
clientRef.current = null; clientRef.current = null;
clearReconnectTimer(); clearReconnectTimer();

16
pnpm-lock.yaml generated
View file

@ -6,7 +6,7 @@ settings:
overrides: overrides:
'@methanium/ui': https://git.methanium.net/methanium/ui/releases/download/0.0.10/methanium-ui.tgz '@methanium/ui': https://git.methanium.net/methanium/ui/releases/download/0.0.10/methanium-ui.tgz
mtp: https://git.methanium.net/methanium/mtp/releases/download/0.2.0-dev-fa271e6/mtp-0.2.0.tgz mtp: https://git.methanium.net/methanium/mtp/releases/download/0.2.0-dev-6f673ba/mtp-0.2.0.tgz
importers: importers:
@ -16,8 +16,8 @@ importers:
specifier: https://git.methanium.net/methanium/ui/releases/download/0.0.10/methanium-ui.tgz specifier: https://git.methanium.net/methanium/ui/releases/download/0.0.10/methanium-ui.tgz
version: https://git.methanium.net/methanium/ui/releases/download/0.0.10/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) version: https://git.methanium.net/methanium/ui/releases/download/0.0.10/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: mtp:
specifier: https://git.methanium.net/methanium/mtp/releases/download/0.2.0-dev-fa271e6/mtp-0.2.0.tgz specifier: https://git.methanium.net/methanium/mtp/releases/download/0.2.0-dev-6f673ba/mtp-0.2.0.tgz
version: https://git.methanium.net/methanium/mtp/releases/download/0.2.0-dev-fa271e6/mtp-0.2.0.tgz version: https://git.methanium.net/methanium/mtp/releases/download/0.2.0-dev-6f673ba/mtp-0.2.0.tgz
sonner: sonner:
specifier: ^2.0.7 specifier: ^2.0.7
version: 2.0.7(react-dom@19.2.7(react@19.2.7))(react@19.2.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 specifier: ^1.14.0
version: 1.23.0(react@19.2.7) version: 1.23.0(react@19.2.7)
mtp: mtp:
specifier: https://git.methanium.net/methanium/mtp/releases/download/0.2.0-dev-fa271e6/mtp-0.2.0.tgz specifier: https://git.methanium.net/methanium/mtp/releases/download/0.2.0-dev-6f673ba/mtp-0.2.0.tgz
version: https://git.methanium.net/methanium/mtp/releases/download/0.2.0-dev-fa271e6/mtp-0.2.0.tgz version: https://git.methanium.net/methanium/mtp/releases/download/0.2.0-dev-6f673ba/mtp-0.2.0.tgz
react: react:
specifier: ^19.2.0 specifier: ^19.2.0
version: 19.2.7 version: 19.2.7
@ -4006,8 +4006,8 @@ packages:
ms@2.1.3: ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
mtp@https://git.methanium.net/methanium/mtp/releases/download/0.2.0-dev-fa271e6/mtp-0.2.0.tgz: mtp@https://git.methanium.net/methanium/mtp/releases/download/0.2.0-dev-6f673ba/mtp-0.2.0.tgz:
resolution: {integrity: sha512-kvJ98CMrr/ZCQr1oVhys9D3c+/OZAl6Oar3DjI6bvnYLelpjnXbrtq/8A31ktbipgMsaRInQuFBKRYMBm/f8OQ==, tarball: https://git.methanium.net/methanium/mtp/releases/download/0.2.0-dev-fa271e6/mtp-0.2.0.tgz} resolution: {integrity: sha512-WcXHd53aDM0zQfO8eaUnK5tpnRP73GlWjwAMMcstcVAIWrvyFGuINkCRNEkb2RsNk9h57UMhxElCZeBkFrVmCg==, tarball: https://git.methanium.net/methanium/mtp/releases/download/0.2.0-dev-6f673ba/mtp-0.2.0.tgz}
version: 0.2.0 version: 0.2.0
nanoid@3.3.15: nanoid@3.3.15:
@ -8254,7 +8254,7 @@ snapshots:
ms@2.1.3: {} ms@2.1.3: {}
mtp@https://git.methanium.net/methanium/mtp/releases/download/0.2.0-dev-fa271e6/mtp-0.2.0.tgz: mtp@https://git.methanium.net/methanium/mtp/releases/download/0.2.0-dev-6f673ba/mtp-0.2.0.tgz:
dependencies: dependencies:
yaml: 2.9.0 yaml: 2.9.0

View file

@ -7,4 +7,4 @@ allowBuilds:
esbuild: true esbuild: true
overrides: overrides:
"@methanium/ui": "https://git.methanium.net/methanium/ui/releases/download/0.0.10/methanium-ui.tgz" "@methanium/ui": "https://git.methanium.net/methanium/ui/releases/download/0.0.10/methanium-ui.tgz"
mtp: "https://git.methanium.net/methanium/mtp/releases/download/0.2.0-dev-fa271e6/mtp-0.2.0.tgz" mtp: "https://git.methanium.net/methanium/mtp/releases/download/0.2.0-dev-6f673ba/mtp-0.2.0.tgz"