mirror of
https://github.com/fromchat-messenger/web.git
synced 2026-09-23 03:25:07 +03:00
Create new Material components, redesign the chat UI, add security settings
This commit is contained in:
@@ -3,7 +3,7 @@ import { generateX25519KeyPair } from "@/utils/crypto/asymmetric";
|
||||
import { encodeBlob, encryptBackupWithPassword, decryptBackupWithPassword, decodeBlob } from "@/utils/crypto/backup";
|
||||
import { b64, ub64 } from "@/utils/utils";
|
||||
import { API_BASE_URL } from "@/core/config";
|
||||
import { importPassword, hkdfExtractAndExpand } from "@/utils/crypto/kdf";
|
||||
import { hkdfExtractAndExpand } from "@/utils/crypto/kdf";
|
||||
|
||||
/**
|
||||
* Generates authentication headers for API requests
|
||||
@@ -151,7 +151,6 @@ export function getAuthToken(): string | null {
|
||||
* Uses PBKDF2 (via WebCrypto) + HKDF to produce a stable 32-byte key, then base64.
|
||||
*/
|
||||
export async function deriveAuthSecret(username: string, password: string): Promise<string> {
|
||||
const key = await importPassword(password);
|
||||
// Use per-user salt derived from username; in future we can fetch a server-provided salt
|
||||
const salt = new TextEncoder().encode(`fromchat.user:${username}`);
|
||||
// Derive 32 bytes using HKDF; PBKDF2 already used within importPassword
|
||||
|
||||
@@ -3,6 +3,7 @@ import { getAuthHeaders } from "@/core/api/authApi";
|
||||
|
||||
export interface DeviceInfo {
|
||||
session_id: string;
|
||||
device_name?: string;
|
||||
device_type?: string;
|
||||
os_name?: string;
|
||||
os_version?: string;
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import type { TextField } from "mdui/components/text-field";
|
||||
|
||||
interface TextFieldProps extends React.ComponentPropsWithoutRef<"mdui-text-field"> {
|
||||
ref?: React.Ref<TextField>
|
||||
}
|
||||
|
||||
export function MaterialTextField({ ref, ...props }: TextFieldProps) {
|
||||
return <mdui-text-field autocomplete="off" ref={ref as React.Ref<HTMLElement>} {...props} />
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import "./css/searchBar.scss";
|
||||
import { MaterialIcon } from "@/utils/material";
|
||||
|
||||
interface SearchBarProps {
|
||||
placeholder: string;
|
||||
@@ -57,9 +58,9 @@ export default function SearchBar({
|
||||
function renderIcon(icon: string | React.ReactNode | undefined, defaultIcon?: string) {
|
||||
if (icon === null) return null;
|
||||
if (!icon) {
|
||||
return defaultIcon ? <mdui-icon name={defaultIcon}></mdui-icon> : null;
|
||||
return defaultIcon ? <MaterialIcon name={defaultIcon} /> : null;
|
||||
} else if (typeof icon === 'string') {
|
||||
return <mdui-icon name={icon}></mdui-icon>;
|
||||
return <MaterialIcon name={icon} />;
|
||||
} else {
|
||||
return icon;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { checkUserSimilarity } from "@/core/api/profileApi";
|
||||
import { useAppState } from "@/pages/chat/state";
|
||||
import { MaterialIcon } from "@/utils/material";
|
||||
|
||||
interface StatusBadgeProps {
|
||||
verified: boolean;
|
||||
@@ -33,7 +34,7 @@ export function StatusBadge({ verified, userId, size = "small" }: StatusBadgePro
|
||||
if (verified) {
|
||||
return (
|
||||
<span className={`${className} verified`} title="Подтверждённый аккаунт">
|
||||
<mdui-icon name="verified--filled" />
|
||||
<MaterialIcon name="verified--filled" />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@@ -41,7 +42,7 @@ export function StatusBadge({ verified, userId, size = "small" }: StatusBadgePro
|
||||
if (isSimilarToVerified) {
|
||||
return (
|
||||
<span className={`${className} warning`} title="Похож на подтверждённый аккаунт">
|
||||
<mdui-icon name="warning" />
|
||||
<MaterialIcon name="warning--filled" />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState } from "react";
|
||||
import { verifyUser } from "@/core/api/profileApi";
|
||||
import { useAppState } from "@/pages/chat/state";
|
||||
import { MaterialButton } from "@/utils/material";
|
||||
|
||||
interface VerifyButtonProps {
|
||||
userId: number;
|
||||
@@ -34,13 +35,13 @@ export function VerifyButton({ userId, verified, onVerificationChange }: VerifyB
|
||||
}
|
||||
|
||||
return (
|
||||
<mdui-button
|
||||
<MaterialButton
|
||||
variant="filled"
|
||||
loading={isVerifying}
|
||||
onClick={handleVerifyToggle}
|
||||
title={verified ? "Снять подтверждение" : "Подтвердить аккаунт"}
|
||||
>
|
||||
{verified ? "Отменить подтверждение" : "Подтвердить"}
|
||||
</mdui-button>
|
||||
</MaterialButton>
|
||||
);
|
||||
}
|
||||
Vendored
+8
-1
@@ -630,4 +630,11 @@ export interface StopDmTypingRequest extends WebSocketMessage {
|
||||
data: {
|
||||
recipientId: number;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -------------
|
||||
// Utility types
|
||||
// -------------
|
||||
|
||||
export type Override<TBase, TExt> = Omit<TBase, keyof TExt> & TExt;
|
||||
Reference in New Issue
Block a user