@lobehub/lobehub 2.0.0-next.333 → 2.0.0-next.335

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +14 -0
  3. package/docs/development/database-schema.dbml +34 -0
  4. package/locales/ar/common.json +1 -0
  5. package/locales/bg-BG/common.json +1 -0
  6. package/locales/de-DE/common.json +1 -0
  7. package/locales/en-US/common.json +1 -0
  8. package/locales/es-ES/common.json +1 -0
  9. package/locales/fa-IR/common.json +1 -0
  10. package/locales/fr-FR/common.json +1 -0
  11. package/locales/it-IT/common.json +1 -0
  12. package/locales/ja-JP/common.json +1 -0
  13. package/locales/ko-KR/common.json +1 -0
  14. package/locales/nl-NL/common.json +1 -0
  15. package/locales/pl-PL/common.json +1 -0
  16. package/locales/pt-BR/common.json +1 -0
  17. package/locales/ru-RU/common.json +1 -0
  18. package/locales/tr-TR/common.json +1 -0
  19. package/locales/vi-VN/common.json +1 -0
  20. package/locales/zh-CN/common.json +1 -0
  21. package/locales/zh-TW/common.json +1 -0
  22. package/package.json +1 -1
  23. package/packages/const/src/url.ts +6 -0
  24. package/packages/database/migrations/0070_add_user_memory_activities.sql +35 -0
  25. package/packages/database/migrations/meta/0070_snapshot.json +10656 -0
  26. package/packages/database/migrations/meta/_journal.json +8 -1
  27. package/packages/database/src/schemas/userMemories/index.ts +71 -0
  28. package/src/app/[variants]/(mobile)/me/(home)/features/useCategory.tsx +16 -9
  29. package/src/app/[variants]/layout.tsx +0 -4
  30. package/src/features/User/UserPanel/useMenu.tsx +18 -9
  31. package/src/hooks/usePlatform.test.ts +5 -0
  32. package/src/hooks/usePlatform.ts +1 -0
  33. package/src/locales/default/common.ts +1 -0
@@ -490,7 +490,14 @@
490
490
  "when": 1768303764632,
491
491
  "tag": "0069_add_topic_shares_table",
492
492
  "breakpoints": true
493
+ },
494
+ {
495
+ "idx": 70,
496
+ "version": "7",
497
+ "when": 1768999498635,
498
+ "tag": "0070_add_user_memory_activities",
499
+ "breakpoints": true
493
500
  }
494
501
  ],
495
502
  "version": "6"
496
- }
503
+ }
@@ -124,6 +124,70 @@ export const userMemoriesPreferences = pgTable(
124
124
  ],
125
125
  );
126
126
 
