2 Commits

8 changed files with 22 additions and 31 deletions
+2 -2
View File
@@ -5,7 +5,7 @@ import { request } from "@/core/websocket";
import type { DmEnvelope, User } from "@/core/types"; import type { DmEnvelope, User } from "@/core/types";
import { ub64 } from "@/utils/utils"; import { ub64 } from "@/utils/utils";
import { fetchUserPublicKey } from "../crypto/identity"; import { fetchUserPublicKey } from "../crypto/identity";
import { fetchUsers, searchUsers } from "../user/search"; import { searchUsers } from "../user/search";
import { deriveWrappingKey, importAesGcmKey, aesGcmDecrypt } from "@fromchat/protocol"; import { deriveWrappingKey, importAesGcmKey, aesGcmDecrypt } from "@fromchat/protocol";
import tweetnacl from "tweetnacl"; import tweetnacl from "tweetnacl";
@@ -320,4 +320,4 @@ export async function editMessage(
} }
// Re-export user functions for convenience // Re-export user functions for convenience
export { fetchUsers, searchUsers, fetchUserPublicKey }; export { searchUsers, fetchUserPublicKey };
+2 -2
View File
@@ -3,7 +3,7 @@ import { getAuthHeaders } from "./account";
import { request } from "@/core/websocket"; import { request } from "@/core/websocket";
import type { DmEnvelope, User } from "@/core/types"; import type { DmEnvelope, User } from "@/core/types";
import { fetchUserPublicKey } from "./crypto"; import { fetchUserPublicKey } from "./crypto";
import { fetchUsers, searchUsers } from "./users"; import { searchUsers } from "./users";
/** /**
* Decrypt a DM envelope using client-side MEK unwrapping. * Decrypt a DM envelope using client-side MEK unwrapping.
@@ -25,7 +25,7 @@ export async function fetchDMHistory(userId: number, token: string, limit: numbe
} }
// Re-export user functions for convenience // Re-export user functions for convenience
export { fetchUsers, searchUsers, fetchUserPublicKey }; export { searchUsers, fetchUserPublicKey };
/** /**
* Send DM via WebSocket using transport encryption. * Send DM via WebSocket using transport encryption.
+2 -2
View File
@@ -3,7 +3,7 @@ import { getAuthHeaders } from "./account";
import { request } from "@/core/websocket"; import { request } from "@/core/websocket";
import type { DmEnvelope, User } from "@/core/types"; import type { DmEnvelope, User } from "@/core/types";
import { fetchUserPublicKey } from "./crypto"; import { fetchUserPublicKey } from "./crypto";
import { fetchUsers, searchUsers } from "./users"; import { searchUsers } from "./users";
export async function decryptDm(envelope: DmEnvelope): Promise<string> { export async function decryptDm(envelope: DmEnvelope): Promise<string> {
const { decrypt } = await import("./chats/dm"); const { decrypt } = await import("./chats/dm");
@@ -20,7 +20,7 @@ export async function fetchDMHistory(userId: number, token: string, limit: numbe
} }
// Re-export user functions for convenience // Re-export user functions for convenience
export { fetchUsers, searchUsers, fetchUserPublicKey }; export { searchUsers, fetchUserPublicKey };
export async function sendDMViaWebSocket(recipientId: number, recipientPublicKeyB64: string, plaintext: string, authToken: string, replyToId?: number): Promise<void> { export async function sendDMViaWebSocket(recipientId: number, recipientPublicKeyB64: string, plaintext: string, authToken: string, replyToId?: number): Promise<void> {
const { send } = await import("./chats/dm"); const { send } = await import("./chats/dm");
-10
View File
@@ -2,16 +2,6 @@ import { API_BASE_URL } from "@/core/config";
import { getAuthHeaders } from "./auth"; import { getAuthHeaders } from "./auth";
import type { User } from "@/core/types"; import type { User } from "@/core/types";
/**
* Fetches a list of all users (excluding current user)
*/
export async function fetchUsers(token: string): Promise<User[]> {
const res = await fetch(`${API_BASE_URL}/users`, { headers: getAuthHeaders(token, true) });
if (!res.ok) return [];
const data = await res.json();
return data.users || [];
}
/** /**
* Searches for users by username query * Searches for users by username query
*/ */
-10
View File
@@ -2,16 +2,6 @@ import { API_BASE_URL } from "@/core/config";
import { getAuthHeaders } from "./account"; import { getAuthHeaders } from "./account";
import type { User } from "@/core/types"; import type { User } from "@/core/types";
/**
* Fetches a list of all users (excluding current user)
*/
export async function fetchUsers(token: string): Promise<User[]> {
const res = await fetch(`${API_BASE_URL}/users`, { headers: getAuthHeaders(token, true) });
if (!res.ok) return [];
const data = await res.json();
return data.users || [];
}
/** /**
* Searches for users by username query * Searches for users by username query
*/ */
+12 -1
View File
@@ -22,6 +22,16 @@ export function isDeletedPeer(user: {
return isDeletedUser(user) || isDeletedAccountUsername(user.username); return isDeletedUser(user) || isDeletedAccountUsername(user.username);
} }
/** Peers should treat suspended accounts like deleted (no PII). */
export function isRedactedPeer(user: {
id?: number;
deleted?: boolean;
suspended?: boolean;
username?: string | null;
}): boolean {
return isDeletedPeer(user) || isSuspendedUser(user);
}
export const DELETED_ACCOUNT_LABEL = "Deleted account"; export const DELETED_ACCOUNT_LABEL = "Deleted account";
export function deletedUserLabel(): string { export function deletedUserLabel(): string {
@@ -33,8 +43,9 @@ export function displayNameForUser(user: {
display_name?: string | null; display_name?: string | null;
username?: string | null; username?: string | null;
deleted?: boolean; deleted?: boolean;
suspended?: boolean;
}): string { }): string {
if (isDeletedPeer(user)) { if (isRedactedPeer(user)) {
return deletedUserLabel(); return deletedUserLabel();
} }
return user.display_name?.trim() || user.username?.trim() || ""; return user.display_name?.trim() || user.username?.trim() || "";
@@ -10,7 +10,7 @@ import { onlineStatusManager } from "@/core/onlineStatusManager";
import { OnlineIndicator } from "@/pages/chat/ui/right/OnlineIndicator"; import { OnlineIndicator } from "@/pages/chat/ui/right/OnlineIndicator";
import defaultAvatar from "@/images/default-avatar.png"; import defaultAvatar from "@/images/default-avatar.png";
import { MaterialBadge, MaterialCircularProgress, MaterialIcon, MaterialList, MaterialListItem } from "@/utils/material"; import { MaterialBadge, MaterialCircularProgress, MaterialIcon, MaterialList, MaterialListItem } from "@/utils/material";
import { displayNameForUser, isDeletedPeer } from "@/core/userDisplay"; import { displayNameForUser, isRedactedPeer } from "@/core/userDisplay";
import { DeletedUserAvatar } from "@/core/DeletedUserAvatar"; import { DeletedUserAvatar } from "@/core/DeletedUserAvatar";
import styles from "@/pages/chat/css/left-panel.module.scss"; import styles from "@/pages/chat/css/left-panel.module.scss";
@@ -229,7 +229,7 @@ export function UnifiedChatsList() {
); );
} }
const isDeletedDm = isDeletedPeer(chat); const isDeletedDm = isRedactedPeer(chat);
const displayName = displayNameForUser({ ...chat, id: chat.id }); const displayName = displayNameForUser({ ...chat, id: chat.id });
return ( return (
+2 -2
View File
@@ -14,7 +14,7 @@ import { useImmer } from "use-immer";
import { createPortal } from "react-dom"; import { createPortal } from "react-dom";
import { parseProfileLink } from "@/core/profileLinks"; import { parseProfileLink } from "@/core/profileLinks";
import { MaterialCircularProgress, MaterialIconButton, MaterialList, MaterialListItem } from "@/utils/material"; import { MaterialCircularProgress, MaterialIconButton, MaterialList, MaterialListItem } from "@/utils/material";
import { displayNameForUser, isDeletedPeer } from "@/core/userDisplay"; import { displayNameForUser, isRedactedPeer } from "@/core/userDisplay";
import { DeletedUserAvatar } from "@/core/DeletedUserAvatar"; import { DeletedUserAvatar } from "@/core/DeletedUserAvatar";
import styles from "@/pages/chat/css/Message.module.scss"; import styles from "@/pages/chat/css/Message.module.scss";
import replyPreviewStyles from "@/pages/chat/css/reply-preview.module.scss"; import replyPreviewStyles from "@/pages/chat/css/reply-preview.module.scss";
@@ -519,7 +519,7 @@ export function Message({ message, isAuthor, onContextMenu, onReactionClick, isD
return messageText.length > 0 && emojiRegex.test(messageText) && messageText.length <= 4; // Most emojis are 1-4 characters return messageText.length > 0 && emojiRegex.test(messageText) && messageText.length <= 4; // Most emojis are 1-4 characters
}, [messageText]); }, [messageText]);
const isDeletedSender = isDeletedPeer({ id: message.user_id, username: message.username }); const isDeletedSender = isRedactedPeer({ id: message.user_id, username: message.username });
return ( return (
<> <>