Improve mobile support

This commit is contained in:
2025-10-10 13:15:33 +03:00
Unverified
parent 334b7c0f3a
commit f472f3882a
7 changed files with 92 additions and 105 deletions
+7 -28
View File
@@ -1,10 +1,7 @@
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { ElectronTitleBar } from "./Electron";
import { useAppState } from "./pages/chat/state";
import { useEffect, useState, lazy, Suspense } from "react";
import { isElectron } from "./core/electron/electron";
import { MINIMUM_WIDTH } from "./core/config";
import useWindowSize from "./core/hooks/useWindowSize";
import { useEffect, useState, lazy } from "react";
import ProtectedRoute from "./pages/ProtectedRoute";
import NotFoundPage from "./pages/not-found/NotFoundPage";
import DownloadAppPage from "./pages/download-app/DownloadAppPage";
@@ -17,7 +14,6 @@ const ChatPage = lazy(() => import("./pages/chat/ui/ChatPage"));
export default function App() {
const { restoreUserFromStorage } = useAppState();
const { width } = useWindowSize();
const [authReady, setAuthReady] = useState(false);
// Restore user from localStorage on app initialization
@@ -29,28 +25,13 @@ export default function App() {
return authReady && (
<BrowserRouter>
{!isElectron && width < MINIMUM_WIDTH && <Navigate to="/download-app" replace />}
<ElectronTitleBar />
<div id="main-wrapper">
<Routes>
<Route path="/" element={
<Suspense>
<HomePage />
</Suspense>
} />
<Route path="/login" element={
<Suspense>
<LoginPage />
</Suspense>
} />
<Route path="/register" element={
<Suspense>
<RegisterPage />
</Suspense>
} />
<Route path="/download-app" element={
<DownloadAppPage />
} />
<Route path="/" element={<HomePage />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/register" element={<RegisterPage />} />
<Route path="/download-app" element={<DownloadAppPage />} />
<Route path="/">
<Route path="chat" element={
<ProtectedRoute>
@@ -58,9 +39,7 @@ export default function App() {
</ProtectedRoute>
} />
</Route>
<Route path="*" element={
<NotFoundPage />
} />
<Route path="*" element={<NotFoundPage />} />
</Routes>
</div>
</BrowserRouter>
@@ -0,0 +1,13 @@
import { Navigate } from "react-router-dom";
import { MINIMUM_WIDTH } from "../config";
import useWindowSize from "./useWindowSize";
export default function useDownloadAppScreen() {
const { width } = useWindowSize();
const isMobile = width < MINIMUM_WIDTH;
return {
isMobile,
navigate: isMobile ? <Navigate to="/download-app" replace /> : null
};
}
+3
View File
@@ -12,11 +12,14 @@ import { initialize, isSupported, startElectronReceiver, subscribe } from "@/cor
import { isElectron } from "@/core/electron/electron";
import { useNavigate } from "react-router-dom";
import "./auth.scss";
import useDownloadAppScreen from "@/core/hooks/useDownloadAppScreen";
export default function LoginPage() {
const [alerts, updateAlerts] = useImmer<Alert[]>([]);
const setUser = useAppState(state => state.setUser);
const navigate = useNavigate();
const { navigate: navigateDownloadApp } = useDownloadAppScreen();
if (navigateDownloadApp) return navigateDownloadApp;
function showAlert(type: AlertType, message: string) {
updateAlerts((alerts) => { alerts.push({type: type, message: message}) });
+3
View File
@@ -10,11 +10,14 @@ import { MaterialTextField } from "@/core/components/TextField";
import { ensureKeysOnLogin } from "@/core/api/authApi";
import { useNavigate } from "react-router-dom";
import "./auth.scss";
import useDownloadAppScreen from "@/core/hooks/useDownloadAppScreen";
export default function RegisterPage() {
const [alerts, updateAlerts] = useImmer<Alert[]>([]);
const setUser = useAppState(state => state.setUser);
const navigate = useNavigate();
const { navigate: navigateDownloadApp } = useDownloadAppScreen();
if (navigateDownloadApp) return navigateDownloadApp;
function showAlert(type: AlertType, message: string) {
updateAlerts((alerts) => { alerts.push({type: type, message: message}) });
@@ -84,7 +84,6 @@
}
.context-menu {
position: fixed;
background: $color-dark-surface;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
@@ -269,24 +268,3 @@
transform: rotate(90deg);
}
}
// Mobile responsive
@media (max-width: 768px) {
.reaction-bar {
padding: 6px;
}
.reaction-emoji-button,
.reaction-expand-button {
width: 28px;
height: 28px;
}
.reaction-emoji-button {
font-size: 16px;
}
.reaction-expand-button .material-symbols {
font-size: 16px;
}
}
+4
View File
@@ -1,8 +1,12 @@
import { LeftPanel } from "./left/LeftPanel";
import { RightPanel } from "./right/RightPanel";
import "@/pages/chat/css/chat.scss";
import useDownloadAppScreen from "@/core/hooks/useDownloadAppScreen";
export default function ChatPage() {
const { navigate: navigateDownloadApp } = useDownloadAppScreen();
if (navigateDownloadApp) return navigateDownloadApp;
return (
<div id="chat-interface">
<div className="all-container">
+62 -55
View File
@@ -1,7 +1,7 @@
import { Navigate, useNavigate } from "react-router-dom";
import { useNavigate } from "react-router-dom";
import { useAppState } from "@/pages/chat/state";
import { isElectron } from "@/core/electron/electron";
import "./home.scss";
import useDownloadAppScreen from "@/core/hooks/useDownloadAppScreen";
function GitHubLink({ children }: { children: React.ReactNode }) {
return (
@@ -18,18 +18,24 @@ function SupportLink({ children }: { children: React.ReactNode }) {
export default function HomePage() {
const navigate = useNavigate();
const { user } = useAppState();
const { isMobile } = useDownloadAppScreen();
const isLoggedIn = user.authToken && user.currentUser;
const handleGetStarted = () => {
if (user.authToken && user.currentUser) {
function handleGetStarted() {
if (isMobile) {
navigate("/download-app");
} else if (isLoggedIn) {
navigate("/chat");
} else {
navigate("/login");
}
}
if (isElectron) {
return <Navigate to="/chat" />;
}
const openBtn = (
<mdui-button variant="filled" onClick={handleGetStarted}>
{isMobile ? "Скачать приложение" : isLoggedIn ? "Перейти в чат" : "Войти"}
</mdui-button>
);
return (
<div className="homepage">
@@ -48,15 +54,7 @@ export default function HomePage() {
<mdui-button variant="text">Поддержка</mdui-button>
</SupportLink>
{user.authToken ? (
<mdui-button variant="filled" onClick={() => navigate("/chat")}>
Перейти в чат
</mdui-button>
) : (
<mdui-button variant="filled" onClick={() => navigate("/login")}>
Войти
</mdui-button>
)}
{openBtn}
</nav>
</div>
</div>
@@ -74,18 +72,13 @@ export default function HomePage() {
поддержкой файлов и уведомлений. Создан для тех, кто ценит приватность и свободу.
</p>
<div className="hero-actions">
<mdui-button
variant="filled"
onClick={handleGetStarted}
>
{user.authToken ? "Перейти в чат" : "Начать общение"}
</mdui-button>
<mdui-button
{openBtn}
{!isMobile && <mdui-button
variant="outlined"
onClick={() => navigate("/register")}
>
Зарегистрироваться
</mdui-button>
</mdui-button>}
</div>
</div>
<div className="hero-visual">
@@ -129,7 +122,7 @@ export default function HomePage() {
<div className="features-grid">
<div className="feature-card">
<div className="feature-icon">
<mdui-icon name="security"></mdui-icon>
<mdui-icon name="security" />
</div>
<h4>End-to-End Шифрование</h4>
<p>
@@ -140,7 +133,7 @@ export default function HomePage() {
<div className="feature-card">
<div className="feature-icon">
<mdui-icon name="code"></mdui-icon>
<mdui-icon name="code" />
</div>
<h4>100% открытый код</h4>
<p>
@@ -151,7 +144,7 @@ export default function HomePage() {
<div className="feature-card">
<div className="feature-icon">
<mdui-icon name="attach_file"></mdui-icon>
<mdui-icon name="attach_file" />
</div>
<h4>Обмен Файлами</h4>
<p>
@@ -162,7 +155,7 @@ export default function HomePage() {
<div className="feature-card">
<div className="feature-icon">
<mdui-icon name="notifications"></mdui-icon>
<mdui-icon name="notifications" />
</div>
<h4>Уведомления</h4>
<p>
@@ -173,7 +166,7 @@ export default function HomePage() {
<div className="feature-card">
<div className="feature-icon">
<mdui-icon name="edit"></mdui-icon>
<mdui-icon name="edit" />
</div>
<h4>Редактирование</h4>
<p>
@@ -184,7 +177,7 @@ export default function HomePage() {
<div className="feature-card">
<div className="feature-icon">
<mdui-icon name="computer"></mdui-icon>
<mdui-icon name="computer" />
</div>
<h4>Кроссплатформенность</h4>
<p>
@@ -205,20 +198,28 @@ export default function HomePage() {
уведомлений и автономной работы.
</p>
<div className="download-buttons">
<a
href="https://github.com/Toolbox-io/FromChat/actions/workflows/build.yml"
target="_blank"
rel="noopener noreferrer"
>
<mdui-button variant="filled">
<mdui-icon name="download" slot="icon"></mdui-icon>
Скачать для ПК
{!isMobile ? (
<>
<a
href="https://github.com/Toolbox-io/FromChat/actions/workflows/build.yml"
target="_blank"
rel="noopener noreferrer"
>
<mdui-button variant="filled">
<mdui-icon name="download" slot="icon" />
Скачать для ПК
</mdui-button>
</a>
<mdui-button variant="outlined" onClick={() => navigate("/login")}>
<mdui-icon name="language" slot="icon" />
Веб-версия
</mdui-button>
</>
) : (
<mdui-button variant="filled" onClick={() => navigate("/download-app")}>
Скачать приложение
</mdui-button>
</a>
<mdui-button variant="outlined" onClick={() => navigate("/login")}>
<mdui-icon name="language" slot="icon"></mdui-icon>
Веб-версия
</mdui-button>
)}
</div>
</div>
</div>
@@ -232,18 +233,24 @@ export default function HomePage() {
Присоединяйтесь к FromChat и общайтесь безопасно с друзьями и коллегами.
</p>
<div className="cta-actions">
<mdui-button
variant="filled"
onClick={() => navigate("/register")}
>
Создать аккаунт
</mdui-button>
<mdui-button
variant="outlined"
onClick={() => navigate("/login")}
>
Войти
</mdui-button>
{isMobile ? (
<mdui-button variant="filled" onClick={() => navigate("/download-app")}>
Скачать приложение
</mdui-button>
) : (
<>
<mdui-button
variant="filled"
onClick={() => navigate("/register")}>
Создать аккаунт
</mdui-button>
<mdui-button
variant="outlined"
onClick={() => navigate("/login")}>
Войти
</mdui-button>
</>
)}
</div>
</div>
</div>