mirror of
https://github.com/lobehub/lobe-chat.git
synced 2026-06-21 06:29:59 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d74daf6c68 | |||
| 87f2fd91b7 | |||
| 093c24f119 | |||
| bcf8628087 | |||
| 0160fbde83 | |||
| 12b1d56e33 | |||
| 8e3d3dbb1b | |||
| 55abddc532 | |||
| bae270e7da |
@@ -2,6 +2,56 @@
|
||||
|
||||
# Changelog
|
||||
|
||||
## [Version 2.0.0-next.335](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.334...v2.0.0-next.335)
|
||||
|
||||
<sup>Released on **2026-01-22**</sup>
|
||||
|
||||
#### ✨ Features
|
||||
|
||||
- **database**: Added user memory activity.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### What's improved
|
||||
|
||||
- **database**: Added user memory activity, closes [#11680](https://github.com/lobehub/lobe-chat/issues/11680) ([0160fbd](https://github.com/lobehub/lobe-chat/commit/0160fbd))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
## [Version 2.0.0-next.334](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.333...v2.0.0-next.334)
|
||||
|
||||
<sup>Released on **2026-01-21**</sup>
|
||||
|
||||
#### ✨ Features
|
||||
|
||||
- **misc**: Add platform-aware download client menu option.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### What's improved
|
||||
|
||||
- **misc**: Add platform-aware download client menu option, closes [#11676](https://github.com/lobehub/lobe-chat/issues/11676) ([55abddc](https://github.com/lobehub/lobe-chat/commit/55abddc))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
## [Version 2.0.0-next.333](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.332...v2.0.0-next.333)
|
||||
|
||||
<sup>Released on **2026-01-21**</sup>
|
||||
|
||||
@@ -1,4 +1,24 @@
|
||||
[
|
||||
{
|
||||
"children": {},
|
||||
"date": "2026-01-22",
|
||||
"version": "2.0.0-next.335"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"features": ["Add platform-aware download client menu option."]
|
||||
},
|
||||
"date": "2026-01-21",
|
||||
"version": "2.0.0-next.334"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"features": ["Update the sandbox preinstall libs in sys role."],
|
||||
"fixes": ["Fix multi tasks no summary issue."]
|
||||
},
|
||||
"date": "2026-01-21",
|
||||
"version": "2.0.0-next.333"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Improve e2e server and complete i18n resources."]
|
||||
|
||||
@@ -1235,6 +1235,40 @@ table user_memories {
|
||||
}
|
||||
}
|
||||
|
||||
table user_memories_activities {
|
||||
id varchar(255) [pk, not null]
|
||||
user_id text
|
||||
user_memory_id varchar(255)
|
||||
metadata jsonb
|
||||
tags text[]
|
||||
type varchar(255) [not null]
|
||||
status varchar(255) [not null, default: 'pending']
|
||||
timezone varchar(255)
|
||||
starts_at "timestamp with time zone"
|
||||
ends_at "timestamp with time zone"
|
||||
associated_objects jsonb
|
||||
associated_subjects jsonb
|
||||
associated_locations jsonb
|
||||
notes text
|
||||
narrative text
|
||||
narrative_vector vector(1024)
|
||||
feedback text
|
||||
feedback_vector vector(1024)
|
||||
captured_at "timestamp with time zone" [not null, default: `now()`]
|
||||
accessed_at "timestamp with time zone" [not null, default: `now()`]
|
||||
created_at "timestamp with time zone" [not null, default: `now()`]
|
||||
updated_at "timestamp with time zone" [not null, default: `now()`]
|
||||
|
||||
indexes {
|
||||
narrative_vector [name: 'user_memories_activities_narrative_vector_index']
|
||||
feedback_vector [name: 'user_memories_activities_feedback_vector_index']
|
||||
type [name: 'user_memories_activities_type_index']
|
||||
user_id [name: 'user_memories_activities_user_id_index']
|
||||
user_memory_id [name: 'user_memories_activities_user_memory_id_index']
|
||||
status [name: 'user_memories_activities_status_index']
|
||||
}
|
||||
}
|
||||
|
||||
table user_memories_contexts {
|
||||
id varchar(255) [pk, not null]
|
||||
user_id text
|
||||
|
||||
@@ -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": "檔案格式錯誤",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@lobehub/lobehub",
|
||||
"version": "2.0.0-next.333",
|
||||
"version": "2.0.0-next.335",
|
||||
"description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
||||
"keywords": [
|
||||
"framework",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
CREATE TABLE IF NOT EXISTS "user_memories_activities" (
|
||||
"id" varchar(255) PRIMARY KEY NOT NULL,
|
||||
"user_id" text,
|
||||
"user_memory_id" varchar(255),
|
||||
"metadata" jsonb,
|
||||
"tags" text[],
|
||||
"type" varchar(255) NOT NULL,
|
||||
"status" varchar(255) DEFAULT 'pending' NOT NULL,
|
||||
"timezone" varchar(255),
|
||||
"starts_at" timestamp with time zone,
|
||||
"ends_at" timestamp with time zone,
|
||||
"associated_objects" jsonb,
|
||||
"associated_subjects" jsonb,
|
||||
"associated_locations" jsonb,
|
||||
"notes" text,
|
||||
"narrative" text,
|
||||
"narrative_vector" vector(1024),
|
||||
"feedback" text,
|
||||
"feedback_vector" vector(1024),
|
||||
"captured_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"accessed_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "user_memories_activities" DROP CONSTRAINT IF EXISTS "user_memories_activities_user_id_users_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "user_memories_activities" ADD CONSTRAINT "user_memories_activities_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "user_memories_activities" DROP CONSTRAINT IF EXISTS "user_memories_activities_user_memory_id_user_memories_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "user_memories_activities" ADD CONSTRAINT "user_memories_activities_user_memory_id_user_memories_id_fk" FOREIGN KEY ("user_memory_id") REFERENCES "public"."user_memories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "user_memories_activities_narrative_vector_index" ON "user_memories_activities" USING hnsw ("narrative_vector" vector_cosine_ops);--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "user_memories_activities_feedback_vector_index" ON "user_memories_activities" USING hnsw ("feedback_vector" vector_cosine_ops);--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "user_memories_activities_type_index" ON "user_memories_activities" USING btree ("type");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "user_memories_activities_user_id_index" ON "user_memories_activities" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "user_memories_activities_user_memory_id_index" ON "user_memories_activities" USING btree ("user_memory_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "user_memories_activities_status_index" ON "user_memories_activities" USING btree ("status");
|
||||
File diff suppressed because it is too large
Load Diff
@@ -490,7 +490,14 @@
|
||||
"when": 1768303764632,
|
||||
"tag": "0069_add_topic_shares_table",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 70,
|
||||
"version": "7",
|
||||
"when": 1768999498635,
|
||||
"tag": "0070_add_user_memory_activities",
|
||||
"breakpoints": true
|
||||
}
|
||||
],
|
||||
"version": "6"
|
||||
}
|
||||
}
|
||||
@@ -124,6 +124,70 @@ export const userMemoriesPreferences = pgTable(
|
||||
],
|
||||
);
|
||||
|
||||
export const userMemoriesActivities = pgTable(
|
||||
'user_memories_activities',
|
||||
{
|
||||
id: varchar255('id')
|
||||
.$defaultFn(() => idGenerator('memory'))
|
||||
.primaryKey(),
|
||||
|
||||
userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }),
|
||||
userMemoryId: varchar255('user_memory_id').references(() => userMemories.id, {
|
||||
onDelete: 'cascade',
|
||||
}),
|
||||
|
||||
metadata: jsonb('metadata').$type<Record<string, unknown>>(),
|
||||
tags: text('tags').array(),
|
||||
|
||||
type: varchar255('type').notNull(),
|
||||
status: varchar255('status').notNull().default('pending'),
|
||||
timezone: varchar255('timezone'),
|
||||
startsAt: timestamptz('starts_at'),
|
||||
endsAt: timestamptz('ends_at'),
|
||||
|
||||
associatedObjects: jsonb('associated_objects').$type<{
|
||||
extra?: Record<string, unknown>,
|
||||
name?: string,
|
||||
type?: string
|
||||
}[]>(),
|
||||
associatedSubjects: jsonb('associated_subjects').$type<{
|
||||
extra?: Record<string, unknown>,
|
||||
name?: string,
|
||||
type?: string
|
||||
}[]>(),
|
||||
associatedLocations: jsonb('associated_locations').$type<{
|
||||
address?: string;
|
||||
name?: string;
|
||||
tags?: string[];
|
||||
type?: string;
|
||||
}[]>(),
|
||||
|
||||
notes: text('notes'),
|
||||
narrative: text('narrative'),
|
||||
narrativeVector: vector('narrative_vector', { dimensions: 1024 }),
|
||||
feedback: text('feedback'),
|
||||
feedbackVector: vector('feedback_vector', { dimensions: 1024 }),
|
||||
|
||||
capturedAt: timestamptz('captured_at').notNull().defaultNow(),
|
||||
|
||||
...timestamps,
|
||||
},
|
||||
(table) => [
|
||||
index('user_memories_activities_narrative_vector_index').using(
|
||||
'hnsw',
|
||||
table.narrativeVector.op('vector_cosine_ops'),
|
||||
),
|
||||
index('user_memories_activities_feedback_vector_index').using(
|
||||
'hnsw',
|
||||
table.feedbackVector.op('vector_cosine_ops'),
|
||||
),
|
||||
index('user_memories_activities_type_index').on(table.type),
|
||||
index('user_memories_activities_user_id_index').on(table.userId),
|
||||
index('user_memories_activities_user_memory_id_index').on(table.userMemoryId),
|
||||
index('user_memories_activities_status_index').on(table.status),
|
||||
],
|
||||
);
|
||||
|
||||
export const userMemoriesIdentities = pgTable(
|
||||
'user_memories_identities',
|
||||
{
|
||||
@@ -242,3 +306,10 @@ export type UserMemoryExperiencesWithoutVectors = Omit<
|
||||
'situationVector' | 'actionVector' | 'keyLearningVector'
|
||||
>;
|
||||
export type NewUserMemoryExperience = typeof userMemoriesExperiences.$inferInsert;
|
||||
|
||||
export type UserMemoryActivity = typeof userMemoriesActivities.$inferSelect;
|
||||
export type UserMemoryActivitiesWithoutVectors = Omit<
|
||||
UserMemoryActivity,
|
||||
'narrativeVector' | 'feedbackVector'
|
||||
>;
|
||||
export type NewUserMemoryActivity = typeof userMemoriesActivities.$inferInsert;
|
||||
|
||||
@@ -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