mirror of
https://github.com/fromchat-messenger/web.git
synced 2026-09-23 03:25:07 +03:00
Redesign the download table, finish download dialog
This commit is contained in:
@@ -1,113 +0,0 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { StyledDialog } from "./StyledDialog";
|
||||
import { MaterialButton, MaterialIcon } from "@/utils/material";
|
||||
import { OS_CONFIG, type DownloadOs } from "@/core/downloads/os";
|
||||
import styles from "./css/download-dialog.module.scss";
|
||||
|
||||
interface DownloadDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
os: DownloadOs;
|
||||
}
|
||||
|
||||
function AndroidInstructions(): ReactNode {
|
||||
return (
|
||||
<div className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>Установка на Android</h3>
|
||||
<p className={styles.text}>
|
||||
Вы скачали APK-файл FromChat. Чтобы установить приложение:
|
||||
</p>
|
||||
<ul className={styles.list}>
|
||||
<li>Откройте загруженный APK-файл из шторки уведомлений или файлового менеджера.</li>
|
||||
<li>
|
||||
Если появится запрос "Разрешить установку из неизвестных источников" — дайте
|
||||
разрешение для браузера, из которого вы скачивали APK.
|
||||
</li>
|
||||
<li>
|
||||
Google Play Protect может предупредить о неизвестном приложении. Если вы доверяете FromChat,
|
||||
нажмите "Подробнее" → "Всё равно установить" (или аналогичную кнопку).
|
||||
</li>
|
||||
<li>Дождитесь завершения установки и откройте FromChat из списка приложений.</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function IosInstructions(): ReactNode {
|
||||
return (
|
||||
<div className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>Установка на iOS</h3>
|
||||
<p className={styles.text}>
|
||||
Эта сборка не распространяется через App Store или TestFlight. Чтобы установить FromChat на iPhone
|
||||
или iPad, потребуется один из вариантов сторонней установки:
|
||||
</p>
|
||||
<ul className={styles.list}>
|
||||
<li>
|
||||
<strong>TrollStore</strong>: постоянная установка приложений из IPA-файлов. Требуется поддерживаемая
|
||||
версия iOS и настройка TrollStore на устройстве.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Джейлбрейк</strong>: установка через менеджер пакетов (Sileo, Cydia и т.п.) или напрямую
|
||||
из файлового менеджера, если у вас уже есть джейлбрейк.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Другие сервисы сайдлоада</strong>: сторонние инструменты, которые подписывают IPA-файл
|
||||
вашим сертификатом разработчика или временным сертификатом.
|
||||
</li>
|
||||
</ul>
|
||||
<p className={styles.text}>
|
||||
К сожалению, простого и официально поддерживаемого пути установки для iOS здесь нет — именно поэтому я
|
||||
бы сам iPhone не покупал 😄
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderInstructions(os: DownloadOs): ReactNode {
|
||||
if (os === "android") {
|
||||
return <AndroidInstructions />;
|
||||
}
|
||||
|
||||
if (os === "ios") {
|
||||
return <IosInstructions />;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function DownloadDialog({ open, onOpenChange, os }: DownloadDialogProps) {
|
||||
const osInfo = OS_CONFIG[os];
|
||||
|
||||
return (
|
||||
<StyledDialog
|
||||
open={open}
|
||||
onOpenChange={onOpenChange}
|
||||
className={styles.downloadDialog}
|
||||
contentClassName={styles.downloadDialogContent}
|
||||
afterChildren={
|
||||
<div className={styles.actions}>
|
||||
<MaterialButton variant="filled" onClick={() => onOpenChange(false)}>
|
||||
Закрыть
|
||||
</MaterialButton>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className={styles.body}>
|
||||
<div className={styles.header}>
|
||||
<div className={styles.iconWrapper}>
|
||||
<MaterialIcon name="download" className={styles.icon} />
|
||||
</div>
|
||||
<div className={styles.titleBlock}>
|
||||
<h2 className={styles.title}>Thanks for downloading</h2>
|
||||
<p className={styles.subtitle}>
|
||||
FromChat для
|
||||
<span className={styles.osName}>{osInfo.label}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{renderInstructions(os)}
|
||||
</div>
|
||||
</StyledDialog>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ export function SplitButton({
|
||||
top?: number;
|
||||
bottom?: number;
|
||||
left: number;
|
||||
transform: string;
|
||||
maxHeight: number;
|
||||
} | null>(null);
|
||||
const { width: windowWidth, height: windowHeight } = useWindowSize();
|
||||
@@ -58,9 +57,8 @@ export function SplitButton({
|
||||
const menuWidth = menuEl?.offsetWidth ?? 220;
|
||||
const menuHeight = menuEl?.offsetHeight ?? 320;
|
||||
|
||||
const anchorX = rect.left + rect.width / 2;
|
||||
const anchorCenterX = rect.left + rect.width / 2;
|
||||
let left: number;
|
||||
let transform: string;
|
||||
let top: number | undefined;
|
||||
let bottom: number | undefined;
|
||||
let maxHeight: number;
|
||||
@@ -82,18 +80,15 @@ export function SplitButton({
|
||||
maxHeight = Math.max(100, availableBelow);
|
||||
}
|
||||
|
||||
if (anchorX - menuWidth / 2 < EDGE_PAD) {
|
||||
if (anchorCenterX - menuWidth / 2 < EDGE_PAD) {
|
||||
left = EDGE_PAD;
|
||||
transform = "none";
|
||||
} else if (anchorX + menuWidth / 2 > vw - EDGE_PAD) {
|
||||
} else if (anchorCenterX + menuWidth / 2 > vw - EDGE_PAD) {
|
||||
left = vw - menuWidth - EDGE_PAD;
|
||||
transform = "none";
|
||||
} else {
|
||||
left = anchorX;
|
||||
transform = "translateX(-50%)";
|
||||
left = anchorCenterX - menuWidth / 2;
|
||||
}
|
||||
|
||||
setMenuPosition({ top, bottom, left, transform, maxHeight });
|
||||
setMenuPosition({ top, bottom, left, maxHeight });
|
||||
}, []);
|
||||
|
||||
const closeMenu = useCallback(() => {
|
||||
@@ -245,7 +240,6 @@ export function SplitButton({
|
||||
? { bottom: menuPosition.bottom }
|
||||
: { top: menuPosition.top }),
|
||||
left: menuPosition.left,
|
||||
transform: menuPosition.transform,
|
||||
maxHeight: menuPosition.maxHeight,
|
||||
overflowY: "auto",
|
||||
}}
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
@use "../../../css/material" as *;
|
||||
@use "../../../css/colors" as *;
|
||||
@use "sass:color";
|
||||
|
||||
.downloadDialog {
|
||||
color: $color-dark-on-surface;
|
||||
}
|
||||
|
||||
.downloadDialogContent {
|
||||
padding: 24px 24px 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.iconWrapper {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: $color-dark-primary-container;
|
||||
color: $color-dark-on-primary-container;
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.titleBlock {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
color: $color-dark-on-surface-variant;
|
||||
}
|
||||
|
||||
.osName {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.text {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
color: $color-dark-on-surface-variant;
|
||||
}
|
||||
|
||||
.list {
|
||||
margin: 0;
|
||||
padding-left: 20px;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
color: $color-dark-on-surface-variant;
|
||||
|
||||
li + li {
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
padding: 12px 24px 20px;
|
||||
border-top: 1px solid rgba($color-dark-outline-variant, 0.4);
|
||||
background-color: color.mix($color-dark-surface-container, $color-dark-surface-container-low, 60%);
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.downloadDialogContent {
|
||||
padding: 20px 16px 12px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
padding-inline: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,4 +224,8 @@ $menu-padding: 8px;
|
||||
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba($color-dark-on-surface, 0.25) transparent;
|
||||
|
||||
mdui-list {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user