From a24270b37ae47b099a518dfd4a19ff6cf8dd80fa Mon Sep 17 00:00:00 2001
From: denis0001-dev
Date: Thu, 2 Oct 2025 18:18:08 +0300
Subject: [PATCH 1/5] Restructure code
---
.gitignore | 4 +++-
frontend/src/{ui => }/App.tsx | 18 +++++++++---------
frontend/src/main.tsx | 8 ++++----
frontend/src/{ => pages/app}/api/dmApi.ts | 0
.../src/{ => pages/app}/api/profileApi.ts | 0
frontend/src/{ => pages/app}/auth/api.ts | 0
frontend/src/{ => pages/app}/auth/crypto.ts | 0
frontend/src/{ => pages/app}/core/config.ts | 0
frontend/src/{ => pages/app}/core/init.ts | 0
frontend/src/{ => pages/app}/core/types.d.ts | 0
.../src/{ => pages/app}/core/websocket.ts | 0
.../src/{ => pages/app}/electron/electron.ts | 0
.../{ => pages/app}/resources/css/_auth.scss | 0
.../{ => pages/app}/resources/css/_chat.scss | 0
.../app}/resources/css/_download-app.scss | 0
.../app}/resources/css/_electron.scss | 0
.../app}/resources/css/_panelchat.scss | 0
.../app}/resources/css/_profile.scss | 0
.../app}/resources/css/_settings.scss | 0
.../resources/css/common/_animations.scss | 0
.../app}/resources/css/common/_colors.scss | 0
.../resources/css/common/_components.scss | 0
.../app}/resources/css/common/_material.scss | 0
.../app}/resources/css/dialogs/_reply.scss | 0
.../css/lib/fonts/material-symbols.scss | 0
.../css/lib/fonts/material-symbols.woff2 | Bin
.../resources/css/lib/fonts/montserrat.scss | 0
.../JTUSjIg1_i6t8kCHKm459W1hyyTh89ZNpQ.woff2 | Bin
.../JTUSjIg1_i6t8kCHKm459WRhyyTh89ZNpQ.woff2 | Bin
.../JTUSjIg1_i6t8kCHKm459WZhyyTh89ZNpQ.woff2 | Bin
.../JTUSjIg1_i6t8kCHKm459WdhyyTh89ZNpQ.woff2 | Bin
.../JTUSjIg1_i6t8kCHKm459WlhyyTh89Y.woff2 | Bin
.../{ => pages/app}/resources/css/style.scss | 0
.../app}/resources/images/default-avatar.png | Bin
.../{ => pages/app}/resources/images/logo.png | Bin
.../app}/service-worker/service-worker.ts | 0
.../{ => pages/app}/ui/components/Alerts.tsx | 0
.../{ => pages/app}/ui/components/Auth.tsx | 0
.../app}/ui/components/Electron.tsx | 0
.../app}/ui/components/chat/BottomAppBar.tsx | 0
.../app}/ui/components/chat/ChatHeader.tsx | 0
.../ui/components/chat/ChatInputWrapper.tsx | 0
.../ui/components/chat/ChatMainHeader.tsx | 0
.../app}/ui/components/chat/ChatMessages.tsx | 0
.../app}/ui/components/chat/ChatTabs.tsx | 0
.../app}/ui/components/chat/DMUsersList.tsx | 0
.../app}/ui/components/chat/LeftPanel.tsx | 0
.../app}/ui/components/chat/Message.tsx | 0
.../ui/components/chat/MessageContextMenu.tsx | 0
.../components/chat/MessagePanelRenderer.tsx | 0
.../ui/components/chat/ReplyMessageDialog.tsx | 0
.../app}/ui/components/chat/RightPanel.tsx | 0
.../ui/components/chat/UserProfileDialog.tsx | 0
.../app}/ui/components/core/Dialog.tsx | 0
.../app}/ui/components/core/Quote.tsx | 0
.../app}/ui/components/core/RichTextArea.tsx | 0
.../app}/ui/components/core/TextField.tsx | 0
.../core/animations/AnimatedHeight.tsx | 0
.../core/animations/AnimatedOpacity.tsx | 0
.../ui/components/core/animations/types.d.ts | 0
.../ui/components/profile/CropperDialog.tsx | 0
.../ui/components/profile/ImageCropper.tsx | 0
.../ui/components/profile/ProfileDialog.tsx | 0
.../ui/components/settings/SettingsDialog.tsx | 0
.../src/{ => pages/app}/ui/hooks/useChat.ts | 0
.../app}/ui/hooks/useCombinedRefs.ts | 0
.../src/{ => pages/app}/ui/hooks/useDM.ts | 0
.../{ => pages/app}/ui/hooks/useProfile.ts | 0
.../{ => pages/app}/ui/hooks/useWindowSize.ts | 0
.../src/{ => pages/app}/ui/panels/DMPanel.ts | 0
.../{ => pages/app}/ui/panels/MessagePanel.ts | 0
.../app}/ui/panels/PublicChatPanel.ts | 0
.../{ => pages/app}/ui/screen/ChatScreen.tsx | 0
.../app}/ui/screen/DownloadAppScreen.tsx | 0
.../{ => pages/app}/ui/screen/LoginScreen.tsx | 0
.../app}/ui/screen/RegisterScreen.tsx | 0
frontend/src/{ => pages/app}/ui/state.ts | 0
.../app}/utils/crypto/asymmetric.ts | 2 +-
.../{ => pages/app}/utils/crypto/backup.ts | 0
.../src/{ => pages/app}/utils/crypto/kdf.ts | 0
.../{ => pages/app}/utils/crypto/symmetric.ts | 0
.../src/{ => pages/app}/utils/material.ts | 0
.../src/{ => pages/app}/utils/notification.ts | 0
.../app}/utils/push-notifications.ts | 0
frontend/src/{ => pages/app}/utils/utils.ts | 0
85 files changed, 17 insertions(+), 15 deletions(-)
rename frontend/src/{ui => }/App.tsx (61%)
rename frontend/src/{ => pages/app}/api/dmApi.ts (100%)
rename frontend/src/{ => pages/app}/api/profileApi.ts (100%)
rename frontend/src/{ => pages/app}/auth/api.ts (100%)
rename frontend/src/{ => pages/app}/auth/crypto.ts (100%)
rename frontend/src/{ => pages/app}/core/config.ts (100%)
rename frontend/src/{ => pages/app}/core/init.ts (100%)
rename frontend/src/{ => pages/app}/core/types.d.ts (100%)
rename frontend/src/{ => pages/app}/core/websocket.ts (100%)
rename frontend/src/{ => pages/app}/electron/electron.ts (100%)
rename frontend/src/{ => pages/app}/resources/css/_auth.scss (100%)
rename frontend/src/{ => pages/app}/resources/css/_chat.scss (100%)
rename frontend/src/{ => pages/app}/resources/css/_download-app.scss (100%)
rename frontend/src/{ => pages/app}/resources/css/_electron.scss (100%)
rename frontend/src/{ => pages/app}/resources/css/_panelchat.scss (100%)
rename frontend/src/{ => pages/app}/resources/css/_profile.scss (100%)
rename frontend/src/{ => pages/app}/resources/css/_settings.scss (100%)
rename frontend/src/{ => pages/app}/resources/css/common/_animations.scss (100%)
rename frontend/src/{ => pages/app}/resources/css/common/_colors.scss (100%)
rename frontend/src/{ => pages/app}/resources/css/common/_components.scss (100%)
rename frontend/src/{ => pages/app}/resources/css/common/_material.scss (100%)
rename frontend/src/{ => pages/app}/resources/css/dialogs/_reply.scss (100%)
rename frontend/src/{ => pages/app}/resources/css/lib/fonts/material-symbols.scss (100%)
rename frontend/src/{ => pages/app}/resources/css/lib/fonts/material-symbols.woff2 (100%)
rename frontend/src/{ => pages/app}/resources/css/lib/fonts/montserrat.scss (100%)
rename frontend/src/{ => pages/app}/resources/css/lib/fonts/montserrat/JTUSjIg1_i6t8kCHKm459W1hyyTh89ZNpQ.woff2 (100%)
rename frontend/src/{ => pages/app}/resources/css/lib/fonts/montserrat/JTUSjIg1_i6t8kCHKm459WRhyyTh89ZNpQ.woff2 (100%)
rename frontend/src/{ => pages/app}/resources/css/lib/fonts/montserrat/JTUSjIg1_i6t8kCHKm459WZhyyTh89ZNpQ.woff2 (100%)
rename frontend/src/{ => pages/app}/resources/css/lib/fonts/montserrat/JTUSjIg1_i6t8kCHKm459WdhyyTh89ZNpQ.woff2 (100%)
rename frontend/src/{ => pages/app}/resources/css/lib/fonts/montserrat/JTUSjIg1_i6t8kCHKm459WlhyyTh89Y.woff2 (100%)
rename frontend/src/{ => pages/app}/resources/css/style.scss (100%)
rename frontend/src/{ => pages/app}/resources/images/default-avatar.png (100%)
rename frontend/src/{ => pages/app}/resources/images/logo.png (100%)
rename frontend/src/{ => pages/app}/service-worker/service-worker.ts (100%)
rename frontend/src/{ => pages/app}/ui/components/Alerts.tsx (100%)
rename frontend/src/{ => pages/app}/ui/components/Auth.tsx (100%)
rename frontend/src/{ => pages/app}/ui/components/Electron.tsx (100%)
rename frontend/src/{ => pages/app}/ui/components/chat/BottomAppBar.tsx (100%)
rename frontend/src/{ => pages/app}/ui/components/chat/ChatHeader.tsx (100%)
rename frontend/src/{ => pages/app}/ui/components/chat/ChatInputWrapper.tsx (100%)
rename frontend/src/{ => pages/app}/ui/components/chat/ChatMainHeader.tsx (100%)
rename frontend/src/{ => pages/app}/ui/components/chat/ChatMessages.tsx (100%)
rename frontend/src/{ => pages/app}/ui/components/chat/ChatTabs.tsx (100%)
rename frontend/src/{ => pages/app}/ui/components/chat/DMUsersList.tsx (100%)
rename frontend/src/{ => pages/app}/ui/components/chat/LeftPanel.tsx (100%)
rename frontend/src/{ => pages/app}/ui/components/chat/Message.tsx (100%)
rename frontend/src/{ => pages/app}/ui/components/chat/MessageContextMenu.tsx (100%)
rename frontend/src/{ => pages/app}/ui/components/chat/MessagePanelRenderer.tsx (100%)
rename frontend/src/{ => pages/app}/ui/components/chat/ReplyMessageDialog.tsx (100%)
rename frontend/src/{ => pages/app}/ui/components/chat/RightPanel.tsx (100%)
rename frontend/src/{ => pages/app}/ui/components/chat/UserProfileDialog.tsx (100%)
rename frontend/src/{ => pages/app}/ui/components/core/Dialog.tsx (100%)
rename frontend/src/{ => pages/app}/ui/components/core/Quote.tsx (100%)
rename frontend/src/{ => pages/app}/ui/components/core/RichTextArea.tsx (100%)
rename frontend/src/{ => pages/app}/ui/components/core/TextField.tsx (100%)
rename frontend/src/{ => pages/app}/ui/components/core/animations/AnimatedHeight.tsx (100%)
rename frontend/src/{ => pages/app}/ui/components/core/animations/AnimatedOpacity.tsx (100%)
rename frontend/src/{ => pages/app}/ui/components/core/animations/types.d.ts (100%)
rename frontend/src/{ => pages/app}/ui/components/profile/CropperDialog.tsx (100%)
rename frontend/src/{ => pages/app}/ui/components/profile/ImageCropper.tsx (100%)
rename frontend/src/{ => pages/app}/ui/components/profile/ProfileDialog.tsx (100%)
rename frontend/src/{ => pages/app}/ui/components/settings/SettingsDialog.tsx (100%)
rename frontend/src/{ => pages/app}/ui/hooks/useChat.ts (100%)
rename frontend/src/{ => pages/app}/ui/hooks/useCombinedRefs.ts (100%)
rename frontend/src/{ => pages/app}/ui/hooks/useDM.ts (100%)
rename frontend/src/{ => pages/app}/ui/hooks/useProfile.ts (100%)
rename frontend/src/{ => pages/app}/ui/hooks/useWindowSize.ts (100%)
rename frontend/src/{ => pages/app}/ui/panels/DMPanel.ts (100%)
rename frontend/src/{ => pages/app}/ui/panels/MessagePanel.ts (100%)
rename frontend/src/{ => pages/app}/ui/panels/PublicChatPanel.ts (100%)
rename frontend/src/{ => pages/app}/ui/screen/ChatScreen.tsx (100%)
rename frontend/src/{ => pages/app}/ui/screen/DownloadAppScreen.tsx (100%)
rename frontend/src/{ => pages/app}/ui/screen/LoginScreen.tsx (100%)
rename frontend/src/{ => pages/app}/ui/screen/RegisterScreen.tsx (100%)
rename frontend/src/{ => pages/app}/ui/state.ts (100%)
rename frontend/src/{ => pages/app}/utils/crypto/asymmetric.ts (93%)
rename frontend/src/{ => pages/app}/utils/crypto/backup.ts (100%)
rename frontend/src/{ => pages/app}/utils/crypto/kdf.ts (100%)
rename frontend/src/{ => pages/app}/utils/crypto/symmetric.ts (100%)
rename frontend/src/{ => pages/app}/utils/material.ts (100%)
rename frontend/src/{ => pages/app}/utils/notification.ts (100%)
rename frontend/src/{ => pages/app}/utils/push-notifications.ts (100%)
rename frontend/src/{ => pages/app}/utils/utils.ts (100%)
diff --git a/.gitignore b/.gitignore
index decede0..404f860 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,7 +9,8 @@
.LSOverride
# Icon must end with two \r
-Icon
+Icon
+
# Thumbnails
._*
@@ -572,3 +573,4 @@ package-lock.json
backend/alembic/**
!backend/alembic/env.py
!backend/alembic/script.py.mako
+!frontend/src/pages/app/resources/css/lib
\ No newline at end of file
diff --git a/frontend/src/ui/App.tsx b/frontend/src/App.tsx
similarity index 61%
rename from frontend/src/ui/App.tsx
rename to frontend/src/App.tsx
index 2712a2e..d403a77 100644
--- a/frontend/src/ui/App.tsx
+++ b/frontend/src/App.tsx
@@ -1,12 +1,12 @@
-import { MINIMUM_WIDTH } from "../core/config";
-import { isElectron } from "../electron/electron";
-import { ElectronTitleBar } from "./components/Electron";
-import useWindowSize from "./hooks/useWindowSize";
-import ChatScreen from "./screen/ChatScreen";
-import DownloadAppScreen from "./screen/DownloadAppScreen";
-import LoginScreen from "./screen/LoginScreen";
-import RegisterScreen from "./screen/RegisterScreen";
-import { useAppState } from "./state";
+import { MINIMUM_WIDTH } from "./pages/app/core/config";
+import { isElectron } from "./pages/app/electron/electron";
+import { ElectronTitleBar } from "./pages/app/ui/components/Electron";
+import useWindowSize from "./pages/app/ui/hooks/useWindowSize";
+import ChatScreen from "./pages/app/ui/screen/ChatScreen";
+import DownloadAppScreen from "./pages/app/ui/screen/DownloadAppScreen";
+import LoginScreen from "./pages/app/ui/screen/LoginScreen";
+import RegisterScreen from "./pages/app/ui/screen/RegisterScreen";
+import { useAppState } from "./pages/app/ui/state";
import { useEffect } from "react";
export default function App() {
diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx
index b923780..572bfd4 100644
--- a/frontend/src/main.tsx
+++ b/frontend/src/main.tsx
@@ -8,11 +8,11 @@
import './resources/css/style.scss';
import "mdui/mdui.css";
-import "./utils/material";
-import "./core/init";
-import "./electron/electron";
+import "./pages/app/utils/material";
+import "./pages/app/core/init";
+import "./pages/app/electron/electron";
import { createRoot } from 'react-dom/client';
-import App from './ui/App';
+import App from './App';
import { StrictMode } from 'react';
// Initialize React app
diff --git a/frontend/src/api/dmApi.ts b/frontend/src/pages/app/api/dmApi.ts
similarity index 100%
rename from frontend/src/api/dmApi.ts
rename to frontend/src/pages/app/api/dmApi.ts
diff --git a/frontend/src/api/profileApi.ts b/frontend/src/pages/app/api/profileApi.ts
similarity index 100%
rename from frontend/src/api/profileApi.ts
rename to frontend/src/pages/app/api/profileApi.ts
diff --git a/frontend/src/auth/api.ts b/frontend/src/pages/app/auth/api.ts
similarity index 100%
rename from frontend/src/auth/api.ts
rename to frontend/src/pages/app/auth/api.ts
diff --git a/frontend/src/auth/crypto.ts b/frontend/src/pages/app/auth/crypto.ts
similarity index 100%
rename from frontend/src/auth/crypto.ts
rename to frontend/src/pages/app/auth/crypto.ts
diff --git a/frontend/src/core/config.ts b/frontend/src/pages/app/core/config.ts
similarity index 100%
rename from frontend/src/core/config.ts
rename to frontend/src/pages/app/core/config.ts
diff --git a/frontend/src/core/init.ts b/frontend/src/pages/app/core/init.ts
similarity index 100%
rename from frontend/src/core/init.ts
rename to frontend/src/pages/app/core/init.ts
diff --git a/frontend/src/core/types.d.ts b/frontend/src/pages/app/core/types.d.ts
similarity index 100%
rename from frontend/src/core/types.d.ts
rename to frontend/src/pages/app/core/types.d.ts
diff --git a/frontend/src/core/websocket.ts b/frontend/src/pages/app/core/websocket.ts
similarity index 100%
rename from frontend/src/core/websocket.ts
rename to frontend/src/pages/app/core/websocket.ts
diff --git a/frontend/src/electron/electron.ts b/frontend/src/pages/app/electron/electron.ts
similarity index 100%
rename from frontend/src/electron/electron.ts
rename to frontend/src/pages/app/electron/electron.ts
diff --git a/frontend/src/resources/css/_auth.scss b/frontend/src/pages/app/resources/css/_auth.scss
similarity index 100%
rename from frontend/src/resources/css/_auth.scss
rename to frontend/src/pages/app/resources/css/_auth.scss
diff --git a/frontend/src/resources/css/_chat.scss b/frontend/src/pages/app/resources/css/_chat.scss
similarity index 100%
rename from frontend/src/resources/css/_chat.scss
rename to frontend/src/pages/app/resources/css/_chat.scss
diff --git a/frontend/src/resources/css/_download-app.scss b/frontend/src/pages/app/resources/css/_download-app.scss
similarity index 100%
rename from frontend/src/resources/css/_download-app.scss
rename to frontend/src/pages/app/resources/css/_download-app.scss
diff --git a/frontend/src/resources/css/_electron.scss b/frontend/src/pages/app/resources/css/_electron.scss
similarity index 100%
rename from frontend/src/resources/css/_electron.scss
rename to frontend/src/pages/app/resources/css/_electron.scss
diff --git a/frontend/src/resources/css/_panelchat.scss b/frontend/src/pages/app/resources/css/_panelchat.scss
similarity index 100%
rename from frontend/src/resources/css/_panelchat.scss
rename to frontend/src/pages/app/resources/css/_panelchat.scss
diff --git a/frontend/src/resources/css/_profile.scss b/frontend/src/pages/app/resources/css/_profile.scss
similarity index 100%
rename from frontend/src/resources/css/_profile.scss
rename to frontend/src/pages/app/resources/css/_profile.scss
diff --git a/frontend/src/resources/css/_settings.scss b/frontend/src/pages/app/resources/css/_settings.scss
similarity index 100%
rename from frontend/src/resources/css/_settings.scss
rename to frontend/src/pages/app/resources/css/_settings.scss
diff --git a/frontend/src/resources/css/common/_animations.scss b/frontend/src/pages/app/resources/css/common/_animations.scss
similarity index 100%
rename from frontend/src/resources/css/common/_animations.scss
rename to frontend/src/pages/app/resources/css/common/_animations.scss
diff --git a/frontend/src/resources/css/common/_colors.scss b/frontend/src/pages/app/resources/css/common/_colors.scss
similarity index 100%
rename from frontend/src/resources/css/common/_colors.scss
rename to frontend/src/pages/app/resources/css/common/_colors.scss
diff --git a/frontend/src/resources/css/common/_components.scss b/frontend/src/pages/app/resources/css/common/_components.scss
similarity index 100%
rename from frontend/src/resources/css/common/_components.scss
rename to frontend/src/pages/app/resources/css/common/_components.scss
diff --git a/frontend/src/resources/css/common/_material.scss b/frontend/src/pages/app/resources/css/common/_material.scss
similarity index 100%
rename from frontend/src/resources/css/common/_material.scss
rename to frontend/src/pages/app/resources/css/common/_material.scss
diff --git a/frontend/src/resources/css/dialogs/_reply.scss b/frontend/src/pages/app/resources/css/dialogs/_reply.scss
similarity index 100%
rename from frontend/src/resources/css/dialogs/_reply.scss
rename to frontend/src/pages/app/resources/css/dialogs/_reply.scss
diff --git a/frontend/src/resources/css/lib/fonts/material-symbols.scss b/frontend/src/pages/app/resources/css/lib/fonts/material-symbols.scss
similarity index 100%
rename from frontend/src/resources/css/lib/fonts/material-symbols.scss
rename to frontend/src/pages/app/resources/css/lib/fonts/material-symbols.scss
diff --git a/frontend/src/resources/css/lib/fonts/material-symbols.woff2 b/frontend/src/pages/app/resources/css/lib/fonts/material-symbols.woff2
similarity index 100%
rename from frontend/src/resources/css/lib/fonts/material-symbols.woff2
rename to frontend/src/pages/app/resources/css/lib/fonts/material-symbols.woff2
diff --git a/frontend/src/resources/css/lib/fonts/montserrat.scss b/frontend/src/pages/app/resources/css/lib/fonts/montserrat.scss
similarity index 100%
rename from frontend/src/resources/css/lib/fonts/montserrat.scss
rename to frontend/src/pages/app/resources/css/lib/fonts/montserrat.scss
diff --git a/frontend/src/resources/css/lib/fonts/montserrat/JTUSjIg1_i6t8kCHKm459W1hyyTh89ZNpQ.woff2 b/frontend/src/pages/app/resources/css/lib/fonts/montserrat/JTUSjIg1_i6t8kCHKm459W1hyyTh89ZNpQ.woff2
similarity index 100%
rename from frontend/src/resources/css/lib/fonts/montserrat/JTUSjIg1_i6t8kCHKm459W1hyyTh89ZNpQ.woff2
rename to frontend/src/pages/app/resources/css/lib/fonts/montserrat/JTUSjIg1_i6t8kCHKm459W1hyyTh89ZNpQ.woff2
diff --git a/frontend/src/resources/css/lib/fonts/montserrat/JTUSjIg1_i6t8kCHKm459WRhyyTh89ZNpQ.woff2 b/frontend/src/pages/app/resources/css/lib/fonts/montserrat/JTUSjIg1_i6t8kCHKm459WRhyyTh89ZNpQ.woff2
similarity index 100%
rename from frontend/src/resources/css/lib/fonts/montserrat/JTUSjIg1_i6t8kCHKm459WRhyyTh89ZNpQ.woff2
rename to frontend/src/pages/app/resources/css/lib/fonts/montserrat/JTUSjIg1_i6t8kCHKm459WRhyyTh89ZNpQ.woff2
diff --git a/frontend/src/resources/css/lib/fonts/montserrat/JTUSjIg1_i6t8kCHKm459WZhyyTh89ZNpQ.woff2 b/frontend/src/pages/app/resources/css/lib/fonts/montserrat/JTUSjIg1_i6t8kCHKm459WZhyyTh89ZNpQ.woff2
similarity index 100%
rename from frontend/src/resources/css/lib/fonts/montserrat/JTUSjIg1_i6t8kCHKm459WZhyyTh89ZNpQ.woff2
rename to frontend/src/pages/app/resources/css/lib/fonts/montserrat/JTUSjIg1_i6t8kCHKm459WZhyyTh89ZNpQ.woff2
diff --git a/frontend/src/resources/css/lib/fonts/montserrat/JTUSjIg1_i6t8kCHKm459WdhyyTh89ZNpQ.woff2 b/frontend/src/pages/app/resources/css/lib/fonts/montserrat/JTUSjIg1_i6t8kCHKm459WdhyyTh89ZNpQ.woff2
similarity index 100%
rename from frontend/src/resources/css/lib/fonts/montserrat/JTUSjIg1_i6t8kCHKm459WdhyyTh89ZNpQ.woff2
rename to frontend/src/pages/app/resources/css/lib/fonts/montserrat/JTUSjIg1_i6t8kCHKm459WdhyyTh89ZNpQ.woff2
diff --git a/frontend/src/resources/css/lib/fonts/montserrat/JTUSjIg1_i6t8kCHKm459WlhyyTh89Y.woff2 b/frontend/src/pages/app/resources/css/lib/fonts/montserrat/JTUSjIg1_i6t8kCHKm459WlhyyTh89Y.woff2
similarity index 100%
rename from frontend/src/resources/css/lib/fonts/montserrat/JTUSjIg1_i6t8kCHKm459WlhyyTh89Y.woff2
rename to frontend/src/pages/app/resources/css/lib/fonts/montserrat/JTUSjIg1_i6t8kCHKm459WlhyyTh89Y.woff2
diff --git a/frontend/src/resources/css/style.scss b/frontend/src/pages/app/resources/css/style.scss
similarity index 100%
rename from frontend/src/resources/css/style.scss
rename to frontend/src/pages/app/resources/css/style.scss
diff --git a/frontend/src/resources/images/default-avatar.png b/frontend/src/pages/app/resources/images/default-avatar.png
similarity index 100%
rename from frontend/src/resources/images/default-avatar.png
rename to frontend/src/pages/app/resources/images/default-avatar.png
diff --git a/frontend/src/resources/images/logo.png b/frontend/src/pages/app/resources/images/logo.png
similarity index 100%
rename from frontend/src/resources/images/logo.png
rename to frontend/src/pages/app/resources/images/logo.png
diff --git a/frontend/src/service-worker/service-worker.ts b/frontend/src/pages/app/service-worker/service-worker.ts
similarity index 100%
rename from frontend/src/service-worker/service-worker.ts
rename to frontend/src/pages/app/service-worker/service-worker.ts
diff --git a/frontend/src/ui/components/Alerts.tsx b/frontend/src/pages/app/ui/components/Alerts.tsx
similarity index 100%
rename from frontend/src/ui/components/Alerts.tsx
rename to frontend/src/pages/app/ui/components/Alerts.tsx
diff --git a/frontend/src/ui/components/Auth.tsx b/frontend/src/pages/app/ui/components/Auth.tsx
similarity index 100%
rename from frontend/src/ui/components/Auth.tsx
rename to frontend/src/pages/app/ui/components/Auth.tsx
diff --git a/frontend/src/ui/components/Electron.tsx b/frontend/src/pages/app/ui/components/Electron.tsx
similarity index 100%
rename from frontend/src/ui/components/Electron.tsx
rename to frontend/src/pages/app/ui/components/Electron.tsx
diff --git a/frontend/src/ui/components/chat/BottomAppBar.tsx b/frontend/src/pages/app/ui/components/chat/BottomAppBar.tsx
similarity index 100%
rename from frontend/src/ui/components/chat/BottomAppBar.tsx
rename to frontend/src/pages/app/ui/components/chat/BottomAppBar.tsx
diff --git a/frontend/src/ui/components/chat/ChatHeader.tsx b/frontend/src/pages/app/ui/components/chat/ChatHeader.tsx
similarity index 100%
rename from frontend/src/ui/components/chat/ChatHeader.tsx
rename to frontend/src/pages/app/ui/components/chat/ChatHeader.tsx
diff --git a/frontend/src/ui/components/chat/ChatInputWrapper.tsx b/frontend/src/pages/app/ui/components/chat/ChatInputWrapper.tsx
similarity index 100%
rename from frontend/src/ui/components/chat/ChatInputWrapper.tsx
rename to frontend/src/pages/app/ui/components/chat/ChatInputWrapper.tsx
diff --git a/frontend/src/ui/components/chat/ChatMainHeader.tsx b/frontend/src/pages/app/ui/components/chat/ChatMainHeader.tsx
similarity index 100%
rename from frontend/src/ui/components/chat/ChatMainHeader.tsx
rename to frontend/src/pages/app/ui/components/chat/ChatMainHeader.tsx
diff --git a/frontend/src/ui/components/chat/ChatMessages.tsx b/frontend/src/pages/app/ui/components/chat/ChatMessages.tsx
similarity index 100%
rename from frontend/src/ui/components/chat/ChatMessages.tsx
rename to frontend/src/pages/app/ui/components/chat/ChatMessages.tsx
diff --git a/frontend/src/ui/components/chat/ChatTabs.tsx b/frontend/src/pages/app/ui/components/chat/ChatTabs.tsx
similarity index 100%
rename from frontend/src/ui/components/chat/ChatTabs.tsx
rename to frontend/src/pages/app/ui/components/chat/ChatTabs.tsx
diff --git a/frontend/src/ui/components/chat/DMUsersList.tsx b/frontend/src/pages/app/ui/components/chat/DMUsersList.tsx
similarity index 100%
rename from frontend/src/ui/components/chat/DMUsersList.tsx
rename to frontend/src/pages/app/ui/components/chat/DMUsersList.tsx
diff --git a/frontend/src/ui/components/chat/LeftPanel.tsx b/frontend/src/pages/app/ui/components/chat/LeftPanel.tsx
similarity index 100%
rename from frontend/src/ui/components/chat/LeftPanel.tsx
rename to frontend/src/pages/app/ui/components/chat/LeftPanel.tsx
diff --git a/frontend/src/ui/components/chat/Message.tsx b/frontend/src/pages/app/ui/components/chat/Message.tsx
similarity index 100%
rename from frontend/src/ui/components/chat/Message.tsx
rename to frontend/src/pages/app/ui/components/chat/Message.tsx
diff --git a/frontend/src/ui/components/chat/MessageContextMenu.tsx b/frontend/src/pages/app/ui/components/chat/MessageContextMenu.tsx
similarity index 100%
rename from frontend/src/ui/components/chat/MessageContextMenu.tsx
rename to frontend/src/pages/app/ui/components/chat/MessageContextMenu.tsx
diff --git a/frontend/src/ui/components/chat/MessagePanelRenderer.tsx b/frontend/src/pages/app/ui/components/chat/MessagePanelRenderer.tsx
similarity index 100%
rename from frontend/src/ui/components/chat/MessagePanelRenderer.tsx
rename to frontend/src/pages/app/ui/components/chat/MessagePanelRenderer.tsx
diff --git a/frontend/src/ui/components/chat/ReplyMessageDialog.tsx b/frontend/src/pages/app/ui/components/chat/ReplyMessageDialog.tsx
similarity index 100%
rename from frontend/src/ui/components/chat/ReplyMessageDialog.tsx
rename to frontend/src/pages/app/ui/components/chat/ReplyMessageDialog.tsx
diff --git a/frontend/src/ui/components/chat/RightPanel.tsx b/frontend/src/pages/app/ui/components/chat/RightPanel.tsx
similarity index 100%
rename from frontend/src/ui/components/chat/RightPanel.tsx
rename to frontend/src/pages/app/ui/components/chat/RightPanel.tsx
diff --git a/frontend/src/ui/components/chat/UserProfileDialog.tsx b/frontend/src/pages/app/ui/components/chat/UserProfileDialog.tsx
similarity index 100%
rename from frontend/src/ui/components/chat/UserProfileDialog.tsx
rename to frontend/src/pages/app/ui/components/chat/UserProfileDialog.tsx
diff --git a/frontend/src/ui/components/core/Dialog.tsx b/frontend/src/pages/app/ui/components/core/Dialog.tsx
similarity index 100%
rename from frontend/src/ui/components/core/Dialog.tsx
rename to frontend/src/pages/app/ui/components/core/Dialog.tsx
diff --git a/frontend/src/ui/components/core/Quote.tsx b/frontend/src/pages/app/ui/components/core/Quote.tsx
similarity index 100%
rename from frontend/src/ui/components/core/Quote.tsx
rename to frontend/src/pages/app/ui/components/core/Quote.tsx
diff --git a/frontend/src/ui/components/core/RichTextArea.tsx b/frontend/src/pages/app/ui/components/core/RichTextArea.tsx
similarity index 100%
rename from frontend/src/ui/components/core/RichTextArea.tsx
rename to frontend/src/pages/app/ui/components/core/RichTextArea.tsx
diff --git a/frontend/src/ui/components/core/TextField.tsx b/frontend/src/pages/app/ui/components/core/TextField.tsx
similarity index 100%
rename from frontend/src/ui/components/core/TextField.tsx
rename to frontend/src/pages/app/ui/components/core/TextField.tsx
diff --git a/frontend/src/ui/components/core/animations/AnimatedHeight.tsx b/frontend/src/pages/app/ui/components/core/animations/AnimatedHeight.tsx
similarity index 100%
rename from frontend/src/ui/components/core/animations/AnimatedHeight.tsx
rename to frontend/src/pages/app/ui/components/core/animations/AnimatedHeight.tsx
diff --git a/frontend/src/ui/components/core/animations/AnimatedOpacity.tsx b/frontend/src/pages/app/ui/components/core/animations/AnimatedOpacity.tsx
similarity index 100%
rename from frontend/src/ui/components/core/animations/AnimatedOpacity.tsx
rename to frontend/src/pages/app/ui/components/core/animations/AnimatedOpacity.tsx
diff --git a/frontend/src/ui/components/core/animations/types.d.ts b/frontend/src/pages/app/ui/components/core/animations/types.d.ts
similarity index 100%
rename from frontend/src/ui/components/core/animations/types.d.ts
rename to frontend/src/pages/app/ui/components/core/animations/types.d.ts
diff --git a/frontend/src/ui/components/profile/CropperDialog.tsx b/frontend/src/pages/app/ui/components/profile/CropperDialog.tsx
similarity index 100%
rename from frontend/src/ui/components/profile/CropperDialog.tsx
rename to frontend/src/pages/app/ui/components/profile/CropperDialog.tsx
diff --git a/frontend/src/ui/components/profile/ImageCropper.tsx b/frontend/src/pages/app/ui/components/profile/ImageCropper.tsx
similarity index 100%
rename from frontend/src/ui/components/profile/ImageCropper.tsx
rename to frontend/src/pages/app/ui/components/profile/ImageCropper.tsx
diff --git a/frontend/src/ui/components/profile/ProfileDialog.tsx b/frontend/src/pages/app/ui/components/profile/ProfileDialog.tsx
similarity index 100%
rename from frontend/src/ui/components/profile/ProfileDialog.tsx
rename to frontend/src/pages/app/ui/components/profile/ProfileDialog.tsx
diff --git a/frontend/src/ui/components/settings/SettingsDialog.tsx b/frontend/src/pages/app/ui/components/settings/SettingsDialog.tsx
similarity index 100%
rename from frontend/src/ui/components/settings/SettingsDialog.tsx
rename to frontend/src/pages/app/ui/components/settings/SettingsDialog.tsx
diff --git a/frontend/src/ui/hooks/useChat.ts b/frontend/src/pages/app/ui/hooks/useChat.ts
similarity index 100%
rename from frontend/src/ui/hooks/useChat.ts
rename to frontend/src/pages/app/ui/hooks/useChat.ts
diff --git a/frontend/src/ui/hooks/useCombinedRefs.ts b/frontend/src/pages/app/ui/hooks/useCombinedRefs.ts
similarity index 100%
rename from frontend/src/ui/hooks/useCombinedRefs.ts
rename to frontend/src/pages/app/ui/hooks/useCombinedRefs.ts
diff --git a/frontend/src/ui/hooks/useDM.ts b/frontend/src/pages/app/ui/hooks/useDM.ts
similarity index 100%
rename from frontend/src/ui/hooks/useDM.ts
rename to frontend/src/pages/app/ui/hooks/useDM.ts
diff --git a/frontend/src/ui/hooks/useProfile.ts b/frontend/src/pages/app/ui/hooks/useProfile.ts
similarity index 100%
rename from frontend/src/ui/hooks/useProfile.ts
rename to frontend/src/pages/app/ui/hooks/useProfile.ts
diff --git a/frontend/src/ui/hooks/useWindowSize.ts b/frontend/src/pages/app/ui/hooks/useWindowSize.ts
similarity index 100%
rename from frontend/src/ui/hooks/useWindowSize.ts
rename to frontend/src/pages/app/ui/hooks/useWindowSize.ts
diff --git a/frontend/src/ui/panels/DMPanel.ts b/frontend/src/pages/app/ui/panels/DMPanel.ts
similarity index 100%
rename from frontend/src/ui/panels/DMPanel.ts
rename to frontend/src/pages/app/ui/panels/DMPanel.ts
diff --git a/frontend/src/ui/panels/MessagePanel.ts b/frontend/src/pages/app/ui/panels/MessagePanel.ts
similarity index 100%
rename from frontend/src/ui/panels/MessagePanel.ts
rename to frontend/src/pages/app/ui/panels/MessagePanel.ts
diff --git a/frontend/src/ui/panels/PublicChatPanel.ts b/frontend/src/pages/app/ui/panels/PublicChatPanel.ts
similarity index 100%
rename from frontend/src/ui/panels/PublicChatPanel.ts
rename to frontend/src/pages/app/ui/panels/PublicChatPanel.ts
diff --git a/frontend/src/ui/screen/ChatScreen.tsx b/frontend/src/pages/app/ui/screen/ChatScreen.tsx
similarity index 100%
rename from frontend/src/ui/screen/ChatScreen.tsx
rename to frontend/src/pages/app/ui/screen/ChatScreen.tsx
diff --git a/frontend/src/ui/screen/DownloadAppScreen.tsx b/frontend/src/pages/app/ui/screen/DownloadAppScreen.tsx
similarity index 100%
rename from frontend/src/ui/screen/DownloadAppScreen.tsx
rename to frontend/src/pages/app/ui/screen/DownloadAppScreen.tsx
diff --git a/frontend/src/ui/screen/LoginScreen.tsx b/frontend/src/pages/app/ui/screen/LoginScreen.tsx
similarity index 100%
rename from frontend/src/ui/screen/LoginScreen.tsx
rename to frontend/src/pages/app/ui/screen/LoginScreen.tsx
diff --git a/frontend/src/ui/screen/RegisterScreen.tsx b/frontend/src/pages/app/ui/screen/RegisterScreen.tsx
similarity index 100%
rename from frontend/src/ui/screen/RegisterScreen.tsx
rename to frontend/src/pages/app/ui/screen/RegisterScreen.tsx
diff --git a/frontend/src/ui/state.ts b/frontend/src/pages/app/ui/state.ts
similarity index 100%
rename from frontend/src/ui/state.ts
rename to frontend/src/pages/app/ui/state.ts
diff --git a/frontend/src/utils/crypto/asymmetric.ts b/frontend/src/pages/app/utils/crypto/asymmetric.ts
similarity index 93%
rename from frontend/src/utils/crypto/asymmetric.ts
rename to frontend/src/pages/app/utils/crypto/asymmetric.ts
index 09b8dfa..72bf39f 100644
--- a/frontend/src/utils/crypto/asymmetric.ts
+++ b/frontend/src/pages/app/utils/crypto/asymmetric.ts
@@ -1,5 +1,5 @@
import nacl from "tweetnacl";
-import { hkdfExtractAndExpand } from "../crypto/kdf";
+import { hkdfExtractAndExpand } from "./kdf";
export interface X25519KeyPair {
publicKey: Uint8Array;
diff --git a/frontend/src/utils/crypto/backup.ts b/frontend/src/pages/app/utils/crypto/backup.ts
similarity index 100%
rename from frontend/src/utils/crypto/backup.ts
rename to frontend/src/pages/app/utils/crypto/backup.ts
diff --git a/frontend/src/utils/crypto/kdf.ts b/frontend/src/pages/app/utils/crypto/kdf.ts
similarity index 100%
rename from frontend/src/utils/crypto/kdf.ts
rename to frontend/src/pages/app/utils/crypto/kdf.ts
diff --git a/frontend/src/utils/crypto/symmetric.ts b/frontend/src/pages/app/utils/crypto/symmetric.ts
similarity index 100%
rename from frontend/src/utils/crypto/symmetric.ts
rename to frontend/src/pages/app/utils/crypto/symmetric.ts
diff --git a/frontend/src/utils/material.ts b/frontend/src/pages/app/utils/material.ts
similarity index 100%
rename from frontend/src/utils/material.ts
rename to frontend/src/pages/app/utils/material.ts
diff --git a/frontend/src/utils/notification.ts b/frontend/src/pages/app/utils/notification.ts
similarity index 100%
rename from frontend/src/utils/notification.ts
rename to frontend/src/pages/app/utils/notification.ts
diff --git a/frontend/src/utils/push-notifications.ts b/frontend/src/pages/app/utils/push-notifications.ts
similarity index 100%
rename from frontend/src/utils/push-notifications.ts
rename to frontend/src/pages/app/utils/push-notifications.ts
diff --git a/frontend/src/utils/utils.ts b/frontend/src/pages/app/utils/utils.ts
similarity index 100%
rename from frontend/src/utils/utils.ts
rename to frontend/src/pages/app/utils/utils.ts
From 2995bc96acfca7ec33b5bbdafe4b3098bfeb4e92 Mon Sep 17 00:00:00 2001
From: denis0001-dev
Date: Thu, 2 Oct 2025 18:24:11 +0300
Subject: [PATCH 2/5] Use proper Vite syntax
---
.../src/pages/app/utils/push-notifications.ts | 3 +-
frontend/vite.config.ts | 46 +------------------
2 files changed, 4 insertions(+), 45 deletions(-)
diff --git a/frontend/src/pages/app/utils/push-notifications.ts b/frontend/src/pages/app/utils/push-notifications.ts
index eb8cf5d..2696d30 100644
--- a/frontend/src/pages/app/utils/push-notifications.ts
+++ b/frontend/src/pages/app/utils/push-notifications.ts
@@ -2,6 +2,7 @@ import { API_BASE_URL } from "../core/config";
import { isElectron } from "../electron/electron";
import { websocket } from "../core/websocket";
import type { NewMessageWebSocketMessage, WebSocketMessage } from "../core/types";
+import serviceWorker from "../service-worker/service-worker.ts?worker&url";
export interface PushSubscriptionData {
endpoint: string;
@@ -152,7 +153,7 @@ export async function initialize(): Promise {
}
try {
- registration = await navigator.serviceWorker.register("/assets/serviceWorker.js");
+ registration = await navigator.serviceWorker.register(serviceWorker, { type: "module" });
console.log("Service Worker registered successfully");
const permission = await Notification.requestPermission();
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
index 1386a83..66e9e17 100644
--- a/frontend/vite.config.ts
+++ b/frontend/vite.config.ts
@@ -5,8 +5,6 @@ import electron from "vite-plugin-electron/simple";
import react from "@vitejs/plugin-react";
import { resolve } from "path";
-const serviceWorkerPath = resolve(__dirname, "src/service-worker/service-worker.ts");
-
const plugins: PluginOption[] = [
react(),
createHtmlPlugin({
@@ -20,31 +18,7 @@ const plugins: PluginOption[] = [
minifyCSS: true,
minifyJS: true
}
- }),
- {
- name: 'service-worker-redirect',
- configureServer(server) {
- server.middlewares.use(async (req, res, next) => {
- if (req.url === '/assets/serviceWorker.js') {
- try {
- const traspiled = await server.transformRequest(serviceWorkerPath);
-
- res.writeHead(200, {
- 'Content-Type': "application/javascript"
- });
- res.end(traspiled!.code);
- } catch (e) {
- try {
- res.writeHead(500);
- res.end();
- } catch (e) {}
- }
- } else {
- next();
- }
- });
- }
- }
+ })
]
if (process.env.VITE_ELECTRON) {
@@ -107,22 +81,6 @@ export default defineConfig({
cssMinify: true,
assetsInlineLimit: 0,
outDir: process.env.VITE_ELECTRON ? "build/electron/dist" : "build/normal/dist",
- chunkSizeWarningLimit: 1024,
- rollupOptions: {
- input: {
- main: resolve(__dirname, "index.html"),
- serviceWorker: serviceWorkerPath
- },
- output: {
- entryFileNames: (chunkInfo) => {
- // Проверяем имя чанка
- if (chunkInfo.name === 'serviceWorker') {
- return 'assets/serviceWorker.js'; // Указываем фиксированное имя для этого скрипта
- }
- // Для остальных файлов используем стандартное именование с хэшем
- return 'assets/[name]-[hash].js';
- }
- }
- }
+ chunkSizeWarningLimit: 1024
}
});
\ No newline at end of file
From aa1fb8c5188a60025f26b577ee68ad90ea3608b5 Mon Sep 17 00:00:00 2001
From: denis0001-dev
Date: Thu, 2 Oct 2025 18:35:29 +0300
Subject: [PATCH 3/5] Migrate to React Router
---
frontend/src/App.tsx | 67 ++++----
frontend/src/main.tsx | 2 +-
frontend/src/pages/ChatPage.tsx | 13 ++
frontend/src/pages/DownloadAppPage.tsx | 25 +++
frontend/src/pages/LoginPage.tsx | 142 +++++++++++++++++
frontend/src/pages/NotFoundPage.tsx | 36 +++++
frontend/src/pages/ProtectedRoute.tsx | 21 +++
frontend/src/pages/RegisterPage.tsx | 147 ++++++++++++++++++
.../src/pages/app/resources/css/_404.scss | 84 ++++++++++
.../src/pages/app/resources/css/style.scss | 1 +
.../src/pages/app/ui/screen/LoginScreen.tsx | 7 +-
.../pages/app/ui/screen/RegisterScreen.tsx | 7 +-
frontend/src/pages/app/ui/state.ts | 15 +-
package.json | 1 +
14 files changed, 514 insertions(+), 54 deletions(-)
create mode 100644 frontend/src/pages/ChatPage.tsx
create mode 100644 frontend/src/pages/DownloadAppPage.tsx
create mode 100644 frontend/src/pages/LoginPage.tsx
create mode 100644 frontend/src/pages/NotFoundPage.tsx
create mode 100644 frontend/src/pages/ProtectedRoute.tsx
create mode 100644 frontend/src/pages/RegisterPage.tsx
create mode 100644 frontend/src/pages/app/resources/css/_404.scss
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index d403a77..275b2e0 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -1,50 +1,47 @@
-import { MINIMUM_WIDTH } from "./pages/app/core/config";
-import { isElectron } from "./pages/app/electron/electron";
+import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import { ElectronTitleBar } from "./pages/app/ui/components/Electron";
-import useWindowSize from "./pages/app/ui/hooks/useWindowSize";
-import ChatScreen from "./pages/app/ui/screen/ChatScreen";
-import DownloadAppScreen from "./pages/app/ui/screen/DownloadAppScreen";
-import LoginScreen from "./pages/app/ui/screen/LoginScreen";
-import RegisterScreen from "./pages/app/ui/screen/RegisterScreen";
import { useAppState } from "./pages/app/ui/state";
-import { useEffect } from "react";
+import { useEffect, useState } from "react";
+import LoginPage from "./pages/LoginPage";
+import RegisterPage from "./pages/RegisterPage";
+import ChatPage from "./pages/ChatPage";
+import NotFoundPage from "./pages/NotFoundPage";
+import ProtectedRoute from "./pages/ProtectedRoute";
+import { isElectron } from "./pages/app/electron/electron";
+import { MINIMUM_WIDTH } from "./pages/app/core/config";
+import useWindowSize from "./pages/app/ui/hooks/useWindowSize";
export default function App() {
- const { currentPage, restoreUserFromStorage } = useAppState();
+ const { restoreUserFromStorage } = useAppState();
const { width } = useWindowSize();
+ const [authReady, setAuthReady] = useState(false);
// Restore user from localStorage on app initialization
useEffect(() => {
- restoreUserFromStorage();
+ restoreUserFromStorage().finally(() => {
+ setAuthReady(true);
+ });
}, [restoreUserFromStorage]);
- if (!isElectron && width < MINIMUM_WIDTH) {
- return
- }
-
- let page = ;
-
- switch (currentPage) {
- case "login": {
- page =
- break;
- }
- case "register": {
- page =
- break;
- }
- case "chat": {
- page =
- break;
- }
- }
-
- return (
- <>
+ return authReady && (
+
+ {!isElectron && width < MINIMUM_WIDTH && }
- {page}
+
+ } />
+ } />
+ } />
+
+
+
+
+ } />
+
+ } />
+
- >
+
)
}
\ No newline at end of file
diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx
index 572bfd4..2296930 100644
--- a/frontend/src/main.tsx
+++ b/frontend/src/main.tsx
@@ -5,7 +5,7 @@
* @version 1.0.0
*/
-import './resources/css/style.scss';
+import './pages/app/resources/css/style.scss';
import "mdui/mdui.css";
import "./pages/app/utils/material";
diff --git a/frontend/src/pages/ChatPage.tsx b/frontend/src/pages/ChatPage.tsx
new file mode 100644
index 0000000..0989a42
--- /dev/null
+++ b/frontend/src/pages/ChatPage.tsx
@@ -0,0 +1,13 @@
+import { LeftPanel } from "./app/ui/components/chat/LeftPanel";
+import { RightPanel } from "./app/ui/components/chat/RightPanel";
+
+export default function ChatPage() {
+ return (
+
+ );
+}
diff --git a/frontend/src/pages/DownloadAppPage.tsx b/frontend/src/pages/DownloadAppPage.tsx
new file mode 100644
index 0000000..3e8ec2e
--- /dev/null
+++ b/frontend/src/pages/DownloadAppPage.tsx
@@ -0,0 +1,25 @@
+export default function DownloadAppPage() {
+ return (
+
+
+
Чтобы пользоваться мессенджером, скачайте приложение
+
+ Этот сайт не предназначен для работы на маленьких экранах, поэтому
+ вам нужно скачать приложение мессенджера.
+
+
+
+ Скачать на GitHub
+
+
+
+ Если возникнут сложности или есть вопросы, нажмите кнопку!
+
+
+
+ Написать в поддержку
+
+
+
+ )
+}
diff --git a/frontend/src/pages/LoginPage.tsx b/frontend/src/pages/LoginPage.tsx
new file mode 100644
index 0000000..0fdb5b4
--- /dev/null
+++ b/frontend/src/pages/LoginPage.tsx
@@ -0,0 +1,142 @@
+import { useImmer } from "use-immer";
+import { AlertsContainer, type Alert, type AlertType } from "./app/ui/components/Alerts";
+import { AuthContainer, AuthHeader } from "./app/ui/components/Auth";
+import type { ErrorResponse, LoginRequest, LoginResponse } from "./app/core/types";
+import { ensureKeysOnLogin } from "./app/auth/crypto";
+import { API_BASE_URL } from "./app/core/config";
+import { useRef } from "react";
+import type { TextField } from "mdui/components/text-field";
+import { useAppState } from "./app/ui/state";
+import { MaterialTextField } from "./app/ui/components/core/TextField";
+import { initialize, isSupported, startElectronReceiver, subscribe } from "./app/utils/push-notifications";
+import { isElectron } from "./app/electron/electron";
+import { useNavigate } from "react-router-dom";
+
+export default function LoginPage() {
+ const [alerts, updateAlerts] = useImmer([]);
+ const setUser = useAppState(state => state.setUser);
+ const navigate = useNavigate();
+
+ function showAlert(type: AlertType, message: string) {
+ updateAlerts((alerts) => { alerts.push({type: type, message: message}) });
+ }
+
+ const usernameElement = useRef(null);
+ const passwordElement = useRef(null);
+
+ return (
+
+
+
+
+ )
+}
diff --git a/frontend/src/pages/NotFoundPage.tsx b/frontend/src/pages/NotFoundPage.tsx
new file mode 100644
index 0000000..907119f
--- /dev/null
+++ b/frontend/src/pages/NotFoundPage.tsx
@@ -0,0 +1,36 @@
+import { useNavigate } from "react-router-dom";
+
+export default function NotFoundPage() {
+ const navigate = useNavigate();
+
+ return (
+
+
+
+
404
+
Страница не найдена
+
+ К сожалению, запрашиваемая страница не существует или была перемещена.
+
+
+ navigate("/")}
+ >
+ На главную
+
+ navigate(-1)}
+ >
+ Назад
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/pages/ProtectedRoute.tsx b/frontend/src/pages/ProtectedRoute.tsx
new file mode 100644
index 0000000..40c4652
--- /dev/null
+++ b/frontend/src/pages/ProtectedRoute.tsx
@@ -0,0 +1,21 @@
+import { useEffect } from "react";
+import { useAppState } from "./app/ui/state";
+import { useNavigate } from "react-router-dom";
+
+interface ProtectedRouteProps {
+ children: React.ReactNode;
+}
+
+export default function ProtectedRoute({ children }: ProtectedRouteProps) {
+ const { user } = useAppState();
+ const navigate = useNavigate();
+
+ useEffect(() => {
+ if (!user.authToken) {
+ navigate("/login");
+ return;
+ }
+ }, [user.authToken, user.currentUser, navigate]);
+
+ return <>{children}>;
+}
diff --git a/frontend/src/pages/RegisterPage.tsx b/frontend/src/pages/RegisterPage.tsx
new file mode 100644
index 0000000..2e437e1
--- /dev/null
+++ b/frontend/src/pages/RegisterPage.tsx
@@ -0,0 +1,147 @@
+import { useImmer } from "use-immer";
+import { AuthContainer, AuthHeader } from "./app/ui/components/Auth";
+import { AlertsContainer, type Alert, type AlertType } from "./app/ui/components/Alerts";
+import { useRef } from "react";
+import { TextField } from "mdui/components/text-field";
+import type { ErrorResponse, RegisterRequest, LoginResponse } from "./app/core/types";
+import { API_BASE_URL } from "./app/core/config";
+import { useAppState } from "./app/ui/state";
+import { MaterialTextField } from "./app/ui/components/core/TextField";
+import { ensureKeysOnLogin } from "./app/auth/crypto";
+import { useNavigate } from "react-router-dom";
+
+export default function RegisterPage() {
+ const [alerts, updateAlerts] = useImmer([]);
+ const setUser = useAppState(state => state.setUser);
+ const navigate = useNavigate();
+
+ function showAlert(type: AlertType, message: string) {
+ updateAlerts((alerts) => { alerts.push({type: type, message: message}) });
+ }
+
+ const usernameElement = useRef(null);
+ const passwordElement = useRef(null);
+ const confirmPasswordElement = useRef(null);
+
+ return (
+
+
+
+
+ )
+}
diff --git a/frontend/src/pages/app/resources/css/_404.scss b/frontend/src/pages/app/resources/css/_404.scss
new file mode 100644
index 0000000..d91252f
--- /dev/null
+++ b/frontend/src/pages/app/resources/css/_404.scss
@@ -0,0 +1,84 @@
+.not-found-page {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 100vh;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ padding: 2rem;
+}
+
+.not-found-container {
+ display: flex;
+ align-items: center;
+ gap: 3rem;
+ max-width: 800px;
+ width: 100%;
+ background: rgba(255, 255, 255, 0.95);
+ border-radius: 20px;
+ padding: 3rem;
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
+ backdrop-filter: blur(10px);
+}
+
+.not-found-content {
+ flex: 1;
+}
+
+.error-code {
+ font-size: 6rem;
+ font-weight: 900;
+ color: #667eea;
+ line-height: 1;
+ margin-bottom: 1rem;
+ text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
+}
+
+.not-found-content h1 {
+ font-size: 2.5rem;
+ font-weight: 700;
+ color: #2d3748;
+ margin-bottom: 1rem;
+ line-height: 1.2;
+}
+
+.not-found-content p {
+ font-size: 1.1rem;
+ color: #718096;
+ margin-bottom: 2rem;
+ line-height: 1.6;
+}
+
+.not-found-actions {
+ display: flex;
+ gap: 1rem;
+ flex-wrap: wrap;
+}
+
+.not-found-illustration {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: #667eea;
+ opacity: 0.7;
+}
+
+@media (max-width: 768px) {
+ .not-found-container {
+ flex-direction: column;
+ text-align: center;
+ gap: 2rem;
+ padding: 2rem;
+ }
+
+ .error-code {
+ font-size: 4rem;
+ }
+
+ .not-found-content h1 {
+ font-size: 2rem;
+ }
+
+ .not-found-actions {
+ justify-content: center;
+ }
+}
diff --git a/frontend/src/pages/app/resources/css/style.scss b/frontend/src/pages/app/resources/css/style.scss
index 4cfe673..3e06041 100644
--- a/frontend/src/pages/app/resources/css/style.scss
+++ b/frontend/src/pages/app/resources/css/style.scss
@@ -10,6 +10,7 @@
@use "electron";
@use "dialogs/reply";
@use "download-app";
+@use "404" as not-found;
@use "lib/fonts/montserrat";
@use "lib/fonts/material-symbols";
diff --git a/frontend/src/pages/app/ui/screen/LoginScreen.tsx b/frontend/src/pages/app/ui/screen/LoginScreen.tsx
index 06df52f..168b0d8 100644
--- a/frontend/src/pages/app/ui/screen/LoginScreen.tsx
+++ b/frontend/src/pages/app/ui/screen/LoginScreen.tsx
@@ -7,14 +7,15 @@ import { API_BASE_URL } from "../../core/config";
import { useRef } from "react";
import type { TextField } from "mdui/components/text-field";
import { useAppState } from "../state";
+import { useNavigate } from "react-router-dom";
import { MaterialTextField } from "../components/core/TextField";
import { initialize, isSupported, startElectronReceiver, subscribe } from "../../utils/push-notifications";
import { isElectron } from "../../electron/electron";
export default function LoginScreen() {
const [alerts, updateAlerts] = useImmer([]);
- const setCurrentPage = useAppState(state => state.setCurrentPage);
const setUser = useAppState(state => state.setUser);
+ const navigate = useNavigate();
function showAlert(type: AlertType, message: string) {
updateAlerts((alerts) => { alerts.push({type: type, message: message}) });
@@ -67,7 +68,7 @@ export default function LoginScreen() {
console.error("Key setup failed:", e);
}
- setCurrentPage("chat");
+ navigate("/chat");
// Initialize notifications
try {
@@ -130,7 +131,7 @@ export default function LoginScreen() {
setCurrentPage("register")}>
+ onClick={() => navigate("/register")}>
Зарегистрируйтесь
diff --git a/frontend/src/pages/app/ui/screen/RegisterScreen.tsx b/frontend/src/pages/app/ui/screen/RegisterScreen.tsx
index a93efd3..ec16814 100644
--- a/frontend/src/pages/app/ui/screen/RegisterScreen.tsx
+++ b/frontend/src/pages/app/ui/screen/RegisterScreen.tsx
@@ -7,13 +7,14 @@ import { TextField } from "mdui/components/text-field";
import type { ErrorResponse, RegisterRequest, LoginResponse } from "../../core/types";
import { API_BASE_URL } from "../../core/config";
import { useAppState } from "../state";
+import { useNavigate } from "react-router-dom";
import { MaterialTextField } from "../components/core/TextField";
import { ensureKeysOnLogin } from "../../auth/crypto";
export default function RegisterScreen() {
const [alerts, updateAlerts] = useImmer([]);
- const setCurrentPage = useAppState(state => state.setCurrentPage);
const setUser = useAppState(state => state.setUser);
+ const navigate = useNavigate();
function showAlert(type: AlertType, message: string) {
updateAlerts((alerts) => { alerts.push({type: type, message: message}) });
@@ -83,7 +84,7 @@ export default function RegisterScreen() {
console.error("Key setup failed:", e);
}
- setCurrentPage("chat");
+ navigate("/chat");
} else {
const data: ErrorResponse = await response.json();
showAlert("danger", data.message || "Ошибка при регистрации");
@@ -136,7 +137,7 @@ export default function RegisterScreen() {
href="#"
id="login-link"
className="link"
- onClick={() => setCurrentPage("login")}>
+ onClick={() => navigate("/login")}>
Войдите
diff --git a/frontend/src/pages/app/ui/state.ts b/frontend/src/pages/app/ui/state.ts
index bf31eb4..c5db87e 100644
--- a/frontend/src/pages/app/ui/state.ts
+++ b/frontend/src/pages/app/ui/state.ts
@@ -10,7 +10,6 @@ import { API_BASE_URL } from "../core/config";
import { initialize, subscribe, startElectronReceiver, isSupported } from "../utils/push-notifications";
import { isElectron } from "../electron/electron";
-type Page = "login" | "register" | "chat"
export type ChatTabs = "chats" | "channels" | "contacts" | "dms"
interface ActiveDM {
@@ -37,9 +36,6 @@ export interface UserState {
}
interface AppState {
- currentPage: Page;
- setCurrentPage: (page: Page) => void;
-
// Chat state
chat: ChatState;
addMessage: (message: Message) => void;
@@ -60,13 +56,10 @@ interface AppState {
user: UserState;
setUser: (token: string, user: User) => void;
logout: () => void;
- restoreUserFromStorage: () => void;
+ restoreUserFromStorage: () => Promise;
}
export const useAppState = create((set, get) => ({
- currentPage: "login", // default page
- setCurrentPage: (page: Page) => set({ currentPage: page }),
-
// Chat state
chat: {
messages: [],
@@ -191,8 +184,7 @@ export const useAppState = create((set, get) => ({
user: {
currentUser: null,
authToken: null
- },
- currentPage: "login"
+ }
}));
},
restoreUserFromStorage: async () => {
@@ -212,8 +204,7 @@ export const useAppState = create((set, get) => ({
user: {
currentUser: user,
authToken: token
- },
- currentPage: "chat"
+ }
}));
try {
diff --git a/package.json b/package.json
index 7a1dd91..ab204c5 100644
--- a/package.json
+++ b/package.json
@@ -68,6 +68,7 @@
"mdui": "^2.1.4",
"react": "^19.1.1",
"react-dom": "^19.1.1",
+ "react-router-dom": "^7.9.3",
"tweetnacl": "^1.0.3",
"use-immer": "^0.11.0",
"zustand": "^5.0.8"
From 10c2aaa6caa92f3e818ee6e1803aa008bcda8ef1 Mon Sep 17 00:00:00 2001
From: denis0001-dev
Date: Thu, 2 Oct 2025 19:36:39 +0300
Subject: [PATCH 4/5] Implement the new homepage
---
frontend/src/App.tsx | 5 +-
frontend/src/pages/HomePage.tsx | 267 +++++++
.../pages/app/resources/css/_homepage.scss | 657 ++++++++++++++++++
.../src/pages/app/resources/css/style.scss | 1 +
4 files changed, 929 insertions(+), 1 deletion(-)
create mode 100644 frontend/src/pages/HomePage.tsx
create mode 100644 frontend/src/pages/app/resources/css/_homepage.scss
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 275b2e0..8177b51 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -2,9 +2,11 @@ import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import { ElectronTitleBar } from "./pages/app/ui/components/Electron";
import { useAppState } from "./pages/app/ui/state";
import { useEffect, useState } from "react";
+import HomePage from "./pages/HomePage";
import LoginPage from "./pages/LoginPage";
import RegisterPage from "./pages/RegisterPage";
import ChatPage from "./pages/ChatPage";
+import DownloadAppPage from "./pages/DownloadAppPage";
import NotFoundPage from "./pages/NotFoundPage";
import ProtectedRoute from "./pages/ProtectedRoute";
import { isElectron } from "./pages/app/electron/electron";
@@ -29,9 +31,10 @@ export default function App() {
- } />
+ } />
} />
} />
+ } />
diff --git a/frontend/src/pages/HomePage.tsx b/frontend/src/pages/HomePage.tsx
new file mode 100644
index 0000000..4ab16a4
--- /dev/null
+++ b/frontend/src/pages/HomePage.tsx
@@ -0,0 +1,267 @@
+import { useNavigate } from "react-router-dom";
+import { useAppState } from "./app/ui/state";
+
+function GitHubLink({ children }: { children: React.ReactNode }) {
+ return (
+ {children}
+ );
+}
+
+function SupportLink({ children }: { children: React.ReactNode }) {
+ return (
+ {children}
+ );
+}
+
+export default function HomePage() {
+ const navigate = useNavigate();
+ const { user } = useAppState();
+
+ const handleGetStarted = () => {
+ if (user.authToken && user.currentUser) {
+ navigate("/chat");
+ } else {
+ navigate("/login");
+ }
+ };
+
+ return (
+
+
+
+
+
+
FromChat
+ 100% открытый мессенджер
+
+
+
+ GitHub
+
+
+ Поддержка
+
+
+ {user.authToken ? (
+ navigate("/chat")}>
+ Перейти в чат
+
+ ) : (
+ navigate("/login")}>
+ Войти
+
+ )}
+
+
+
+
+
+
+
+
+
+
+ Безопасный мессенджер с открытым исходным кодом
+
+
+ FromChat — это полностью открытый мессенджер с end-to-end шифрованием,
+ поддержкой файлов и уведомлений. Создан для тех, кто ценит приватность и свободу.
+
+
+
+ {user.authToken ? "Перейти в чат" : "Начать общение"}
+
+ navigate("/register")}
+ >
+ Зарегистрироваться
+
+
+
+
+
+
+
+
+
+
А
+
+
Привет! Как дела?
+
14:30
+
+
+
+
+
Всё отлично! А у тебя как?
+
14:32
+
+
+
+
Б
+
+
Отправляю файл 📎
+
14:35
+
+
+
+
+
+
+
+
+
+
+
+
Возможности
+
+
+
+
+
+
End-to-End Шифрование
+
+ Ваши личные сообщения защищены современным шифрованием X25519 + AES-GCM.
+ Только вы и получатель можете прочитать сообщения.
+
+
+
+
+
+
+
+
100% открытый код
+
+ Весь исходный код доступен на GitHub . Вы можете проверить безопасность,
+ внести изменения или развернуть свой сервер.
+
+
+
+
+
+
+
+
Обмен Файлами
+
+ Отправляйте файлы до 4 ГБ. Файлы в личных сообщениях шифруются.
+ В общем чате шифрования нет, так как ваши сообщения могут читать все пользователи FromChat.
+
+
+
+
+
+
+
+
Уведомления
+
+ Получайте push-уведомления в браузере и настольном приложении.
+ Никогда не пропустите важное сообщение.
+
+
+
+
+
+
+
+
Редактирование
+
+ Редактируйте и удаляйте свои сообщения. Отвечайте на сообщения
+ для лучшего контекста общения.
+
+
+
+
+
+
+
+
Кроссплатформенность
+
+ Работает в браузере и как настольное приложение для Windows,
+ macOS и Linux. Единый интерфейс везде.
+
+
+
+
+
+
+
+
+
+
Скачайте приложение
+
+ Для лучшего опыта используйте настольное приложение с поддержкой
+ уведомлений и автономной работы.
+
+
+
+
+
+
+
+
+
+
Готовы начать общение?
+
+ Присоединяйтесь к FromChat и общайтесь безопасно с друзьями и коллегами.
+
+
+ navigate("/register")}
+ >
+ Создать аккаунт
+
+ navigate("/login")}
+ >
+ Войти
+
+
+
+
+
+
+
+
+
+
+
+
Ссылки
+ GitHub
+ Поддержка
+
+
+
+
+
© 2025 FromChat. Сделано программистом denis0001-dev с ❤️ для свободы общения.
+
+
+
+
+ );
+}
diff --git a/frontend/src/pages/app/resources/css/_homepage.scss b/frontend/src/pages/app/resources/css/_homepage.scss
new file mode 100644
index 0000000..15aee92
--- /dev/null
+++ b/frontend/src/pages/app/resources/css/_homepage.scss
@@ -0,0 +1,657 @@
+@use "common/material" as *;
+
+.homepage {
+ min-height: 100vh;
+ background: linear-gradient(135deg, $color-dark-background 0%, $color-dark-surface-container 50%, $color-dark-primary-container 100%);
+ color: $color-dark-on-background;
+ font-family: 'Montserrat', sans-serif;
+ position: relative;
+
+ &::before {
+ content: '';
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background:
+ radial-gradient(circle at 20% 80%, rgba($color-dark-primary, 0.3) 0%, transparent 50%),
+ radial-gradient(circle at 80% 20%, rgba($color-dark-tertiary, 0.3) 0%, transparent 50%),
+ radial-gradient(circle at 40% 40%, rgba($color-dark-secondary, 0.2) 0%, transparent 50%);
+ pointer-events: none;
+ z-index: 0;
+ }
+
+ // Cascaded styles for all child elements
+ * {
+ position: relative;
+ z-index: 1;
+ }
+
+ // Container styles
+ .container {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 0 2rem;
+ }
+
+ // Header styles
+ .homepage-header {
+ padding: 1rem 0;
+ background: rgba($color-dark-surface-container, 0.8);
+ backdrop-filter: blur(20px);
+ border-bottom: 1px solid rgba($color-dark-primary, 0.3);
+ box-shadow: 0 4px 20px rgba($color-dark-primary, 0.1);
+ position: sticky;
+ top: 0;
+ z-index: 1000;
+ transition: all 0.3s ease;
+
+ .header-content {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ .logo {
+ h1 {
+ font-size: 2rem;
+ font-weight: 700;
+ margin: 0;
+ background: linear-gradient(45deg, $color-dark-primary, $color-dark-tertiary);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ text-shadow: 0 0 20px rgba($color-dark-primary, 0.5);
+ }
+
+ .tagline {
+ font-size: 0.9rem;
+ }
+ }
+
+ .header-nav {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+
+ a {
+ display: flex;
+ }
+ }
+ }
+ }
+
+ // Hero section
+ .hero {
+ padding: 4rem 0;
+ display: flex;
+ align-items: center;
+ min-height: 80vh;
+ margin-top: 0;
+
+ .hero-content {
+ flex: 1;
+ max-width: 600px;
+
+ .hero-title {
+ font-size: 3.5rem;
+ font-weight: 800;
+ line-height: 1.1;
+ margin-bottom: 1.5rem;
+ background: linear-gradient(45deg, $color-dark-primary, $color-dark-tertiary, $color-dark-secondary);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ text-shadow: 0 0 30px rgba($color-dark-primary, 0.5);
+ animation: neonGlow 3s ease-in-out infinite alternate;
+ }
+
+ .hero-description {
+ font-size: 1.25rem;
+ line-height: 1.6;
+ margin-bottom: 2.5rem;
+ opacity: 0.9;
+ }
+
+ .hero-actions {
+ display: flex;
+ gap: 1rem;
+ flex-wrap: wrap;
+ }
+ }
+
+ .hero-visual {
+ flex: 1;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 2rem;
+
+ .chat-preview {
+ perspective: 1000px;
+
+ .chat-window {
+ background: rgba($color-dark-surface-container, 0.95);
+ border-radius: 20px;
+ padding: 1.5rem;
+ box-shadow:
+ 0 20px 40px rgba(0, 0, 0, 0.5),
+ 0 0 20px rgba($color-dark-primary, 0.3),
+ inset 0 1px 0 rgba($color-dark-primary, 0.2);
+ transform: rotateY(-5deg) rotateX(5deg);
+ color: $color-dark-on-surface;
+ max-width: 400px;
+ width: 100%;
+ border: 1px solid rgba($color-dark-primary, 0.3);
+
+ .chat-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding-bottom: 1rem;
+ border-bottom: 1px solid rgba($color-dark-primary, 0.3);
+ margin-bottom: 1rem;
+
+ .chat-title {
+ font-weight: 600;
+ font-size: 1.1rem;
+ }
+
+ .online-indicator {
+ color: $color-dark-primary;
+ font-size: 0.8rem;
+ text-shadow: 0 0 10px rgba($color-dark-primary, 0.8);
+ animation: pulse 2s ease-in-out infinite;
+ }
+ }
+
+ .chat-messages {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+
+ .message {
+ display: flex;
+ gap: 0.75rem;
+ align-items: flex-start;
+
+ &.sent {
+ flex-direction: row-reverse;
+
+ .message-content {
+ background: linear-gradient(135deg, $color-dark-primary, $color-dark-primary-container);
+ color: $color-dark-on-primary;
+ box-shadow: 0 0 15px rgba($color-dark-primary, 0.3);
+ }
+ }
+
+ &.received {
+ .message-content {
+ background: rgba($color-dark-surface-variant, 0.8);
+ color: $color-dark-on-surface-variant;
+ border: 1px solid rgba($color-dark-outline-variant, 0.3);
+ }
+ }
+
+ .message-avatar {
+ width: 32px;
+ height: 32px;
+ border-radius: 50%;
+ background: linear-gradient(135deg, $color-dark-primary, $color-dark-tertiary);
+ color: $color-dark-on-primary;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: 600;
+ font-size: 0.8rem;
+ flex-shrink: 0;
+ box-shadow: 0 0 10px rgba($color-dark-primary, 0.4);
+ }
+
+ .message-content {
+ max-width: 70%;
+ padding: 0.75rem 1rem;
+ border-radius: 18px;
+ position: relative;
+
+ .message-text {
+ font-size: 0.9rem;
+ line-height: 1.4;
+ }
+
+ .message-time {
+ font-size: 0.75rem;
+ opacity: 0.7;
+ margin-top: 0.25rem;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Features section
+ .features {
+ padding: 6rem 0;
+ background: rgba($color-dark-surface-container, 0.3);
+ backdrop-filter: blur(20px);
+ border-top: 1px solid rgba($color-dark-primary, 0.2);
+ border-bottom: 1px solid rgba($color-dark-primary, 0.2);
+
+ .section-title {
+ text-align: center;
+ font-size: 2.5rem;
+ font-weight: 700;
+ margin-bottom: 3rem;
+ background: linear-gradient(45deg, $color-dark-primary, $color-dark-tertiary);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ text-shadow: 0 0 20px rgba($color-dark-primary, 0.5);
+ }
+
+ .features-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
+ gap: 2rem;
+
+ .feature-card {
+ background: rgba($color-dark-surface-container, 0.6);
+ backdrop-filter: blur(20px);
+ border-radius: 20px;
+ padding: 2rem;
+ border: 1px solid rgba($color-dark-primary, 0.3);
+ transition: all 0.3s ease;
+ position: relative;
+ overflow: hidden;
+
+ &::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: linear-gradient(135deg, rgba($color-dark-primary, 0.1), rgba($color-dark-tertiary, 0.1));
+ opacity: 0;
+ transition: opacity 0.3s ease;
+ z-index: 0;
+ }
+
+ > * {
+ position: relative;
+ z-index: 1;
+ }
+
+ &:hover {
+ transform: translateY(-5px);
+ box-shadow:
+ 0 20px 40px rgba(0, 0, 0, 0.3),
+ 0 0 30px rgba($color-dark-primary, 0.2);
+ border-color: rgba($color-dark-primary, 0.5);
+
+ &::before {
+ opacity: 1;
+ }
+ }
+
+ .feature-icon {
+ width: 60px;
+ height: 60px;
+ background: linear-gradient(135deg, rgba($color-dark-primary, 0.3), rgba($color-dark-tertiary, 0.3));
+ border-radius: 15px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-bottom: 1.5rem;
+ border: 1px solid rgba($color-dark-primary, 0.4);
+ box-shadow: 0 0 15px rgba($color-dark-primary, 0.2);
+ user-select: none;
+
+ mdui-icon {
+ font-size: 1.5rem;
+ color: $color-dark-primary;
+ text-shadow: 0 0 10px rgba($color-dark-primary, 0.8);
+ }
+ }
+
+ h4 {
+ font-size: 1.25rem;
+ font-weight: 600;
+ margin-bottom: 1rem;
+ color: $color-dark-on-surface;
+ text-shadow: 0 0 10px rgba($color-dark-primary, 0.3);
+ }
+
+ p {
+ line-height: 1.6;
+ opacity: 0.9;
+ color: $color-dark-on-surface-variant;
+ }
+ }
+ }
+ }
+
+ // Download section
+ .download {
+ padding: 6rem 0;
+
+ .download-content {
+ text-align: center;
+ max-width: 600px;
+ margin: 0 auto;
+
+ h3 {
+ font-size: 2.5rem;
+ font-weight: 700;
+ margin-bottom: 1.5rem;
+ background: linear-gradient(45deg, $color-dark-primary, $color-dark-tertiary);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ text-shadow: 0 0 20px rgba($color-dark-primary, 0.5);
+ }
+
+ p {
+ font-size: 1.25rem;
+ line-height: 1.6;
+ margin-bottom: 2.5rem;
+ opacity: 0.9;
+ }
+
+ .download-buttons {
+ display: flex;
+ gap: 1rem;
+ justify-content: center;
+ flex-wrap: wrap;
+ }
+ }
+ }
+
+ // CTA section
+ .cta {
+ padding: 6rem 0;
+ background: rgba($color-dark-surface-container, 0.3);
+ backdrop-filter: blur(20px);
+ border-top: 1px solid rgba($color-dark-primary, 0.2);
+
+ .cta-content {
+ text-align: center;
+ max-width: 600px;
+ margin: 0 auto;
+
+ h3 {
+ font-size: 2.5rem;
+ font-weight: 700;
+ margin-bottom: 1.5rem;
+ background: linear-gradient(45deg, $color-dark-primary, $color-dark-tertiary);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ text-shadow: 0 0 20px rgba($color-dark-primary, 0.5);
+ }
+
+ p {
+ font-size: 1.25rem;
+ line-height: 1.6;
+ margin-bottom: 2.5rem;
+ opacity: 0.9;
+ }
+
+ .cta-actions {
+ display: flex;
+ gap: 1rem;
+ justify-content: center;
+ flex-wrap: wrap;
+ }
+ }
+ }
+
+ // Footer
+ .homepage-footer {
+ background: rgba($color-dark-surface-container, 0.8);
+ backdrop-filter: blur(20px);
+ padding: 3rem 0 1rem;
+ border-top: 1px solid rgba($color-dark-primary, 0.3);
+ box-shadow: 0 -4px 20px rgba($color-dark-primary, 0.1);
+
+ .footer-content {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+ gap: 2rem;
+ margin-bottom: 2rem;
+
+ .footer-section {
+ h4 {
+ font-size: 1.1rem;
+ font-weight: 600;
+ margin-bottom: 1rem;
+ color: $color-dark-on-surface;
+ text-shadow: 0 0 10px rgba($color-dark-primary, 0.3);
+ }
+
+ p {
+ opacity: 0.8;
+ line-height: 1.6;
+ color: $color-dark-on-surface-variant;
+ }
+
+ a {
+ color: $color-dark-on-surface;
+ text-decoration: none;
+ opacity: 0.8;
+ display: block;
+ margin-bottom: 0.5rem;
+ transition: all 0.3s ease;
+ padding: 0.25rem 0;
+ border-radius: 4px;
+
+ &:hover {
+ opacity: 1;
+ color: $color-dark-primary;
+ text-shadow: 0 0 10px rgba($color-dark-primary, 0.5);
+ background: rgba($color-dark-primary, 0.1);
+ }
+ }
+ }
+ }
+
+ .footer-bottom {
+ text-align: center;
+ padding-top: 2rem;
+ border-top: 1px solid rgba($color-dark-primary, 0.3);
+
+ p {
+ opacity: 0.7;
+ margin: 0;
+ color: $color-dark-on-surface-variant;
+ }
+ }
+ }
+}
+
+// Keyframes for animations
+@keyframes neonGlow {
+ 0% {
+ text-shadow: 0 0 20px rgba($color-dark-primary, 0.5), 0 0 40px rgba($color-dark-primary, 0.3);
+ }
+ 100% {
+ text-shadow: 0 0 30px rgba($color-dark-primary, 0.8), 0 0 60px rgba($color-dark-primary, 0.5);
+ }
+}
+
+@keyframes pulse {
+ 0%, 100% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0.5;
+ }
+}
+
+// Responsive Design
+@media (min-width: 1024px) {
+ .homepage {
+ .homepage-header {
+ position: fixed;
+ top: 1rem;
+ left: 50%;
+ transform: translateX(-50%);
+ width: calc(100% - 2rem);
+ max-width: 1200px;
+ border-radius: 20px;
+ border: 1px solid rgba($color-dark-primary, 0.3);
+ box-shadow:
+ 0 8px 32px rgba(0, 0, 0, 0.3),
+ 0 0 20px rgba($color-dark-primary, 0.2);
+ backdrop-filter: blur(30px);
+ background: rgba($color-dark-surface-container, 0.9);
+ }
+
+ .hero {
+ margin-top: 6rem;
+ }
+ }
+}
+
+@media (max-width: 1023px) {
+ .homepage {
+ .homepage-header {
+ position: sticky;
+ top: 0;
+ border-radius: 0;
+ margin: 0;
+ width: 100%;
+ transform: none;
+ }
+ }
+}
+
+@media (max-width: 768px) {
+ .homepage {
+ .container {
+ padding: 0 1rem;
+ }
+
+ .homepage-header {
+ .header-content {
+ flex-direction: column;
+ gap: 1rem;
+ }
+ }
+
+ .hero {
+ flex-direction: column;
+ text-align: center;
+ padding: 2rem 0;
+
+ .hero-content {
+ .hero-title {
+ font-size: 2.5rem;
+ }
+
+ .hero-description {
+ font-size: 1.1rem;
+ }
+ }
+
+ .hero-visual {
+ padding: 1rem;
+
+ .chat-preview {
+ .chat-window {
+ transform: none;
+ max-width: 100%;
+ }
+ }
+ }
+ }
+
+ .features {
+ .features-grid {
+ grid-template-columns: 1fr;
+
+ .feature-card {
+ padding: 1.5rem;
+ }
+ }
+
+ .section-title {
+ font-size: 2rem;
+ }
+ }
+
+ .download {
+ .download-content {
+ h3 {
+ font-size: 2rem;
+ }
+
+ .download-buttons {
+ flex-direction: column;
+ align-items: center;
+ }
+ }
+ }
+
+ .cta {
+ .cta-content {
+ h3 {
+ font-size: 2rem;
+ }
+
+ .cta-actions {
+ flex-direction: column;
+ align-items: center;
+ }
+ }
+ }
+
+ .homepage-footer {
+ .footer-content {
+ grid-template-columns: 1fr;
+ text-align: center;
+ }
+ }
+ }
+}
+
+@media (max-width: 480px) {
+ .homepage {
+ .hero {
+ .hero-content {
+ .hero-title {
+ font-size: 2rem;
+ }
+
+ .hero-description {
+ font-size: 1rem;
+ }
+ }
+ }
+
+ .features {
+ .section-title {
+ font-size: 1.75rem;
+ }
+ }
+
+ .download {
+ .download-content {
+ h3 {
+ font-size: 1.75rem;
+ }
+ }
+ }
+
+ .cta {
+ .cta-content {
+ h3 {
+ font-size: 1.75rem;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/frontend/src/pages/app/resources/css/style.scss b/frontend/src/pages/app/resources/css/style.scss
index 3e06041..225262c 100644
--- a/frontend/src/pages/app/resources/css/style.scss
+++ b/frontend/src/pages/app/resources/css/style.scss
@@ -11,6 +11,7 @@
@use "dialogs/reply";
@use "download-app";
@use "404" as not-found;
+@use "homepage";
@use "lib/fonts/montserrat";
@use "lib/fonts/material-symbols";
From 1f6a8ec0b6aef9a6d1f82008ad73dea7a1c604d5 Mon Sep 17 00:00:00 2001
From: denis0001-dev
Date: Thu, 2 Oct 2025 20:18:37 +0300
Subject: [PATCH 5/5] Use the same design for the app
---
.../src/pages/app/resources/css/_chat.scss | 193 +++++++++++++++---
1 file changed, 162 insertions(+), 31 deletions(-)
diff --git a/frontend/src/pages/app/resources/css/_chat.scss b/frontend/src/pages/app/resources/css/_chat.scss
index 19bb6ab..cf26163 100644
--- a/frontend/src/pages/app/resources/css/_chat.scss
+++ b/frontend/src/pages/app/resources/css/_chat.scss
@@ -1,8 +1,26 @@
@use "common/colors" as *;
@use "common/material" as *;
+@use "sass:color";
#chat-interface {
height: 100%;
+ background: linear-gradient(135deg, $color-dark-background 0%, $color-dark-surface-container 70%, rgba($color-dark-primary-container, 0.3) 100%);
+ position: relative;
+
+ &::before {
+ content: '';
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background:
+ radial-gradient(circle at 20% 80%, rgba($color-dark-primary, 0.15) 0%, transparent 50%),
+ radial-gradient(circle at 80% 20%, rgba($color-dark-tertiary, 0.15) 0%, transparent 50%),
+ radial-gradient(circle at 40% 40%, rgba($color-dark-secondary, 0.1) 0%, transparent 50%);
+ pointer-events: none;
+ z-index: 0;
+ }
.header {
display: flex;
@@ -69,10 +87,13 @@
.chat-header {
padding: 16px;
- background: $color-dark-surface-container;
+ background: rgba($color-dark-surface-container, 0.8);
+ backdrop-filter: blur(20px);
display: flex;
align-items: center;
- box-shadow: black 0 0 20px;
+ box-shadow: 0 4px 20px rgba($color-dark-primary, 0.1);
+ position: relative;
+ z-index: 1;
.chat-header-avatar {
width: 45px;
@@ -148,8 +169,10 @@
.chat-messages {
flex: 1;
- padding: 1rem;
+ padding: 10px 20px;
overflow-y: auto;
+ position: relative;
+ z-index: 1;
&::-webkit-scrollbar {
width: 7px;
@@ -337,9 +360,47 @@
}
&.received .message-inner {
- background-color: $color-dark-surface-container;
+ background: $color-dark-surface-container;
+ color: $color-dark-on-surface;
border-top-left-radius: 5px;
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ border: 1px solid rgba($color-dark-outline-variant, 0.4);
+ position: relative;
+ overflow: hidden;
+
+ &::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: linear-gradient(135deg, rgba($color-dark-primary, 0.05), rgba($color-dark-tertiary, 0.03));
+ pointer-events: none;
+ z-index: 0;
+ }
+
+ > * {
+ position: relative;
+ z-index: 1;
+ }
+ }
+
+ &.received .message-time {
+ color: $color-dark-on-surface-variant;
+ font-weight: 500;
+
+ .message-status-indicator {
+ .success-icon {
+ color: $color-dark-on-surface-variant;
+ filter: brightness(1.1);
+ }
+
+ .error-icon {
+ color: #ff6b6b;
+ filter: brightness(1.1);
+ }
+ }
}
&.sent {
@@ -347,13 +408,47 @@
flex-direction: row-reverse;
.message-inner {
- background-color: $color-dark-primary-container;
- color: $color-dark-on-primary-container;
+ background: linear-gradient(135deg, color.adjust($color-dark-primary, $lightness: 8%), color.adjust($color-dark-primary-container, $lightness: 5%));
+ color: $color-dark-on-primary;
border-top-right-radius: 5px;
+ box-shadow: 0 0 20px rgba($color-dark-primary, 0.4);
+ border: 1px solid rgba($color-dark-primary, 0.5);
+ position: relative;
+ overflow: hidden;
+
+ &::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.08));
+ pointer-events: none;
+ z-index: 0;
+ }
+
+ > * {
+ position: relative;
+ z-index: 1;
+ }
}
.message-time {
- color: $color-dark-on-primary-container;
+ color: $color-dark-on-primary;
+ font-weight: 500;
+
+ .message-status-indicator {
+ .success-icon {
+ color: $color-dark-on-primary;
+ filter: brightness(1.2);
+ }
+
+ .error-icon {
+ color: #ff6b6b;
+ filter: brightness(1.2);
+ }
+ }
}
}
}
@@ -401,30 +496,15 @@
.chat-input-wrapper {
position: relative;
- margin: 0 20px 20px 20px;
-
- &::before {
- $height: 20px;
-
- content: '';
- position: absolute;
- top: -$height;
- left: 0;
- right: 0;
- width: 100%;
- height: $height;
- background: linear-gradient(
- 0deg,
- $color-dark-surface,
- rgba(255, 255, 255, 0),
- );
- }
+ margin: 0 10px 10px 10px;
.input-group {
display: flex;
- background-color: $color-dark-surface-container;
+ background: $color-dark-surface-container;
border-radius: 30px;
flex-direction: column;
+ border: 1px solid rgba($color-dark-outline-variant, 0.4);
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
.contextual-preview {
padding: 12px 16px 0 16px;
@@ -467,11 +547,17 @@
outline: none;
caret-color: $color-dark-primary;
color: $color-dark-on-surface;
+ background: transparent;
resize: none;
font: inherit;
font-size: 13pt;
height: 100%;
width: 100%;
+
+ &::placeholder {
+ color: $color-dark-on-surface-variant;
+ opacity: 0.7;
+ }
}
.buttons {
@@ -485,17 +571,21 @@
width: 50px;
height: 50px;
border-radius: 50%;
- background-color: $color-dark-primary;
+ background: linear-gradient(135deg, color.adjust($color-dark-primary, $lightness: 8%), color.adjust($color-dark-primary-container, $lightness: 5%));
color: $color-dark-on-primary;
- border: none;
+ border: 1px solid rgba($color-dark-primary, 0.5);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
- transition: background-color 0.25s ease;
+ transition: all 0.25s ease;
align-self: flex-end;
+ box-shadow: 0 0 20px rgba($color-dark-primary, 0.4);
- @include hoverStateLayer($background: $color-dark-primary);
+ &:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 0 30px rgba($color-dark-primary, 0.6);
+ }
}
}
}
@@ -505,6 +595,47 @@
}
}
+// ChatHeader component styles
+.chat-header-left {
+ .product-name {
+ font-size: 1.8rem;
+ font-weight: 700;
+ background: linear-gradient(45deg, $color-dark-primary, $color-dark-tertiary);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ text-shadow: 0 0 20px rgba($color-dark-primary, 0.5);
+ }
+
+ .profile {
+ a {
+ display: flex;
+ align-items: center;
+ text-decoration: none;
+ transition: transform 0.3s ease;
+
+ &:hover {
+ transform: scale(1.05);
+ }
+
+ img {
+ width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ object-fit: cover;
+ border: 2px solid rgba($color-dark-primary, 0.4);
+ box-shadow: 0 0 15px rgba($color-dark-primary, 0.3);
+ transition: all 0.3s ease;
+
+ &:hover {
+ box-shadow: 0 0 25px rgba($color-dark-primary, 0.5);
+ border-color: rgba($color-dark-primary, 0.6);
+ }
+ }
+ }
+ }
+}
+
.message-profile-pic {
img {
width: 40px;