mirror of
https://github.com/lobehub/lobehub.git
synced 2026-03-27 13:29:15 +07:00
✨ feat: add platform-aware download client menu option (#11676)
* ✨ feat: add platform-aware download client menu option * update test Signed-off-by: Innei <tukon479@gmail.com> --------- Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
@@ -179,6 +179,7 @@
|
||||
"delete": "حذف",
|
||||
"document": "دليل المستخدم",
|
||||
"download": "تنزيل",
|
||||
"downloadClient": "تنزيل العميل",
|
||||
"duplicate": "تكرار",
|
||||
"edit": "تعديل",
|
||||
"errors.invalidFileFormat": "تنسيق الملف غير صالح",
|
||||
|
||||
@@ -179,6 +179,7 @@
|
||||
"delete": "Изтрий",
|
||||
"document": "Ръководство за потребителя",
|
||||
"download": "Изтегли",
|
||||
"downloadClient": "Изтегли клиент",
|
||||
"duplicate": "Дублирай",
|
||||
"edit": "Редактирай",
|
||||
"errors.invalidFileFormat": "Невалиден файлов формат",
|
||||
|
||||
@@ -179,6 +179,7 @@
|
||||
"delete": "Löschen",
|
||||
"document": "Benutzerhandbuch",
|
||||
"download": "Herunterladen",
|
||||
"downloadClient": "Client herunterladen",
|
||||
"duplicate": "Duplizieren",
|
||||
"edit": "Bearbeiten",
|
||||
"errors.invalidFileFormat": "Ungültiges Dateiformat",
|
||||
|
||||
@@ -179,6 +179,7 @@
|
||||
"delete": "Delete",
|
||||
"document": "User Manual",
|
||||
"download": "Download",
|
||||
"downloadClient": "Download Client",
|
||||
"duplicate": "Duplicate",
|
||||
"edit": "Edit",
|
||||
"errors.invalidFileFormat": "Invalid file format",
|
||||
|
||||
@@ -179,6 +179,7 @@
|
||||
"delete": "Eliminar",
|
||||
"document": "Manual de usuario",
|
||||
"download": "Descargar",
|
||||
"downloadClient": "Descargar cliente",
|
||||
"duplicate": "Duplicar",
|
||||
"edit": "Editar",
|
||||
"errors.invalidFileFormat": "Formato de archivo no válido",
|
||||
|
||||
@@ -179,6 +179,7 @@
|
||||
"delete": "حذف",
|
||||
"document": "راهنمای کاربر",
|
||||
"download": "دانلود",
|
||||
"downloadClient": "دانلود کلاینت",
|
||||
"duplicate": "تکراری",
|
||||
"edit": "ویرایش",
|
||||
"errors.invalidFileFormat": "فرمت فایل نامعتبر است",
|
||||
|
||||
@@ -179,6 +179,7 @@
|
||||
"delete": "Supprimer",
|
||||
"document": "Manuel utilisateur",
|
||||
"download": "Télécharger",
|
||||
"downloadClient": "Télécharger le client",
|
||||
"duplicate": "Dupliquer",
|
||||
"edit": "Modifier",
|
||||
"errors.invalidFileFormat": "Format de fichier invalide",
|
||||
|
||||
@@ -179,6 +179,7 @@
|
||||
"delete": "Elimina",
|
||||
"document": "Manuale utente",
|
||||
"download": "Scarica",
|
||||
"downloadClient": "Scarica client",
|
||||
"duplicate": "Duplica",
|
||||
"edit": "Modifica",
|
||||
"errors.invalidFileFormat": "Formato file non valido",
|
||||
|
||||
@@ -179,6 +179,7 @@
|
||||
"delete": "削除",
|
||||
"document": "ドキュメント",
|
||||
"download": "ダウンロード",
|
||||
"downloadClient": "クライアントをダウンロード",
|
||||
"duplicate": "コピーを作成",
|
||||
"edit": "編集",
|
||||
"errors.invalidFileFormat": "ファイルフォーマットエラー",
|
||||
|
||||
@@ -179,6 +179,7 @@
|
||||
"delete": "삭제",
|
||||
"document": "사용 설명서",
|
||||
"download": "다운로드",
|
||||
"downloadClient": "클라이언트 다운로드",
|
||||
"duplicate": "복사본 만들기",
|
||||
"edit": "편집",
|
||||
"errors.invalidFileFormat": "파일 형식 오류",
|
||||
|
||||
@@ -179,6 +179,7 @@
|
||||
"delete": "Verwijderen",
|
||||
"document": "Gebruikershandleiding",
|
||||
"download": "Downloaden",
|
||||
"downloadClient": "Client downloaden",
|
||||
"duplicate": "Dupliceren",
|
||||
"edit": "Bewerken",
|
||||
"errors.invalidFileFormat": "Ongeldig bestandsformaat",
|
||||
|
||||
@@ -179,6 +179,7 @@
|
||||
"delete": "Usuń",
|
||||
"document": "Instrukcja obsługi",
|
||||
"download": "Pobierz",
|
||||
"downloadClient": "Pobierz klienta",
|
||||
"duplicate": "Duplikuj",
|
||||
"edit": "Edytuj",
|
||||
"errors.invalidFileFormat": "Nieprawidłowy format pliku",
|
||||
|
||||
@@ -179,6 +179,7 @@
|
||||
"delete": "Excluir",
|
||||
"document": "Manual do Usuário",
|
||||
"download": "Baixar",
|
||||
"downloadClient": "Baixar cliente",
|
||||
"duplicate": "Duplicar",
|
||||
"edit": "Editar",
|
||||
"errors.invalidFileFormat": "Formato de arquivo inválido",
|
||||
|
||||
@@ -179,6 +179,7 @@
|
||||
"delete": "Удалить",
|
||||
"document": "Руководство пользователя",
|
||||
"download": "Скачать",
|
||||
"downloadClient": "Скачать клиент",
|
||||
"duplicate": "Дублировать",
|
||||
"edit": "Редактировать",
|
||||
"errors.invalidFileFormat": "Недопустимый формат файла",
|
||||
|
||||
@@ -179,6 +179,7 @@
|
||||
"delete": "Sil",
|
||||
"document": "Kullanıcı Kılavuzu",
|
||||
"download": "İndir",
|
||||
"downloadClient": "İstemciyi indir",
|
||||
"duplicate": "Çoğalt",
|
||||
"edit": "Düzenle",
|
||||
"errors.invalidFileFormat": "Geçersiz dosya formatı",
|
||||
|
||||
@@ -179,6 +179,7 @@
|
||||
"delete": "Xóa",
|
||||
"document": "Hướng dẫn sử dụng",
|
||||
"download": "Tải xuống",
|
||||
"downloadClient": "Tải xuống client",
|
||||
"duplicate": "Nhân bản",
|
||||
"edit": "Chỉnh sửa",
|
||||
"errors.invalidFileFormat": "Định dạng tệp không hợp lệ",
|
||||
|
||||
@@ -179,6 +179,7 @@
|
||||
"delete": "删除",
|
||||
"document": "使用文档",
|
||||
"download": "下载",
|
||||
"downloadClient": "下载客户端",
|
||||
"duplicate": "创建副本",
|
||||
"edit": "编辑",
|
||||
"errors.invalidFileFormat": "文件格式错误",
|
||||
|
||||
@@ -179,6 +179,7 @@
|
||||
"delete": "刪除",
|
||||
"document": "使用說明文件",
|
||||
"download": "下載",
|
||||
"downloadClient": "下載客戶端",
|
||||
"duplicate": "建立副本",
|
||||
"edit": "編輯",
|
||||
"errors.invalidFileFormat": "檔案格式錯誤",
|
||||
|
||||
@@ -63,3 +63,9 @@ export const AES_GCM_URL = 'https://datatracker.ietf.org/doc/html/draft-ietf-avt
|
||||
export const BASE_PROVIDER_DOC_URL = 'https://lobehub.com/docs/usage/providers';
|
||||
export const SITEMAP_BASE_URL = isDev ? '/sitemap.xml/' : 'sitemap';
|
||||
export const CHANGELOG_URL = urlJoin(OFFICIAL_SITE, 'changelog/versions');
|
||||
|
||||
export const DOWNLOAD_URL = {
|
||||
android: 'https://play.google.com/store/apps/details?id=com.lobehub.app',
|
||||
default: urlJoin(OFFICIAL_SITE, '/download'),
|
||||
ios: 'https://testflight.apple.com/join/2ZbjX4Qp',
|
||||
} as const;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { UTM_SOURCE , LOBE_CHAT_CLOUD } from '@lobechat/business-const';
|
||||
import { OFFICIAL_URL } from '@lobechat/const';
|
||||
import { LOBE_CHAT_CLOUD, UTM_SOURCE } from '@lobechat/business-const';
|
||||
import { DOWNLOAD_URL, OFFICIAL_URL } from '@lobechat/const';
|
||||
import {
|
||||
Book,
|
||||
CircleUserRound,
|
||||
@@ -9,22 +9,29 @@ import {
|
||||
FileClockIcon,
|
||||
Settings2,
|
||||
} from 'lucide-react';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { type CellProps } from '@/components/Cell';
|
||||
import { DOCUMENTS, FEEDBACK } from '@/const/index';
|
||||
import { usePWAInstall } from '@/hooks/usePWAInstall';
|
||||
import { usePlatform } from '@/hooks/usePlatform';
|
||||
import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
|
||||
import { useUserStore } from '@/store/user';
|
||||
import { authSelectors } from '@/store/user/selectors';
|
||||
|
||||
export const useCategory = (onOpenChangelogModal: () => void) => {
|
||||
const navigate = useNavigate();
|
||||
const { canInstall, install } = usePWAInstall();
|
||||
const { t } = useTranslation(['common', 'setting', 'auth']);
|
||||
const { showCloudPromotion, hideDocs } = useServerConfigStore(featureFlagsSelectors);
|
||||
const [isLoginWithAuth] = useUserStore((s) => [authSelectors.isLoginWithAuth(s)]);
|
||||
const { isIOS, isAndroid } = usePlatform();
|
||||
|
||||
const downloadUrl = useMemo(() => {
|
||||
if (isIOS) return DOWNLOAD_URL.ios;
|
||||
if (isAndroid) return DOWNLOAD_URL.android;
|
||||
return DOWNLOAD_URL.default;
|
||||
}, [isIOS, isAndroid]);
|
||||
|
||||
const profile: CellProps[] = [
|
||||
{
|
||||
@@ -47,12 +54,12 @@ export const useCategory = (onOpenChangelogModal: () => void) => {
|
||||
},
|
||||
];
|
||||
|
||||
const pwa: CellProps[] = [
|
||||
const downloadClient: CellProps[] = [
|
||||
{
|
||||
icon: Download,
|
||||
key: 'pwa',
|
||||
label: t('installPWA'),
|
||||
onClick: () => install(),
|
||||
key: 'download-client',
|
||||
label: t('downloadClient'),
|
||||
onClick: () => window.open(downloadUrl, '__blank'),
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
@@ -96,7 +103,7 @@ export const useCategory = (onOpenChangelogModal: () => void) => {
|
||||
/* ↓ cloud slot ↓ */
|
||||
|
||||
/* ↑ cloud slot ↑ */
|
||||
...(canInstall ? pwa : []),
|
||||
...downloadClient,
|
||||
...(!hideDocs ? helps : []),
|
||||
].filter(Boolean) as CellProps[];
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import BusinessGlobalProvider from '@/business/client/BusinessGlobalProvider';
|
||||
import Analytics from '@/components/Analytics';
|
||||
import { DEFAULT_LANG } from '@/const/locale';
|
||||
import { isDesktop } from '@/const/version';
|
||||
import PWAInstall from '@/features/PWAInstall';
|
||||
import AuthProvider from '@/layout/AuthProvider';
|
||||
import GlobalProvider from '@/layout/GlobalProvider';
|
||||
import { type Locales } from '@/locales/resources';
|
||||
@@ -40,9 +39,6 @@ const RootLayout = async ({ children, params }: RootLayoutProps) => {
|
||||
variants={variants}
|
||||
>
|
||||
<AuthProvider>{children}</AuthProvider>
|
||||
<Suspense fallback={null}>
|
||||
<PWAInstall />
|
||||
</Suspense>
|
||||
</GlobalProvider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { LOBE_CHAT_CLOUD, UTM_SOURCE } from '@lobechat/business-const';
|
||||
import { isDesktop } from '@lobechat/const';
|
||||
import { DOWNLOAD_URL, isDesktop } from '@lobechat/const';
|
||||
import { Flexbox, Hotkey, Icon, Tag } from '@lobehub/ui';
|
||||
import { type ItemType } from 'antd/es/menu/interface';
|
||||
import { Cloudy, Download, HardDriveDownload, LogOut, Settings2 } from 'lucide-react';
|
||||
import { type PropsWithChildren, memo } from 'react';
|
||||
import { type PropsWithChildren, memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
@@ -12,7 +12,7 @@ import type { MenuProps } from '@/components/Menu';
|
||||
import { DEFAULT_DESKTOP_HOTKEY_CONFIG } from '@/const/desktop';
|
||||
import { OFFICIAL_URL } from '@/const/url';
|
||||
import DataImporter from '@/features/DataImporter';
|
||||
import { usePWAInstall } from '@/hooks/usePWAInstall';
|
||||
import { usePlatform } from '@/hooks/usePlatform';
|
||||
import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
|
||||
import { useUserStore } from '@/store/user';
|
||||
import { authSelectors } from '@/store/user/selectors';
|
||||
@@ -44,7 +44,6 @@ const NewVersionBadge = memo(
|
||||
);
|
||||
|
||||
export const useMenu = () => {
|
||||
const { canInstall, install } = usePWAInstall();
|
||||
const hasNewVersion = useNewVersion();
|
||||
const { t } = useTranslation(['common', 'setting', 'auth']);
|
||||
const { showCloudPromotion, hideDocs } = useServerConfigStore(featureFlagsSelectors);
|
||||
@@ -53,6 +52,13 @@ export const useMenu = () => {
|
||||
authSelectors.isLoginWithAuth(s),
|
||||
]);
|
||||
const businessMenuItems = useBusinessMenuItems(isLogin);
|
||||
const { isIOS, isAndroid } = usePlatform();
|
||||
|
||||
const downloadUrl = useMemo(() => {
|
||||
if (isIOS) return DOWNLOAD_URL.ios;
|
||||
if (isAndroid) return DOWNLOAD_URL.android;
|
||||
return DOWNLOAD_URL.default;
|
||||
}, [isIOS, isAndroid]);
|
||||
|
||||
const settings: MenuProps['items'] = [
|
||||
{
|
||||
@@ -71,12 +77,15 @@ export const useMenu = () => {
|
||||
},
|
||||
];
|
||||
|
||||
const pwa: MenuProps['items'] = [
|
||||
const downloadClient: MenuProps['items'] = [
|
||||
{
|
||||
icon: <Icon icon={Download} />,
|
||||
key: 'pwa',
|
||||
label: t('installPWA'),
|
||||
onClick: () => install(),
|
||||
key: 'download-client',
|
||||
label: (
|
||||
<a href={downloadUrl} rel="noopener noreferrer" target="_blank">
|
||||
{t('downloadClient')}
|
||||
</a>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
@@ -119,7 +128,7 @@ export const useMenu = () => {
|
||||
|
||||
...(isLogin ? settings : []),
|
||||
...businessMenuItems,
|
||||
...(canInstall ? pwa : []),
|
||||
...(!isDesktop ? downloadClient : []),
|
||||
...data,
|
||||
...(!hideDocs ? helps : []),
|
||||
].filter(Boolean) as MenuProps['items'];
|
||||
|
||||
@@ -25,6 +25,7 @@ describe('usePlatform', () => {
|
||||
const { result } = renderHook(() => usePlatform());
|
||||
|
||||
expect(result.current).toEqual({
|
||||
isAndroid: false,
|
||||
isApple: true,
|
||||
isChrome: true,
|
||||
isChromium: true,
|
||||
@@ -50,6 +51,7 @@ describe('usePlatform', () => {
|
||||
const { result } = renderHook(() => usePlatform());
|
||||
|
||||
expect(result.current).toEqual({
|
||||
isAndroid: false,
|
||||
isApple: true,
|
||||
isChrome: false,
|
||||
isChromium: false,
|
||||
@@ -75,6 +77,7 @@ describe('usePlatform', () => {
|
||||
const { result } = renderHook(() => usePlatform());
|
||||
|
||||
expect(result.current).toEqual({
|
||||
isAndroid: false,
|
||||
isApple: false,
|
||||
isChrome: false,
|
||||
isChromium: true,
|
||||
@@ -100,6 +103,7 @@ describe('usePlatform', () => {
|
||||
const { result } = renderHook(() => usePlatform());
|
||||
|
||||
expect(result.current).toEqual({
|
||||
isAndroid: false,
|
||||
isApple: false,
|
||||
isChrome: false,
|
||||
isChromium: false,
|
||||
@@ -125,6 +129,7 @@ describe('usePlatform', () => {
|
||||
const { result } = renderHook(() => usePlatform());
|
||||
|
||||
expect(result.current).toEqual({
|
||||
isAndroid: false,
|
||||
isApple: true,
|
||||
isChrome: true,
|
||||
isChromium: true,
|
||||
|
||||
@@ -13,6 +13,7 @@ export const usePlatform = () => {
|
||||
const browser = useRef(getBrowser());
|
||||
|
||||
const platformInfo = {
|
||||
isAndroid: platform.current?.toLowerCase() === 'android',
|
||||
isApple: platform.current && ['mac os', 'ios'].includes(platform.current?.toLowerCase()),
|
||||
isArc: isArc(),
|
||||
isChrome: browser.current?.toLowerCase() === 'chrome',
|
||||
|
||||
@@ -193,6 +193,7 @@ export default {
|
||||
'delete': 'Delete',
|
||||
'document': 'User Manual',
|
||||
'download': 'Download',
|
||||
'downloadClient': 'Download Client',
|
||||
'duplicate': 'Duplicate',
|
||||
'edit': 'Edit',
|
||||
'errors.invalidFileFormat': 'Invalid file format',
|
||||
|
||||
Reference in New Issue
Block a user