From 69149b73bd4560835a134751cd34f57e34885207 Mon Sep 17 00:00:00 2001 From: Alois Date: Fri, 3 Jul 2026 21:49:13 +0200 Subject: [PATCH] (feat): remove mtp loading screen (fix): weird dedupe bugs (qol): update todo --- apps/web/src/index.tsx | 14 +++-- apps/web/vite.config.ts | 3 + packages/mtp/src/context.tsx | 101 +++++++++++++----------------- packages/mtp/todo.md | 1 + packages/shared/package.json | 1 + packages/shared/src/asyncQueue.ts | 27 ++++++++ 6 files changed, 86 insertions(+), 61 deletions(-) create mode 100644 packages/mtp/todo.md create mode 100644 packages/shared/src/asyncQueue.ts diff --git a/apps/web/src/index.tsx b/apps/web/src/index.tsx index bbc399e..0a35819 100644 --- a/apps/web/src/index.tsx +++ b/apps/web/src/index.tsx @@ -25,13 +25,13 @@ import CallPopout from "@tensamin/call/popout"; import ChatContext from "@tensamin/chat/context"; import { useInitializeCall } from "@tensamin/call/store"; import { Provider as MTPProvider } from "@tensamin/mtp"; -import UserContext from "@tensamin/user/context"; +import UserProvider from "@tensamin/user/context"; import DeeplinkContext from "@tensamin/tauri/deeplinkHandler"; import NotificationsProvider from "@tensamin/notifications/context"; import TAuthWrapper from "@tensamin/tauth/context"; -import { ThemeProvider, useTheme } from "@tensamin/ui"; +import { ErrorScreen, ThemeProvider, useTheme } from "@tensamin/ui"; import z from "zod"; import { useEffect, useRef, useState, type ReactNode } from "react"; @@ -256,7 +256,7 @@ function AppShell() { return ( - + @@ -268,7 +268,7 @@ function AppShell() { - + ); @@ -281,6 +281,12 @@ function CallInit() { const rootRoute = createRootRoute({ component: RootShell, + errorComponent: ({ error }: { error: Error }) => ( + + ), }); const appRoute = createRoute({ diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts index a224653..c2571df 100644 --- a/apps/web/vite.config.ts +++ b/apps/web/vite.config.ts @@ -114,6 +114,9 @@ export default defineConfig({ "@tensamin/crypto", "@tensamin/storage", "@tensamin/mtp", + "@tensamin/user", + "@tensamin/tauri", + "@tensamin/chat", "@codemirror/commands", "@codemirror/lang-markdown", "@codemirror/state", diff --git a/packages/mtp/src/context.tsx b/packages/mtp/src/context.tsx index 6b75c6e..9d4e6f6 100644 --- a/packages/mtp/src/context.tsx +++ b/packages/mtp/src/context.tsx @@ -10,8 +10,11 @@ import { } from "react"; import { isTauri } from "@tauri-apps/api/core"; import { onResume } from "tauri-plugin-app-events-api"; -import { ConnectionState, MTPClient } from "mtp"; -import type { z } from "zod"; +import { ConnectionState, MTPClient, codec } from "mtp"; +import { type z } from "zod"; +import createAsyncQueue, { + createQueuedFunc, +} from "@tensamin/shared/asyncQueue"; import { type Calls, @@ -20,9 +23,8 @@ import { mtp as schemas, type MTP as Schemas, } from "@tensamin/shared/data"; -import { log } from "@tensamin/shared/log"; +import { log, toast } from "@tensamin/shared/log"; import { useStorage } from "@tensamin/storage/context"; -import { ErrorScreen, LoadingScreen as Loading } from "@tensamin/ui"; import { PING_INTERVAL, @@ -91,6 +93,8 @@ type ContextType = { freshContacts: Contacts; freshCommunities: Communities; freshCalls: Calls; + contextReady: boolean; + loadingDescription: string; }; const MTPContext = createContext(undefined); @@ -161,9 +165,6 @@ export function Provider(props: { const [ownPing, setOwnPing] = useState(0); const [iotaPing, setIotaPing] = useState(0); - const [error, setError] = useState(""); - const [errorDescription, setErrorDescription] = useState(""); - const [freshCommunities, setFreshCommunities] = useState([]); const [freshContacts, setFreshContacts] = useState([]); const [freshCalls, setFreshCalls] = useState([]); @@ -234,9 +235,10 @@ export function Provider(props: { return subscribe("error_no_iota", () => { setIdentified(false); setIdentifying(false); - setError("We couldn't reach your Iota"); - setErrorDescription( - "You could try to restart your Iota, check for updates or check your network connection.", + toast( + "error", + "We couldn't reach your Iota", + "Check your network connection and try restarting your Iota", ); }); }, [connected, subscribe]); @@ -303,9 +305,10 @@ export function Provider(props: { if (disposed || reconnectScheduled) return; if (attempts >= RECONNECT_TRIES) { - setError("Connection Failed"); - setErrorDescription( - "Unable to connect to the server after multiple attempts. Please check your internet connection or try again later.", + toast( + "error", + "Connection Failed", + "Unable to connect to the Omikron after multiple attempts. Cehck your network connection.", ); log(0, "mtp", "red", "Reconnection attempts exhausted", reason); return; @@ -321,15 +324,16 @@ export function Provider(props: { }; async function connect() { - if (disposed || props.blockConnection) { - return; - } + if (disposed || props.blockConnection) return; try { setIdentified(false); setIdentifying(false); await MTPClient.init(); + const rawOmikronData = await fetch(mtpUrl ?? "").then((res) => + codec.decode(res.arrayBuffer()), + ); const client = await MTPClient.create({ url: mtpUrl ?? "", descriptor: "client", @@ -383,8 +387,6 @@ export function Provider(props: { scheduleReconnectReset(); setReadyState(client.state); setIdentifying(true); - setError(""); - setErrorDescription(""); await client.auth(); const finalResponse = await authPayload; @@ -427,9 +429,6 @@ export function Provider(props: { clearReconnectResetTimer(); attempts = 0; reconnectScheduled = false; - setError(""); - setErrorDescription(""); - await connect(); } @@ -459,53 +458,39 @@ export function Provider(props: { }; }, [mtpUrl, props.blockConnection]); - // Loading bar - const progress = useMemo(() => { - if (!mtpUrl) return 10; - if (readyState === ConnectionState.Connecting) return 30; - if (!connected) return 45; - if (identifying) return 75; - if (!identified) return 90; - return 100; - }, [connected, identified, identifying, readyState, mtpUrl]); - - const loadingTitle = useMemo(() => { - if (!mtpUrl) return "Looking up configuration"; - if (readyState === ConnectionState.Connecting || !connected) - return "Connecting to Tensamin"; - if (identifying || !identified) return "Identifying secure session"; - return "Loading"; - }, [connected, identified, identifying, readyState, mtpUrl]); - + // Async queue const loadingDescription = useMemo(() => { if (!mtpUrl) return "Loading connection details"; if (readyState === ConnectionState.Connecting || !connected) { return "Establishing transport channel"; } if (identifying || !identified) return "Waiting for authenticated session"; - return undefined; + return "Loading..."; }, [connected, identified, identifying, readyState, mtpUrl]); - - // Return - if (error !== "" && errorDescription !== "") { - return ; - } - - if (!connected || !identified || !mtpUrl) { - return ( - - ); - } + const contextReady = connected && identified && mtpUrl !== null; + const mtpRef = useMemo( + () => + createAsyncQueue<{ + send: typeof send; + subscribe: typeof subscribe; + subscribePush: typeof subscribePush; + }>(), + [], + ); + useEffect(() => { + if (connected && identified && mtpUrl) { + mtpRef.set({ + send, + subscribe, + subscribePush, + }); + } + }, [connected, identified, mtpUrl, send, subscribe, subscribePush, mtpRef]); return ( (contextReady ? send : null)), subscribe, subscribePush, readyState, @@ -515,6 +500,8 @@ export function Provider(props: { freshContacts, freshCommunities, freshCalls, + contextReady, + loadingDescription, }} > {props.children} diff --git a/packages/mtp/todo.md b/packages/mtp/todo.md new file mode 100644 index 0000000..5224278 --- /dev/null +++ b/packages/mtp/todo.md @@ -0,0 +1 @@ +- Get omikron url & public key from omega diff --git a/packages/shared/package.json b/packages/shared/package.json index 26c0d9f..f9f5ff6 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -4,6 +4,7 @@ "version": "0.0.0", "type": "module", "exports": { + "./asyncQueue": "./src/asyncQueue.ts", "./code": "./src/code.ts", "./data": "./src/data.ts", "./desktopMedia": "./src/desktopMedia.tsx", diff --git a/packages/shared/src/asyncQueue.ts b/packages/shared/src/asyncQueue.ts new file mode 100644 index 0000000..60d12f3 --- /dev/null +++ b/packages/shared/src/asyncQueue.ts @@ -0,0 +1,27 @@ +export default function createAsyncQueue() { + let resolve!: (value: T) => void; + + const promise = new Promise((r) => { + resolve = r; + }); + + return { + set: resolve, + get: () => promise, + }; +} + +// eslint-disable-next-line +type AnyFn = (...args: any[]) => any; + +export function createQueuedFunc( + getFn: () => F | null | undefined, +) { + return (async (...args: Parameters): Promise>> => { + while (!getFn()) { + await new Promise((r) => setTimeout(r, 50)); + } + + return await getFn()!(...args); + }) as F; +}