mirror of
https://github.com/fromchat-messenger/web.git
synced 2026-09-22 19:15:08 +03:00
Change the structure
This commit is contained in:
@@ -6,7 +6,7 @@ import { useEffect, useRef } from "react";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
import { useUserStore } from "@/state/user";
|
||||
import { useProfileStore } from "@/state/profile";
|
||||
import { fetchUserProfileById, fetchUserProfile } from "@/core/api/account/profile";
|
||||
import api from "@/core/api";
|
||||
import styles from "@/pages/chat/css/layout.module.scss";
|
||||
|
||||
export default function ChatPage() {
|
||||
@@ -43,10 +43,10 @@ export default function ChatPage() {
|
||||
|
||||
if (profileInfo.userId) {
|
||||
// Fetch by user ID
|
||||
userProfile = await fetchUserProfileById(user.authToken, profileInfo.userId);
|
||||
userProfile = await api.user.profile.fetchById(user.authToken, profileInfo.userId);
|
||||
} else if (profileInfo.username) {
|
||||
// Fetch by username
|
||||
userProfile = await fetchUserProfile(user.authToken, profileInfo.username);
|
||||
userProfile = await api.user.profile.fetchByUsername(user.authToken, profileInfo.username);
|
||||
}
|
||||
|
||||
if (userProfile) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { ProfileDialogData } from "@/state/types";
|
||||
import defaultAvatar from "@/images/default-avatar.png";
|
||||
import { confirm } from "mdui/functions/confirm";
|
||||
import { prompt } from "mdui/functions/prompt";
|
||||
import { updateProfile, uploadProfilePicture, fetchUserProfileById, suspendUser, unsuspendUser, deleteUser } from "@/core/api/account/profile";
|
||||
import api from "@/core/api";
|
||||
import { RichTextArea } from "@/core/components/RichTextArea";
|
||||
import { StatusBadge } from "@/core/components/StatusBadge";
|
||||
import { VerifyButton } from "@/core/components/VerifyButton";
|
||||
@@ -98,7 +98,7 @@ export function ProfileDialog() {
|
||||
|
||||
// If it's not the public chat and has a user ID, fetch fresh data
|
||||
if (profileData.userId && profileData.username !== "Общий чат") {
|
||||
const userProfile = await fetchUserProfileById(user.authToken, profileData.userId);
|
||||
const userProfile = await api.user.profile.fetchById(user.authToken, profileData.userId);
|
||||
if (userProfile) {
|
||||
freshData = {
|
||||
...userProfile,
|
||||
@@ -285,7 +285,7 @@ export function ProfileDialog() {
|
||||
}
|
||||
|
||||
if (Object.keys(updateData).length > 0) {
|
||||
await updateProfile(user.authToken, updateData);
|
||||
await api.user.profile.update(user.authToken, updateData);
|
||||
}
|
||||
|
||||
// Update profile picture if changed
|
||||
@@ -294,7 +294,7 @@ export function ProfileDialog() {
|
||||
if (currentData.profilePicture.startsWith("data:")) {
|
||||
const response = await fetch(currentData.profilePicture);
|
||||
const blob = await response.blob();
|
||||
await uploadProfilePicture(user.authToken, blob);
|
||||
await api.user.profile.uploadPicture(user.authToken, blob);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,7 +351,7 @@ export function ProfileDialog() {
|
||||
});
|
||||
|
||||
if (reason) {
|
||||
const result = await suspendUser(currentData.userId, reason, user.authToken!);
|
||||
const result = await api.moderation.users.suspend(currentData.userId, reason, user.authToken!);
|
||||
if (result) {
|
||||
closeProfileDialog();
|
||||
} else {
|
||||
@@ -360,7 +360,7 @@ export function ProfileDialog() {
|
||||
}
|
||||
} else {
|
||||
// Unsuspend user
|
||||
const result = await unsuspendUser(currentData.userId, user.authToken!);
|
||||
const result = await api.moderation.users.unsuspend(currentData.userId, user.authToken!);
|
||||
if (result) {
|
||||
closeProfileDialog();
|
||||
} else {
|
||||
@@ -383,7 +383,7 @@ export function ProfileDialog() {
|
||||
cancelText: "Cancel"
|
||||
});
|
||||
|
||||
const result = await deleteUser(currentData.userId, user.authToken!);
|
||||
const result = await api.moderation.users.deleteUser(currentData.userId, user.authToken!);
|
||||
|
||||
if (result) {
|
||||
closeProfileDialog();
|
||||
|
||||
@@ -2,8 +2,7 @@ import { useState, useEffect, useCallback, useMemo } from "react";
|
||||
import { useUserStore } from "@/state/user";
|
||||
import { useChatStore } from "@/state/chat";
|
||||
import { useDM, type DMUser } from "@/pages/chat/hooks/useDM";
|
||||
import { fetchMessages } from "@/core/api/messaging";
|
||||
import { fetchUserPublicKey } from "@/core/api/dm";
|
||||
import api from "@/core/api";
|
||||
import { StatusBadge } from "@/core/components/StatusBadge";
|
||||
import type { Message } from "@/core/types";
|
||||
import { websocket } from "@/core/websocket";
|
||||
@@ -52,7 +51,7 @@ export function UnifiedChatsList() {
|
||||
if (!user.authToken) return;
|
||||
|
||||
try {
|
||||
const messages = await fetchMessages(user.authToken, 1);
|
||||
const { messages } = await api.chats.general.fetchMessages(user.authToken, 1);
|
||||
if (messages?.length > 0) {
|
||||
const lastMessage = messages[messages.length - 1];
|
||||
setLastMessages({ general: lastMessage });
|
||||
@@ -160,7 +159,7 @@ export function UnifiedChatsList() {
|
||||
const authToken = useUserStore.getState().user.authToken;
|
||||
if (!authToken) return;
|
||||
|
||||
const publicKey = await fetchUserPublicKey(dmConversation.id, authToken);
|
||||
const publicKey = await api.chats.dm.fetchUserPublicKey(dmConversation.id, authToken);
|
||||
if (!publicKey) {
|
||||
console.error("Failed to get public key for user:", dmConversation.id);
|
||||
return;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { useUserStore } from "@/state/user";
|
||||
import { useChatStore } from "@/state/chat";
|
||||
import { searchUsers, fetchUserPublicKey } from "@/core/api/dm";
|
||||
import api from "@/core/api";
|
||||
import { StatusBadge } from "@/core/components/StatusBadge";
|
||||
import type { User } from "@/core/types";
|
||||
import { onlineStatusManager } from "@/core/onlineStatusManager";
|
||||
@@ -45,7 +45,7 @@ export function UsernameSearch({ containerRef, headerRef, bottomAppBarRef }: Use
|
||||
const newTimeout = setTimeout(async () => {
|
||||
if (user.authToken) {
|
||||
try {
|
||||
const users = await searchUsers(searchQuery, user.authToken);
|
||||
const users = await api.user.search.searchUsers(searchQuery, user.authToken);
|
||||
setSearchResults(users);
|
||||
} catch (error) {
|
||||
console.error("Search failed:", error);
|
||||
@@ -118,7 +118,7 @@ export function UsernameSearch({ containerRef, headerRef, bottomAppBarRef }: Use
|
||||
|
||||
let publicKey = searchUser.publicKey;
|
||||
if (!publicKey) {
|
||||
const fetchedPublicKey = await fetchUserPublicKey(searchUser.id, user.authToken);
|
||||
const fetchedPublicKey = await api.chats.dm.fetchUserPublicKey(searchUser.id, user.authToken);
|
||||
publicKey = fetchedPublicKey;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { MaterialList, MaterialListItem } from "@/utils/material";
|
||||
import { useUserStore } from "@/state/user";
|
||||
import { deleteAccount } from "@/core/api/account";
|
||||
import api from "@/core/api";
|
||||
import { confirm } from "mdui/functions/confirm";
|
||||
import styles from "@/pages/chat/css/settings-dialog.module.scss";
|
||||
|
||||
@@ -23,7 +23,7 @@ export function AccountPanel({ onClose }: AccountPanelProps) {
|
||||
cancelText: "Cancel"
|
||||
});
|
||||
|
||||
await deleteAccount(authToken);
|
||||
await api.user.auth.deleteAccount(authToken);
|
||||
logout();
|
||||
onClose();
|
||||
} catch (error) {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useState } from "react";
|
||||
import { StyledDialog } from "@/core/components/StyledDialog";
|
||||
import type { DialogProps } from "@/core/types";
|
||||
import { useUserStore } from "@/state/user";
|
||||
import { changePassword } from "@/core/api/account";
|
||||
import api from "@/core/api";
|
||||
import { MaterialButton, MaterialIconButton, MaterialSwitch, MaterialTextField } from "@/utils/material";
|
||||
import styles from "@/pages/chat/css/changePasswordDialog.module.scss";
|
||||
|
||||
@@ -29,7 +29,7 @@ export default function ChangePasswordDialog({ isOpen, onOpenChange }: DialogPro
|
||||
if (!current || !next || next !== confirm) return;
|
||||
setBusy(true);
|
||||
try {
|
||||
await changePassword(user.authToken, user.currentUser?.username, current, next, logoutAll);
|
||||
await api.user.auth.changePassword(user.authToken, user.currentUser?.username, current, next, logoutAll);
|
||||
setCurrent("");
|
||||
setNext("");
|
||||
setConfirm("");
|
||||
|
||||
@@ -2,7 +2,8 @@ import { useState, useEffect } from "react";
|
||||
import { useImmer } from "use-immer";
|
||||
import { MaterialList, MaterialListItem, MaterialButton, MaterialCircularProgress } from "@/utils/material";
|
||||
import { useUserStore } from "@/state/user";
|
||||
import { listDevices, revokeDevice, logoutAllOtherDevices, type DeviceInfo } from "@/core/api/account/devices";
|
||||
import api from "@/core/api";
|
||||
import type { DeviceInfo } from "@/core/api/user/devices";
|
||||
import { confirm } from "mdui/functions/confirm";
|
||||
import styles from "@/pages/chat/css/settings-dialog.module.scss";
|
||||
|
||||
@@ -24,7 +25,7 @@ export function DevicesPanel() {
|
||||
|
||||
setDevicesLoading(true);
|
||||
try {
|
||||
const deviceList = await listDevices(authToken);
|
||||
const deviceList = await api.user.devices.list(authToken);
|
||||
updateDevices(deviceList);
|
||||
} catch (error) {
|
||||
console.error("Failed to load devices:", error);
|
||||
@@ -48,7 +49,7 @@ export function DevicesPanel() {
|
||||
draft.add(sessionId);
|
||||
});
|
||||
|
||||
await revokeDevice(authToken, sessionId);
|
||||
await api.user.devices.revoke(authToken, sessionId);
|
||||
await loadDevices();
|
||||
} catch (error) {
|
||||
if (error !== "cancelled") {
|
||||
@@ -72,7 +73,7 @@ export function DevicesPanel() {
|
||||
cancelText: "Cancel"
|
||||
});
|
||||
|
||||
await logoutAllOtherDevices(authToken);
|
||||
await api.user.devices.revokeAll(authToken);
|
||||
await loadDevices();
|
||||
} catch (error) {
|
||||
if (error !== "cancelled") {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { MaterialList, MaterialListItem, MaterialSwitch, type MDUISwitch } from
|
||||
import { useUserStore } from "@/state/user";
|
||||
import { initialize, subscribe, unsubscribe, isSupported } from "@/core/push-notifications/push-notifications";
|
||||
import { isElectron } from "@/core/electron/electron";
|
||||
import { unsubscribeFromPush } from "@/core/api/push";
|
||||
import api from "@/core/api";
|
||||
import styles from "@/pages/chat/css/settings-dialog.module.scss";
|
||||
|
||||
export function NotificationsPanel() {
|
||||
@@ -73,7 +73,7 @@ export function NotificationsPanel() {
|
||||
}
|
||||
|
||||
// Then unsubscribe from server
|
||||
await unsubscribeFromPush(authToken);
|
||||
await api.push.subscription.unsubscribe(authToken);
|
||||
|
||||
// After unsubscribing, permission is still granted but we're not subscribed
|
||||
// So we keep the state as disabled (false)
|
||||
|
||||
@@ -5,12 +5,11 @@ import Quote from "@/core/components/Quote";
|
||||
import { parse } from "marked";
|
||||
import { escape as escapeHtml } from "he";
|
||||
import { useEffect, useState, useRef, useMemo } from "react";
|
||||
import { getCurrentKeys, getAuthHeaders } from "@/core/api/account";
|
||||
import api from "@/core/api";
|
||||
import { ecdhSharedSecret, deriveWrappingKey } from "@/utils/crypto/asymmetric";
|
||||
import { importAesGcmKey, aesGcmDecrypt } from "@/utils/crypto/symmetric";
|
||||
import { useUserStore } from "@/state/user";
|
||||
import { useProfileStore } from "@/state/profile";
|
||||
import { fetchUserProfileById, fetchUserProfile } from "@/core/api/account/profile";
|
||||
import { StatusBadge } from "@/core/components/StatusBadge";
|
||||
import { ub64 } from "@/utils/utils";
|
||||
import { useImmer } from "use-immer";
|
||||
@@ -223,14 +222,14 @@ export function Message({ message, isAuthor, onContextMenu, onReactionClick, isD
|
||||
// no-op decrypt indicator removed from UI
|
||||
// Fetch encrypted file
|
||||
const response = await fetch(file.path, {
|
||||
headers: getAuthHeaders(user.authToken!)
|
||||
headers: api.user.auth.getAuthHeaders(user.authToken!)
|
||||
});
|
||||
if (!response.ok) throw new Error("Failed to fetch file");
|
||||
|
||||
const encryptedData = await response.arrayBuffer();
|
||||
|
||||
// Get current user's keys
|
||||
const keys = getCurrentKeys();
|
||||
const keys = api.user.auth.getCurrentKeys();
|
||||
if (!keys) throw new Error("Keys not initialized");
|
||||
|
||||
// Derive shared secret with the recipient's public key
|
||||
@@ -343,7 +342,7 @@ export function Message({ message, isAuthor, onContextMenu, onReactionClick, isD
|
||||
|
||||
// Fetch with credentials/headers when not a blob URL
|
||||
const response = await fetch(src, {
|
||||
headers: user.authToken ? getAuthHeaders(user.authToken) : undefined,
|
||||
headers: user.authToken ? api.user.auth.getAuthHeaders(user.authToken) : undefined,
|
||||
credentials: "include"
|
||||
});
|
||||
if (!response.ok) throw new Error("Failed to download image");
|
||||
@@ -381,7 +380,7 @@ export function Message({ message, isAuthor, onContextMenu, onReactionClick, isD
|
||||
|
||||
// If not decrypted or public file, fetch with credentials/headers
|
||||
const response = await fetch(file.path, {
|
||||
headers: user.authToken ? getAuthHeaders(user.authToken) : undefined,
|
||||
headers: user.authToken ? api.user.auth.getAuthHeaders(user.authToken) : undefined,
|
||||
credentials: "include"
|
||||
});
|
||||
if (!response.ok) throw new Error("Failed to download file");
|
||||
@@ -405,7 +404,7 @@ export function Message({ message, isAuthor, onContextMenu, onReactionClick, isD
|
||||
if (!user.authToken || !message.user_id) return;
|
||||
|
||||
try {
|
||||
const userProfile = await fetchUserProfileById(user.authToken, message.user_id);
|
||||
const userProfile = await api.user.profile.fetchById(user.authToken, message.user_id);
|
||||
if (userProfile) {
|
||||
setProfileDialog({
|
||||
...userProfile,
|
||||
@@ -434,9 +433,9 @@ export function Message({ message, isAuthor, onContextMenu, onReactionClick, isD
|
||||
let userProfile;
|
||||
|
||||
if (profileLink.userId) {
|
||||
userProfile = await fetchUserProfileById(user.authToken, profileLink.userId);
|
||||
} else if (profileLink.username) {
|
||||
userProfile = await fetchUserProfile(user.authToken, profileLink.username);
|
||||
userProfile = await api.user.profile.fetchById(user.authToken, profileLink.userId);
|
||||
} else if (profileLink.username) {
|
||||
userProfile = await api.user.profile.fetchByUsername(user.authToken, profileLink.username);
|
||||
}
|
||||
|
||||
if (userProfile) {
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
import { MessagePanel } from "./MessagePanel";
|
||||
import {
|
||||
fetchDMHistory,
|
||||
decryptDm,
|
||||
sendDMViaWebSocket,
|
||||
sendDmWithFiles,
|
||||
editDmEnvelope,
|
||||
deleteDmEnvelope
|
||||
} from "@/core/api/dm";
|
||||
import { fetchUserProfileById } from "@/core/api/account/profile";
|
||||
import api from "@/core/api";
|
||||
import type { DmEncryptedJSON, DmEnvelope, DMWebSocketMessage, EncryptedMessageJson, Message } from "@/core/types";
|
||||
import type { UserState, ProfileDialogData } from "@/state/types";
|
||||
import { formatDMUsername } from "@/pages/chat/hooks/useDM";
|
||||
@@ -63,7 +55,7 @@ export class DMPanel extends MessagePanel {
|
||||
}
|
||||
|
||||
private async parseTextPayload(env: DmEnvelope, decryptedMessages: Message[]) {
|
||||
const plaintext = await decryptDm(env, this.dmData!.publicKey);
|
||||
const plaintext = await api.chats.dm.decrypt(env, this.dmData!.publicKey);
|
||||
const username = formatDMUsername(
|
||||
env.senderId,
|
||||
env.recipientId,
|
||||
@@ -111,7 +103,7 @@ export class DMPanel extends MessagePanel {
|
||||
|
||||
this.setLoading(true);
|
||||
try {
|
||||
const messages = await fetchDMHistory(this.dmData.userId, this.currentUser.authToken, 50);
|
||||
const { messages } = await api.chats.dm.fetchMessages(this.dmData.userId, this.currentUser.authToken, 50);
|
||||
const decryptedMessages: Message[] = [];
|
||||
let maxIncomingId = 0;
|
||||
|
||||
@@ -157,14 +149,14 @@ export class DMPanel extends MessagePanel {
|
||||
const json = JSON.stringify(payload);
|
||||
|
||||
if (files.length === 0) {
|
||||
await sendDMViaWebSocket(
|
||||
await api.chats.dm.send(
|
||||
this.dmData.userId,
|
||||
this.dmData.publicKey,
|
||||
json,
|
||||
this.currentUser.authToken
|
||||
);
|
||||
} else {
|
||||
await sendDmWithFiles(
|
||||
await api.chats.dm.sendWithFiles(
|
||||
this.dmData.userId,
|
||||
this.dmData.publicKey,
|
||||
json,
|
||||
@@ -228,7 +220,7 @@ export class DMPanel extends MessagePanel {
|
||||
const { id, iv, ciphertext, salt, iv2, wrappedMk } = response.data;
|
||||
try {
|
||||
// Decrypt new content in-place
|
||||
const plaintext = await decryptDm(
|
||||
const plaintext = await api.chats.dm.decrypt(
|
||||
{
|
||||
id,
|
||||
senderId: 0,
|
||||
@@ -330,7 +322,7 @@ export class DMPanel extends MessagePanel {
|
||||
this.deleteMessageImmediately(messageId);
|
||||
|
||||
// Fire and forget server deletion; UI already updated
|
||||
await deleteDmEnvelope(messageId, this.dmData.userId, this.currentUser.authToken);
|
||||
await api.chats.dm.deleteMessage(messageId, this.dmData.userId, this.currentUser.authToken);
|
||||
}
|
||||
|
||||
async handleEditMessage(messageId: number, content: string): Promise<void> {
|
||||
@@ -345,7 +337,7 @@ export class DMPanel extends MessagePanel {
|
||||
reply_to_id: msg?.reply_to?.id ?? undefined
|
||||
}
|
||||
};
|
||||
editDmEnvelope(messageId, this.dmData.publicKey, JSON.stringify(payload), this.currentUser.authToken).catch((e) => {
|
||||
api.chats.dm.edit(messageId, this.dmData.publicKey, JSON.stringify(payload), this.currentUser.authToken).catch((e) => {
|
||||
console.error("Failed to edit DM:", e);
|
||||
});
|
||||
}
|
||||
@@ -354,7 +346,7 @@ export class DMPanel extends MessagePanel {
|
||||
if (!this.dmData || !this.currentUser.authToken) return null;
|
||||
|
||||
try {
|
||||
const userProfile = await fetchUserProfileById(this.currentUser.authToken, this.dmData.userId);
|
||||
const userProfile = await api.user.profile.fetchById(this.currentUser.authToken, this.dmData.userId);
|
||||
if (!userProfile) return null;
|
||||
|
||||
return {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { MessagePanel } from "./MessagePanel";
|
||||
import { request } from "@/core/websocket";
|
||||
import type { ChatWebSocketMessage, Message, ReactionUpdateWebSocketMessage } from "@/core/types";
|
||||
import type { UserState, ProfileDialogData } from "@/state/types";
|
||||
import { fetchMessages, sendMessage, sendMessageWithFiles } from "@/core/api/messaging";
|
||||
import api from "@/core/api";
|
||||
|
||||
export class PublicChatPanel extends MessagePanel {
|
||||
private messagesLoaded: boolean = false;
|
||||
@@ -41,7 +41,7 @@ export class PublicChatPanel extends MessagePanel {
|
||||
|
||||
this.setLoading(true);
|
||||
try {
|
||||
const messages = await fetchMessages(this.currentUser.authToken);
|
||||
const { messages } = await api.chats.general.fetchMessages(this.currentUser.authToken);
|
||||
if (messages && messages.length > 0) {
|
||||
this.clearMessages();
|
||||
messages.forEach((msg: Message) => {
|
||||
@@ -61,9 +61,9 @@ export class PublicChatPanel extends MessagePanel {
|
||||
|
||||
try {
|
||||
if (files.length === 0) {
|
||||
await sendMessage(content, replyToId ?? null, this.currentUser.authToken);
|
||||
await api.chats.general.send(content, replyToId ?? null, this.currentUser.authToken);
|
||||
} else {
|
||||
await sendMessageWithFiles(content, replyToId ?? null, files, this.currentUser.authToken);
|
||||
await api.chats.general.sendWithFiles(content, replyToId ?? null, files, this.currentUser.authToken);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error sending message:", error);
|
||||
|
||||
Reference in New Issue
Block a user