Add "Download app" screen for too small screens, translate strings

# Please enter the commit message for your changes. Lines starting
This commit is contained in:
grabic21
2025-09-13 17:17:43 +04:00
committed by denis0001-dev
Unverified
parent 002a59d3c8
commit a7275f58e1
10 changed files with 88 additions and 15 deletions
+2
View File
@@ -28,3 +28,5 @@ export const API_WS_BASE_URL = `${location.host || BASE_DOMAIN}/api`;
* @constant
*/
export const PRODUCT_NAME = "FromChat";
export const MINIMUM_WIDTH = 800;
@@ -0,0 +1,8 @@
.download-app-screen {
display: flex;
justify-content: center;
align-items: center;
min-width: 100vw;
min-height: 100vh;
padding: 32px;
}
+1
View File
@@ -9,6 +9,7 @@
@use "common/material" as *;
@use "electron";
@use "dialogs/reply";
@use "download-app";
@use "lib/fonts/montserrat";
@use "lib/fonts/material-symbols";
+8
View File
@@ -1,5 +1,8 @@
import { MINIMUM_WIDTH } from "../core/config";
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";
@@ -7,12 +10,17 @@ import { useEffect } from "react";
export default function App() {
const { currentPage, restoreUserFromStorage } = useAppState();
const { width } = useWindowSize();
// Restore user from localStorage on app initialization
useEffect(() => {
restoreUserFromStorage();
}, [restoreUserFromStorage]);
if (width < MINIMUM_WIDTH) {
return <DownloadAppScreen />
}
let page = <LoginScreen />;
switch (currentPage) {
@@ -176,17 +176,17 @@ export function MessageContextMenu({
>
<div className="context-menu-item" onClick={() => handleAction("reply")}>
<span className="material-symbols">reply</span>
Reply
Ответить
</div>
{isAuthor && (
<>
<div className="context-menu-item" onClick={() => handleAction("edit")}>
<span className="material-symbols">edit</span>
Edit
Редактировать
</div>
<div className="context-menu-item" onClick={() => handleAction("delete")}>
<span className="material-symbols">delete</span>
Delete
Удалить
</div>
</>
)}
@@ -41,8 +41,8 @@ export function MessagePanelRenderer({ panel, isChatSwitching }: MessagePanelRen
// Cleanup function
return () => {
if (panel && (panel as any).onStateChange) {
(panel as any).onStateChange = null;
if (panel && panel.onStateChange) {
panel.onStateChange = null;
}
};
}, [panel]);
@@ -72,10 +72,10 @@ export function MessagePanelRenderer({ panel, isChatSwitching }: MessagePanelRen
<img src={defaultAvatar} alt="Avatar" className="chat-header-avatar" />
<div className="chat-header-info">
<div className="info-chat">
<h4 id="chat-name">Select a chat</h4>
<h4 id="chat-name">Выбор чата</h4>
<p>
<span className="online-status"></span>
Choose a chat to start messaging
Выберите чат, чтобы начать переписку
</p>
</div>
</div>
@@ -88,7 +88,7 @@ export function MessagePanelRenderer({ panel, isChatSwitching }: MessagePanelRen
height: "100%",
color: "var(--mdui-color-on-surface-variant)"
}}>
Select a chat from the sidebar to start messaging
Выберите чат на боковой панели, чтобы начать переписку
</div>
</div>
</div>
@@ -128,7 +128,7 @@ export function MessagePanelRenderer({ panel, isChatSwitching }: MessagePanelRen
height: "100%",
color: "var(--mdui-color-on-surface-variant)"
}}>
Loading messages...
Загрузка сообщений...
</div>
</div>
): (
@@ -36,7 +36,7 @@ export function ReplyMessageDialog({ isOpen, onOpenChange, replyToMessage, onSen
return (
<MaterialDialog open={isOpen} onOpenChange={onOpenChange} close-on-overlay-click close-on-esc className="reply-dialog">
<div className="dialog-content">
<h3>Reply to Message</h3>
<h3>Ответить на сообщение</h3>
<div className="reply-preview-dialog">
<div className="reply-content">
<span className="reply-username">{replyToMessage.username}</span>
@@ -28,24 +28,24 @@ export function UserProfileDialog({ isOpen, onOpenChange, userProfile }: UserPro
<div className={`online-status ${userProfile.online ? "online" : "offline"}`}>
{userProfile.online ? (
<>
<span className="online-indicator"></span> Online
<span className="online-indicator"></span> Онлайн
</>
) : (
<>
<span className="offline-indicator"></span> Last seen {formatTime(userProfile.last_seen)}
<span className="offline-indicator"></span> Последний заход {formatTime(userProfile.last_seen)}
</>
)}
</div>
</div>
<div className="bio-section">
<label>Bio:</label>
<label>О себе:</label>
<div className="bio-display">
{userProfile.bio || "No bio available."}
</div>
</div>
<div className="profile-stats">
<div className="stat">
<span className="stat-label">Member since:</span>
<span className="stat-label">Зарегистрирован:</span>
<span className="stat-value member-since">{formatTime(userProfile.created_at)}</span>
</div>
<div className="stat">
+29
View File
@@ -0,0 +1,29 @@
import { useEffect, useState } from "react";
export interface WindowSize {
width: number;
height: number;
}
export default function useWindowSize(): WindowSize {
const [width, setWidth] = useState(innerWidth);
const [height, setHeight] = useState(innerHeight);
useEffect(() => {
function listener() {
setWidth(innerWidth);
setHeight(innerHeight);
}
addEventListener("resize", listener);
return () => {
removeEventListener("resize", listener);
}
});
return {
width: width,
height: height
}
}
@@ -0,0 +1,25 @@
export default function DownloadAppScreen() {
return (
<div className="download-app-screen">
<div className="inner">
<h1>Чтобы пользоваться мессенджером, скачайте приложение</h1>
<p>
Этот сайт <b>не предназначен</b> для работы на маленьких экранах, поэтому
вам нужно скачать приложение мессенджера.
</p>
<a href="https://github.com/denis0001-dev/FromChat-android/releases/latest">
<mdui-button>Скачать на GitHub</mdui-button>
</a>
<p>
Если возникнут сложности или есть вопросы, нажмите кнопку!
</p>
<a href="https://t.me/denis0001-dev">
<mdui-button>Написать в поддержку</mdui-button>
</a>
</div>
</div>
)
}