127
+ export const userMemoriesActivities = pgTable(
128
+ 'user_memories_activities',
129
+ {
130
+ id: varchar255('id')
131
+ .$defaultFn(() => idGenerator('memory'))
132
+ .primaryKey(),
133
+
134
+ userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }),
135
+ userMemoryId: varchar255('user_memory_id').references(() => userMemories.id, {
136
+ onDelete: 'cascade',
137
+ }),
138
+
139
+ metadata: jsonb('metadata').$type<Record<string, unknown>>(),
140
+ tags: text('tags').array(),
141
+
142
+ type: varchar255('type').notNull(),
143
+ status: varchar255('status').notNull().default('pending'),
144
+ timezone: varchar255('timezone'),
145
+ startsAt: timestamptz('starts_at'),
146
+ endsAt: timestamptz('ends_at'),
147
+
148
+ associatedObjects: jsonb('associated_objects').$type<{
149
+ extra?: Record<string, unknown>,
150
+ name?: string,
151
+ type?: string
152
+ }[]>(),
153
+ associatedSubjects: jsonb('associated_subjects').$type<{
154
+ extra?: Record<string, unknown>,
155
+ name?: string,
156
+ type?: string
157
+ }[]>(),
158
+ associatedLocations: jsonb('associated_locations').$type<{
159
+ address?: string;
160
+ name?: string;
161
+ tags?: string[];
162
+ type?: string;
163
+ }[]>(),
164
+
165
+ notes: text('notes'),
166
+ narrative: text('narrative'),
167
+ narrativeVector: vector('narrative_vector', { dimensions: 1024 }),
168
+ feedback: text('feedback'),
169
+ feedbackVector: vector('feedback_vector', { dimensions: 1024 }),
170
+
171
+ capturedAt: timestamptz('captured_at').notNull().defaultNow(),
172
+
173
+ ...timestamps,
174
+ },
175
+ (table) => [
176
+ index('user_memories_activities_narrative_vector_index').using(
177
+ 'hnsw',
178
+ table.narrativeVector.op('vector_cosine_ops'),
179
+ ),
180
+ index('user_memories_activities_feedback_vector_index').using(
181
+ 'hnsw',
182
+ table.feedbackVector.op('vector_cosine_ops'),
183
+ ),
184
+ index('user_memories_activities_type_index').on(table.type),
185
+ index('user_memories_activities_user_id_index').on(table.userId),
186
+ index('user_memories_activities_user_memory_id_index').on(table.userMemoryId),
187
+ index('user_memories_activities_status_index').on(table.status),
188
+ ],
189
+ );
190
+
127
191
  export const userMemoriesIdentities = pgTable(
128
192
  'user_memories_identities',
129
193
  {
@@ -242,3 +306,10 @@ export type UserMemoryExperiencesWithoutVectors = Omit<
242
306
  'situationVector' | 'actionVector' | 'keyLearningVector'
243
307
  >;
244
308
  export type NewUserMemoryExperience = typeof userMemoriesExperiences.$inferInsert;
309
+
310
+ export type UserMemoryActivity = typeof userMemoriesActivities.$inferSelect;
311
+ export type UserMemoryActivitiesWithoutVectors = Omit<
312
+ UserMemoryActivity,
313
+ 'narrativeVector' | 'feedbackVector'
314
+ >;
315
+ export type NewUserMemoryActivity = typeof userMemoriesActivities.$inferInsert;
@@ -1,5 +1,5 @@
1
- import { UTM_SOURCE , LOBE_CHAT_CLOUD } from '@lobechat/business-const';
2
- import { OFFICIAL_URL } from '@lobechat/const';
1
+ import { LOBE_CHAT_CLOUD, UTM_SOURCE } from '@lobechat/business-const';
2
+ import { DOWNLOAD_URL, OFFICIAL_URL } from '@lobechat/const';
3
3
  import {
4
4
  Book,
5
5
  CircleUserRound,
@@ -9,22 +9,29 @@ import {
9
9
  FileClockIcon,
10
10
  Settings2,
11
11
  } from 'lucide-react';
12
+ import { useMemo } from 'react';
12
13
  import { useTranslation } from 'react-i18next';
13
14
  import { useNavigate } from 'react-router-dom';
14
15
 
15
16
  import { type CellProps } from '@/components/Cell';
16
17
  import { DOCUMENTS, FEEDBACK } from '@/const/index';
17
- import { usePWAInstall } from '@/hooks/usePWAInstall';
18
+ import { usePlatform } from '@/hooks/usePlatform';
18
19
  import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
19
20
  import { useUserStore } from '@/store/user';
20
21
  import { authSelectors } from '@/store/user/selectors';
21
22
 
22
23
  export const useCategory = (onOpenChangelogModal: () => void) => {
23
24
  const navigate = useNavigate();
24
- const { canInstall, install } = usePWAInstall();
25
25
  const { t } = useTranslation(['common', 'setting', 'auth']);
26
26
  const { showCloudPromotion, hideDocs } = useServerConfigStore(featureFlagsSelectors);
27
27
  const [isLoginWithAuth] = useUserStore((s) => [authSelectors.isLoginWithAuth(s)]);
28
+ const { isIOS, isAndroid } = usePlatform();
29
+
30
+ const downloadUrl = useMemo(() => {
31
+ if (isIOS) return DOWNLOAD_URL.ios;
32
+ if (isAndroid) return DOWNLOAD_URL.android;
33
+ return DOWNLOAD_URL.default;
34
+ }, [isIOS, isAndroid]);
28
35
 
29
36
  const profile: CellProps[] = [
30
37
  {
@@ -47,12 +54,12 @@ export const useCategory = (onOpenChangelogModal: () => void) => {
47
54
  },
48
55
  ];
49
56
 
50
- const pwa: CellProps[] = [
57
+ const downloadClient: CellProps[] = [
51
58
  {
52
59
  icon: Download,
53
- key: 'pwa',
54
- label: t('installPWA'),
55
- onClick: () => install(),
60
+ key: 'download-client',
61
+ label: t('downloadClient'),
62
+ onClick: () => window.open(downloadUrl, '__blank'),
56
63
  },
57
64
  {
58
65
  type: 'divider',
@@ -96,7 +103,7 @@ export const useCategory = (onOpenChangelogModal: () => void) => {
96
103
  /* ↓ cloud slot ↓ */
97
104
 
98
105
  /* ↑ cloud slot ↑ */
99
- ...(canInstall ? pwa : []),
106
+ ...downloadClient,
100
107
  ...(!hideDocs ? helps : []),
101
108
  ].filter(Boolean) as CellProps[];
102
109
 
@@ -9,7 +9,6 @@ import BusinessGlobalProvider from '@/business/client/BusinessGlobalProvider';
9
9
  import Analytics from '@/components/Analytics';
10
10
  import { DEFAULT_LANG } from '@/const/locale';
11
11
  import { isDesktop } from '@/const/version';
12
- import PWAInstall from '@/features/PWAInstall';
13
12
  import AuthProvider from '@/layout/AuthProvider';
14
13
  import GlobalProvider from '@/layout/GlobalProvider';
15
14
  import { type Locales } from '@/locales/resources';
@@ -40,9 +39,6 @@ const RootLayout = async ({ children, params }: RootLayoutProps) => {
40
39
  variants={variants}
41
40
  >
42
41
  <AuthProvider>{children}</AuthProvider>
43
- <Suspense fallback={null}>
44
- <PWAInstall />
45
- </Suspense>
46
42
  </GlobalProvider>
47
43
  );
48
44
  };
@@ -1,9 +1,9 @@
1
1
  import { LOBE_CHAT_CLOUD, UTM_SOURCE } from '@lobechat/business-const';
2
- import { isDesktop } from '@lobechat/const';
2
+ import { DOWNLOAD_URL, isDesktop } from '@lobechat/const';
3
3
  import { Flexbox, Hotkey, Icon, Tag } from '@lobehub/ui';
4
4
  import { type ItemType } from 'antd/es/menu/interface';
5
5
  import { Cloudy, Download, HardDriveDownload, LogOut, Settings2 } from 'lucide-react';
6
- import { type PropsWithChildren, memo } from 'react';
6
+ import { type PropsWithChildren, memo, useMemo } from 'react';
7
7
  import { useTranslation } from 'react-i18next';
8
8
  import { Link } from 'react-router-dom';
9
9
 
@@ -12,7 +12,7 @@ import type { MenuProps } from '@/components/Menu';
12
12
  import { DEFAULT_DESKTOP_HOTKEY_CONFIG } from '@/const/desktop';
13
13
  import { OFFICIAL_URL } from '@/const/url';
14
14
  import DataImporter from '@/features/DataImporter';
15
- import { usePWAInstall } from '@/hooks/usePWAInstall';
15
+ import { usePlatform } from '@/hooks/usePlatform';
16
16
  import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
17
17
  import { useUserStore } from '@/store/user';
18
18
  import { authSelectors } from '@/store/user/selectors';
@@ -44,7 +44,6 @@ const NewVersionBadge = memo(
44
44
  );
45
45
 
46
46
  export const useMenu = () => {
47
- const { canInstall, install } = usePWAInstall();
48
47
  const hasNewVersion = useNewVersion();
49
48
  const { t } = useTranslation(['common', 'setting', 'auth']);
50
49
  const { showCloudPromotion, hideDocs } = useServerConfigStore(featureFlagsSelectors);
@@ -53,6 +52,13 @@ export const useMenu = () => {
53
52
  authSelectors.isLoginWithAuth(s),
54
53
  ]);
55
54
  const businessMenuItems = useBusinessMenuItems(isLogin);
55
+ const { isIOS, isAndroid } = usePlatform();
56
+
57
+ const downloadUrl = useMemo(() => {
58
+ if (isIOS) return DOWNLOAD_URL.ios;
59
+ if (isAndroid) return DOWNLOAD_URL.android;
60
+ return DOWNLOAD_URL.default;
61
+ }, [isIOS, isAndroid]);
56
62
 
57
63
  const settings: MenuProps['items'] = [
58
64
  {
@@ -71,12 +77,15 @@ export const useMenu = () => {
71
77
  },
72
78
  ];
73
79
 
74
- const pwa: MenuProps['items'] = [
80
+ const downloadClient: MenuProps['items'] = [
75
81
  {
76
82
  icon: <Icon icon={Download} />,
77
- key: 'pwa',
78
- label: t('installPWA'),
79
- onClick: () => install(),
83
+ key: 'download-client',
84
+ label: (
85
+ <a href={downloadUrl} rel="noopener noreferrer" target="_blank">
86
+ {t('downloadClient')}
87
+ </a>
88
+ ),
80
89
  },
81
90
  {
82
91
  type: 'divider',
@@ -119,7 +128,7 @@ export const useMenu = () => {
119
128
 
120
129
  ...(isLogin ? settings : []),
121
130
  ...businessMenuItems,
122
- ...(canInstall ? pwa : []),
131
+ ...(!isDesktop ? downloadClient : []),
123
132
  ...data,
124
133
  ...(!hideDocs ? helps : []),
125
134
  ].filter(Boolean) as MenuProps['items'];
@@ -25,6 +25,7 @@ describe('usePlatform', () => {
25
25
  const { result } = renderHook(() => usePlatform());
26
26
 
27
27
  expect(result.current).toEqual({
28
+ isAndroid: false,
28
29
  isApple: true,
29
30
  isChrome: true,
30
31
  isChromium: true,
@@ -50,6 +51,7 @@ describe('usePlatform', () => {
50
51
  const { result } = renderHook(() => usePlatform());
51
52
 
52
53
  expect(result.current).toEqual({
54
+ isAndroid: false,
53
55
  isApple: true,
54
56
  isChrome: false,
55
57
  isChromium: false,
@@ -75,6 +77,7 @@ describe('usePlatform', () => {
75
77
  const { result } = renderHook(() => usePlatform());
76
78
 
77
79
  expect(result.current).toEqual({
80
+ isAndroid: false,
78
81
  isApple: false,
79
82
  isChrome: false,
80
83
  isChromium: true,
@@ -100,6 +103,7 @@ describe('usePlatform', () => {
100
103
  const { result } = renderHook(() => usePlatform());
101
104
 
102
105
  expect(result.current).toEqual({
106
+ isAndroid: false,
103
107
  isApple: false,
104
108
  isChrome: false,
105
109
  isChromium: false,
@@ -125,6 +129,7 @@ describe('usePlatform', () => {
125
129
  const { result } = renderHook(() => usePlatform());
126
130
 
127
131
  expect(result.current).toEqual({
132
+ isAndroid: false,
128
133
  isApple: true,
129
134
  isChrome: true,
130
135
  isChromium: true,
@@ -13,6 +13,7 @@ export const usePlatform = () => {
13
13
  const browser = useRef(getBrowser());
14
14
 
15
15
  const platformInfo = {
16
+ isAndroid: platform.current?.toLowerCase() === 'android',
16
17
  isApple: platform.current && ['mac os', 'ios'].includes(platform.current?.toLowerCase()),
17
18
  isArc: isArc(),
18
19
  isChrome: browser.current?.toLowerCase() === 'chrome',
@@ -193,6 +193,7 @@ export default {
193
193
  'delete': 'Delete',
194
194
  'document': 'User Manual',
195
195
  'download': 'Download',
196
+ 'downloadClient': 'Download Client',
196
197
  'duplicate': 'Duplicate',
197
198
  'edit': 'Edit',
198
199
  'errors.invalidFileFormat': 'Invalid file format',