(undefined);
const ignoreSyncRef = useRef(false);
@@ -176,22 +143,7 @@ export default function Input(props: InputProps) {
});
}, [props.value]);
- return (
-
- );
+ return ;
}
/**
@@ -252,7 +204,7 @@ function createEditorExtensions(
}),
EditorView.theme({
"&": {
- fontSize: "inherit",
+ fontSize: "1rem",
},
"&.cm-editor": {
width: "100%",
diff --git a/packages/markdown/src/markdown.tsx b/packages/markdown/src/markdown.tsx
index 46ebb92..c3a17a6 100644
--- a/packages/markdown/src/markdown.tsx
+++ b/packages/markdown/src/markdown.tsx
@@ -560,7 +560,7 @@ export function renderBlocks(blocks: MarkdownBlock[]): React.ReactElement {
}
return (
-
+
{block.text.split("\n").map((line, lineIndex) => (
{lineIndex > 0 ?
: null}
@@ -645,6 +645,7 @@ export const markdownStyles = `
.tm-md-h4 { font-size: 1.1rem; }
.tm-md-h5 { font-size: 1rem; }
.tm-md-h6 { font-size: 0.95rem; opacity: 0.9; }
+.tm-md-p { margin: 0.25rem 0; }
.tm-md-blockquote { margin: 0.45rem 0; padding-left: 0.75rem; opacity: 0.95; }
.tm-md-blockquote p { margin: 0.2rem 0; }
.tm-md-pre { margin: 0.45rem 0; padding: 0.65rem 0.75rem; border-radius: 0.5rem; background: hsl(var(--muted)); overflow-x: auto; }
@@ -664,12 +665,12 @@ export const markdownStyles = `
.tm-md-table th { background: hsl(var(--muted)); font-weight: 600; }
.tm-md-hr { margin: 0.55rem 0; }
-.cm-editor.tm-md-editor { border-radius: inherit; background: transparent; caret-color: var(--foreground); }
+.cm-editor.tm-md-editor { border-radius: 0.65rem; background: hsl(var(--card)); caret-color: var(--foreground); }
.cm-editor.tm-md-editor.cm-focused { outline: none; box-shadow: none; }
.cm-editor.tm-md-editor .cm-scroller { font-family: inherit; line-height: 1.55; max-height: 30vh; overflow-y: auto; overflow-x: hidden; }
.cm-editor.tm-md-editor .cm-content { caret-color: var(--foreground); }
-.cm-editor.tm-md-editor .cm-content { padding: var(--tm-md-content-padding, 0.25rem 0.625rem); min-height: 2rem; }
-.cm-editor.tm-md-editor .cm-line { padding: 0; color: hsl(var(--foreground)); }
+.cm-editor.tm-md-editor .cm-content { padding: 0.7rem 0.85rem; min-height: 2.75rem; }
+.cm-editor.tm-md-editor .cm-line { padding: 0 1px; color: hsl(var(--foreground)); }
.cm-editor.tm-md-editor .tm-md-hidden-token { color: transparent; opacity: 0; font-size: inherit; }
.cm-editor.tm-md-editor .tm-md-code-line { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; background: hsl(var(--muted)); border-radius: 0.3rem; }
`;
diff --git a/packages/notifications/package.json b/packages/notifications/package.json
index a6b06be..af90c45 100644
--- a/packages/notifications/package.json
+++ b/packages/notifications/package.json
@@ -12,7 +12,6 @@
"build": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
- "@tanstack/react-router": "^1.169.1",
"@tauri-apps/api": "^2.11.0",
"@tensamin/crypto": "workspace:*",
"@tensamin/shared": "workspace:*",
diff --git a/packages/notifications/src/context.tsx b/packages/notifications/src/context.tsx
index 8da45f4..4e433a3 100644
--- a/packages/notifications/src/context.tsx
+++ b/packages/notifications/src/context.tsx
@@ -10,27 +10,25 @@ import { message as messageSchema } from "@tensamin/shared/data";
import { Avatar, AvatarFallback, AvatarImage } from "@tensamin/ui";
import { isTauri } from "@tauri-apps/api/core";
import { useSession } from "@tensamin/storage/session";
-import { useNavigate } from "@tanstack/react-router";
export const context = createContext(undefined);
-async function requestNotificationPermission() {
- if (!("Notification" in window)) return false;
-
- if (Notification.permission === "granted") return true;
-
- const permission = await Notification.requestPermission();
- return permission === "granted";
+function reduceDisplay(display: string) {
+ const words = display.split(" ");
+ if (words.length === 1) {
+ return display.slice(0, 2).toUpperCase();
+ } else {
+ return words[0].charAt(0).toUpperCase() + words[1].charAt(0).toUpperCase();
+ }
}
export default function Provider(props: { children: React.ReactNode }) {
- const { subscribePush, send } = useTTP();
+ const { subscribePush } = useTTP();
const { load } = useStorage();
const { get } = useUser();
const { decryptText, getSharedSecret } = useCrypto();
const { addLiveMessage, userId } = useChat();
const { moveUserIdToTop } = useSession();
- const navigate = useNavigate();
useEffect(() => {
return subscribePush(async (ttpMessage) => {
@@ -51,32 +49,7 @@ export default function Provider(props: { children: React.ReactNode }) {
message.content,
);
- // Update message state
- if (
- (await load("settings.read_confirmations")) &&
- userId === sender_id
- ) {
- void send(
- "message_state",
- {
- message_state: "read",
- },
- {
- id: ttpMessage.id,
- },
- );
- } else {
- void send(
- "message_state",
- {
- message_state: "received",
- },
- {
- id: ttpMessage.id,
- },
- );
- }
-
+ console.log(userId, sender_id, userId === sender_id);
if (userId === sender_id) {
addLiveMessage({
...message,
@@ -87,48 +60,25 @@ export default function Provider(props: { children: React.ReactNode }) {
return;
}
- // todo: add notification symbol to conversation cards (incl. message start)
+ // add notification symbol to conversation cards
moveUserIdToTop(sender_id);
if (isTauri()) {
console.log("weewoo");
} else {
- const hasPermissions = await requestNotificationPermission();
-
- if (hasPermissions) {
- const notification = new Notification(user.display, {
- body: decryptedContent,
- icon: user.avatar || user.display.slice(0, 2).toUpperCase(),
- badge: user.avatar || user.display.slice(0, 2).toUpperCase(),
- tag: `message-${user.user_id}`,
- silent: true,
- });
-
- notification.onclick = () => {
- window.focus();
- navigate({
- to: `/chat?id=${user.user_id}`,
- });
-
- notification.close();
- };
- } else {
- sonnerToast(user.display, {
- classNames: {
- content: "pl-4",
- },
- description: decryptedContent,
- icon: (
-
-
-
- {user.display.slice(0, 2).toUpperCase()}
-
-
- ),
- });
- }
+ sonnerToast(user.display, {
+ classNames: {
+ content: "pl-4",
+ },
+ description: decryptedContent,
+ icon: (
+
+
+ {reduceDisplay(user.display)}
+
+ ),
+ });
}
}
});
@@ -141,8 +91,6 @@ export default function Provider(props: { children: React.ReactNode }) {
addLiveMessage,
userId,
moveUserIdToTop,
- send,
- navigate,
]);
return (
diff --git a/packages/shared/src/data.ts b/packages/shared/src/data.ts
index 2210192..a059e9c 100644
--- a/packages/shared/src/data.ts
+++ b/packages/shared/src/data.ts
@@ -48,31 +48,6 @@ export type Communities = z.infer<
export type Calls = z.infer;
// TTP
-const user = z.object({
- about: z.string().max(255).optional(),
- avatar: z.string().optional(),
- display: z.string().min(1).max(15),
- iota_id: z.number(),
- omikron_connections: z.array(z.number()),
- omikron_id: z.number().optional(),
- online_status: z.enum([
- "user_offline",
- "user_online",
- "user_dnd",
- "user_idle",
- "user_wc",
- "user_borked",
- "iota_offline",
- "iota_online",
- "iota_borked",
- ]),
- public_key: z.base64(),
- status: z.string().max(15).optional(),
- sub_end: z.number(),
- sub_level: z.number(),
- user_id: z.number(),
- username: z.string().min(1).max(15),
-});
export const ttp = {
identification: {
request: z.object({
@@ -115,14 +90,33 @@ export const ttp = {
},
get_user_data: {
request: z.object({
- user_id: z.number().optional(),
- username: z.string().optional(),
+ user_id: z.number(),
+ }),
+ response: z.object({
+ about: z.string().max(255).optional(),
+ avatar: z.string().optional(),
+ display: z.string().max(15),
+ iota_id: z.number(),
+ omikron_connections: z.array(z.number()),
+ omikron_id: z.number().optional(),
+ online_status: z.enum([
+ "user_offline",
+ "user_online",
+ "user_dnd",
+ "user_idle",
+ "user_wc",
+ "user_borked",
+ "iota_offline",
+ "iota_online",
+ "iota_borked",
+ ]),
+ public_key: z.base64(),
+ status: z.string().max(15).optional(),
+ sub_end: z.number(),
+ sub_level: z.number(),
+ user_id: z.number(),
+ username: z.string().max(15),
}),
- response: user,
- },
- change_user_data: {
- request: user.partial(),
- response: z.object({}),
},
ping: {
request: z.object({
@@ -167,17 +161,11 @@ export const ttp = {
response: z.object({}),
},
message_state: {
- request: z
- .object({
- chat_partner_id: z.number(),
- send_time: z.number(),
- message_state: message.shape.message_state,
- })
- .or(
- z.object({
- message_state: message.shape.message_state,
- }),
- ),
+ request: z.object({
+ chat_partner_id: z.number(),
+ send_time: z.number(),
+ message_state: message.shape.message_state,
+ }),
response: z.object({
chat_partner_id: z.number(),
message_state: message.shape.message_state,
@@ -256,9 +244,6 @@ export interface Storage extends SettingsStorageDefaults {
legal_docs: z.infer;
cached_contacts: Contacts;
cached_communities: Communities;
- ttp_url: string;
- call_mute_range_start: number;
- call_mute_range_end: number;
}
export const storageDefaults: Storage = {
@@ -291,32 +276,4 @@ export const storageDefaults: Storage = {
},
cached_contacts: [],
cached_communities: [],
- ttp_url: "https://tensamin.net:959",
- call_mute_range_start: -55,
- call_mute_range_end: -45,
};
-
-// User Status
-export function getStatusColor(
- status: z.infer,
-) {
- switch (status) {
- case "user_online":
- return "#22c55e";
- case "iota_online":
- return "#22c55e";
- case "user_dnd":
- return "#ef4444";
- case "user_idle":
- return "#f59e0b";
- case "user_wc":
- return "#3b82f6";
- case "user_borked":
- case "iota_borked":
- return "#6b7280";
- case "user_offline":
- case "iota_offline":
- default:
- return "#9ca3af";
- }
-}
diff --git a/packages/shared/src/settings.ts b/packages/shared/src/settings.ts
index aacc162..0099f64 100644
--- a/packages/shared/src/settings.ts
+++ b/packages/shared/src/settings.ts
@@ -28,21 +28,6 @@ const settings = {
type: "boolean",
default: false,
},
- read_confirmations: {
- display: "Enable Read Confirmations",
- type: "boolean",
- default: true,
- },
- receive_confirmations: {
- display: "Enable Receive Confirmations",
- type: "boolean",
- default: true,
- },
- show_start_of_last_message_in_sidebar: {
- display: "Show Start of Last Message in Sidebar",
- type: "boolean",
- default: true,
- },
},
},
application: {
@@ -50,7 +35,6 @@ const settings = {
},
} as const satisfies SettingsSchema;
-// Assemble storage defaults
export default settings;
type BooleanSettingNames = {
diff --git a/packages/storage/src/session.tsx b/packages/storage/src/session.tsx
index d992a03..03b3826 100644
--- a/packages/storage/src/session.tsx
+++ b/packages/storage/src/session.tsx
@@ -14,7 +14,6 @@ interface SessionContextType {
communities: Communities;
calls: Calls;
moveUserIdToTop: (userId: number) => void;
- insertContact: (userId: number) => void;
}
const SessionContext = createContext(undefined);
@@ -70,31 +69,9 @@ export default function SessionProvider({ children }: { children: ReactNode }) {
});
};
- const insertContact = (userId: number) => {
- setContacts((prevContacts) => {
- if (prevContacts.some((contact) => contact.user_id === userId)) {
- return prevContacts;
- }
-
- const newUser = {
- user_id: userId,
- last_message_at: new Date().getTime(),
- messages: [],
- } satisfies Contacts[0];
-
- return [newUser, ...prevContacts];
- });
- };
-
return (
{children}
diff --git a/packages/ttp/src/context.tsx b/packages/ttp/src/context.tsx
index d33313d..f1c30aa 100644
--- a/packages/ttp/src/context.tsx
+++ b/packages/ttp/src/context.tsx
@@ -24,6 +24,7 @@ import {
RECONNECT_RESET,
RECONNECT_TRIES,
RETRY_INTERVAL,
+ TRANSPORT_URL,
} from "./values";
import {
type Calls,
@@ -191,15 +192,6 @@ export function Provider(props: {
typeof createTransportClient
> | null>(null);
const identificationStartedRef = useRef(false);
- const identificationCancelRef = useRef(false);
-
- // Load ttp url
- const [ttpUrl, setTtpUrl] = useState(null);
- useEffect(() => {
- load("ttp_url").then((url) => {
- setTtpUrl(url);
- });
- }, [load]);
/**
* Sends typed protocol messages through the active transport client.
@@ -243,23 +235,6 @@ export function Provider(props: {
return client.subscribePush(handler);
}, []);
- // Check for error_no_iota
- useEffect(() => {
- if (!connected) return;
-
- return subscribePush((message) => {
- if (message.type !== "error_no_iota") return;
-
- identificationCancelRef.current = true;
- 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.",
- );
- });
- }, [connected, subscribePush]);
-
useEffect(() => {
if (!connected || !identified) {
return;
@@ -291,10 +266,6 @@ export function Provider(props: {
}, [connected, identified, send]);
useEffect(() => {
- if (!ttpUrl) {
- return;
- }
-
let attempts = 0;
let reconnectTimer: ReturnType | null = null;
let reconnectResetTimer: ReturnType | null = null;
@@ -372,7 +343,7 @@ export function Provider(props: {
const transportClient = !props.blockConnection
? createTransportClient(schemas, {
- url: ttpUrl,
+ url: TRANSPORT_URL,
onReadyStateChange: (state) => {
currentReadyState = state;
setReadyState(state);
@@ -381,7 +352,6 @@ export function Provider(props: {
clearReconnectTimer();
scheduleReconnectReset();
identificationStartedRef.current = false;
- identificationCancelRef.current = false;
setConnected(true);
setIdentified(false);
setError("");
@@ -422,12 +392,8 @@ export function Provider(props: {
return;
}
- if (!ttpUrl) {
- return;
- }
-
try {
- await transportClient?.connect(ttpUrl);
+ await transportClient?.connect(TRANSPORT_URL);
} catch (connectError) {
if (disposed) {
return;
@@ -494,7 +460,7 @@ export function Provider(props: {
setIdentifying(false);
identificationStartedRef.current = false;
};
- }, [props.blockConnection, ttpUrl]);
+ }, [props.blockConnection]);
useEffect(() => {
if (!connected) {
@@ -502,11 +468,6 @@ export function Provider(props: {
return;
}
- if (identificationCancelRef.current) {
- identificationStartedRef.current = false;
- return;
- }
-
if (identificationStartedRef.current) {
return;
}
@@ -575,12 +536,10 @@ export function Provider(props: {
const finalResponse = await send("challenge_response", {
challenge: decryptedChallenge,
}).catch((error) => {
- if (!identificationCancelRef.current) {
- setError("Identification Failed");
- setErrorDescription(
- "Unable to complete secure identification. Please verify your credentials and try again.",
- );
- }
+ setError("Identification Failed");
+ setErrorDescription(
+ "Unable to complete secure identification. Please verify your credentials and try again.",
+ );
throw error;
});
@@ -588,7 +547,7 @@ export function Provider(props: {
setFreshContacts(finalResponse.data.contacts);
setFreshCommunities(finalResponse.data.communities);
setFreshCalls(finalResponse.data.calls);
- if (cancelled || identificationCancelRef.current) {
+ if (cancelled) {
return;
}
@@ -600,10 +559,6 @@ export function Provider(props: {
return;
}
- if (identificationCancelRef.current) {
- return;
- }
-
if (isStopSendingError(identificationError)) {
setError("Connection closed");
setErrorDescription(
@@ -634,7 +589,7 @@ export function Provider(props: {
: "Unable to complete secure identification because the transport request failed.",
);
} finally {
- if (!cancelled && !identificationCancelRef.current) {
+ if (!cancelled) {
setIdentifying(false);
}
}
@@ -648,19 +603,14 @@ export function Provider(props: {
}, [connected, decrypt, getSharedSecret, load, send]);
const progress = useMemo(() => {
- if (!ttpUrl) return 10;
if (readyState === READY_STATE.CONNECTING) return 30;
if (!connected) return 45;
if (identifying) return 75;
if (!identified) return 90;
return 100;
- }, [connected, identified, identifying, readyState, ttpUrl]);
+ }, [connected, identified, identifying, readyState]);
const loadingTitle = useMemo(() => {
- if (!ttpUrl) {
- return "Looking up configuration";
- }
-
if (readyState === READY_STATE.CONNECTING || !connected) {
return "Connecting to Tensamin";
}
@@ -670,13 +620,9 @@ export function Provider(props: {
}
return "Loading";
- }, [connected, identified, identifying, readyState, ttpUrl]);
+ }, [connected, identified, identifying, readyState]);
const loadingDescription = useMemo(() => {
- if (!ttpUrl) {
- return "Loading connection details";
- }
-
if (readyState === READY_STATE.CONNECTING || !connected) {
return "Establishing transport channel";
}
@@ -686,13 +632,13 @@ export function Provider(props: {
}
return undefined;
- }, [connected, identified, identifying, readyState, ttpUrl]);
+ }, [connected, identified, identifying, readyState]);
if (error !== "" && errorDescription !== "") {
return ;
}
- if (!connected || !identified || !ttpUrl) {
+ if (!connected || !identified) {
return (