@lobehub/chat 1.119.1 → 1.120.0
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.
- package/.vscode/settings.json +2 -3
- package/CHANGELOG.md +58 -0
- package/changelog/v1.json +17 -0
- package/package.json +5 -6
- package/packages/const/src/auth.ts +0 -36
- package/packages/const/src/index.ts +3 -1
- package/packages/database/src/models/__tests__/aiModel.test.ts +1 -2
- package/packages/database/src/models/__tests__/generationBatch.test.ts +47 -1
- package/packages/database/src/models/aiModel.ts +2 -3
- package/packages/database/src/models/generationBatch.ts +8 -1
- package/packages/database/src/repositories/aiInfra/index.test.ts +1 -1
- package/packages/database/src/repositories/aiInfra/index.ts +4 -4
- package/packages/model-bank/src/aiModels/ai21.ts +1 -1
- package/packages/model-bank/src/aiModels/ai302.ts +1 -1
- package/packages/model-bank/src/aiModels/ai360.ts +1 -1
- package/packages/model-bank/src/aiModels/aihubmix.ts +3 -3
- package/packages/model-bank/src/aiModels/akashchat.ts +1 -1
- package/packages/model-bank/src/aiModels/anthropic.ts +1 -1
- package/packages/model-bank/src/aiModels/azure.ts +1 -1
- package/packages/model-bank/src/aiModels/azureai.ts +1 -1
- package/packages/model-bank/src/aiModels/baichuan.ts +1 -1
- package/packages/model-bank/src/aiModels/bedrock.ts +1 -1
- package/packages/model-bank/src/aiModels/bfl.ts +2 -3
- package/packages/model-bank/src/aiModels/cloudflare.ts +1 -1
- package/packages/model-bank/src/aiModels/cohere.ts +1 -1
- package/packages/model-bank/src/aiModels/deepseek.ts +1 -1
- package/packages/model-bank/src/aiModels/fal.ts +1 -1
- package/packages/model-bank/src/aiModels/fireworksai.ts +1 -1
- package/packages/model-bank/src/aiModels/giteeai.ts +1 -1
- package/packages/model-bank/src/aiModels/github.ts +1 -1
- package/packages/model-bank/src/aiModels/google.ts +6 -7
- package/packages/model-bank/src/aiModels/groq.ts +1 -1
- package/packages/model-bank/src/aiModels/higress.ts +1 -1
- package/packages/model-bank/src/aiModels/huggingface.ts +1 -1
- package/packages/model-bank/src/aiModels/hunyuan.ts +1 -1
- package/packages/model-bank/src/aiModels/index.ts +1 -1
- package/packages/model-bank/src/aiModels/infiniai.ts +1 -1
- package/packages/model-bank/src/aiModels/internlm.ts +1 -1
- package/packages/model-bank/src/aiModels/jina.ts +1 -1
- package/packages/model-bank/src/aiModels/lmstudio.ts +1 -1
- package/packages/model-bank/src/aiModels/lobehub.ts +1 -1
- package/packages/model-bank/src/aiModels/minimax.ts +1 -1
- package/packages/model-bank/src/aiModels/mistral.ts +1 -1
- package/packages/model-bank/src/aiModels/modelscope.ts +1 -1
- package/packages/model-bank/src/aiModels/moonshot.ts +1 -1
- package/packages/model-bank/src/aiModels/novita.ts +1 -1
- package/packages/model-bank/src/aiModels/nvidia.ts +1 -1
- package/packages/model-bank/src/aiModels/ollama.ts +1 -1
- package/packages/model-bank/src/aiModels/openai.ts +1 -1
- package/packages/model-bank/src/aiModels/openrouter.ts +3 -3
- package/packages/model-bank/src/aiModels/perplexity.ts +1 -1
- package/packages/model-bank/src/aiModels/ppio.ts +1 -1
- package/packages/model-bank/src/aiModels/qiniu.ts +1 -1
- package/packages/model-bank/src/aiModels/qwen.ts +4 -2
- package/packages/model-bank/src/aiModels/sambanova.ts +1 -1
- package/packages/model-bank/src/aiModels/search1api.ts +1 -1
- package/packages/model-bank/src/aiModels/sensenova.ts +1 -1
- package/packages/model-bank/src/aiModels/siliconcloud.ts +7 -1
- package/packages/model-bank/src/aiModels/spark.ts +1 -1
- package/packages/model-bank/src/aiModels/stepfun.ts +1 -1
- package/packages/model-bank/src/aiModels/taichu.ts +1 -1
- package/packages/model-bank/src/aiModels/tencentcloud.ts +1 -1
- package/packages/model-bank/src/aiModels/togetherai.ts +1 -1
- package/packages/model-bank/src/aiModels/upstage.ts +1 -1
- package/packages/model-bank/src/aiModels/v0.ts +1 -1
- package/packages/model-bank/src/aiModels/vertexai.ts +3 -3
- package/packages/model-bank/src/aiModels/vllm.ts +1 -1
- package/packages/model-bank/src/aiModels/volcengine.ts +1 -1
- package/packages/model-bank/src/aiModels/wenxin.ts +1 -1
- package/packages/model-bank/src/aiModels/xai.ts +1 -1
- package/packages/model-bank/src/aiModels/xinference.ts +1 -1
- package/packages/model-bank/src/aiModels/zeroone.ts +1 -1
- package/packages/model-bank/src/aiModels/zhipu.ts +1 -1
- package/packages/model-bank/src/index.ts +1 -0
- package/packages/model-bank/src/standard-parameters/index.ts +48 -0
- package/packages/{types/src → model-bank/src/types}/aiModel.ts +12 -1
- package/packages/model-bank/src/types/index.ts +1 -0
- package/packages/model-runtime/package.json +4 -1
- package/packages/model-runtime/src/BaseAI.ts +2 -2
- package/packages/model-runtime/src/ModelRuntime.test.ts +4 -4
- package/packages/model-runtime/src/RouterRuntime/createRuntime.ts +3 -7
- package/packages/model-runtime/src/ai302/index.ts +1 -1
- package/packages/model-runtime/src/aihubmix/index.ts +1 -2
- package/packages/model-runtime/src/anthropic/index.ts +1 -1
- package/packages/model-runtime/src/azureOpenai/index.ts +2 -3
- package/packages/model-runtime/src/azureai/index.ts +2 -3
- package/packages/model-runtime/src/bedrock/index.ts +1 -1
- package/packages/model-runtime/src/bfl/createImage.test.ts +4 -4
- package/packages/model-runtime/src/bfl/createImage.ts +2 -2
- package/packages/model-runtime/src/bfl/index.ts +1 -1
- package/packages/model-runtime/src/cloudflare/index.ts +1 -1
- package/packages/model-runtime/src/const/models.ts +64 -0
- package/packages/model-runtime/src/fal/index.test.ts +2 -3
- package/packages/model-runtime/src/fal/index.ts +1 -1
- package/packages/model-runtime/src/github/index.ts +1 -1
- package/packages/model-runtime/src/google/createImage.test.ts +1 -1
- package/packages/model-runtime/src/google/createImage.ts +53 -25
- package/packages/model-runtime/src/google/index.test.ts +1 -1
- package/packages/model-runtime/src/google/index.ts +4 -3
- package/packages/model-runtime/src/groq/index.ts +1 -1
- package/packages/model-runtime/src/helpers/parseToolCalls.ts +1 -2
- package/packages/model-runtime/src/huggingface/index.ts +1 -1
- package/packages/model-runtime/src/index.ts +3 -1
- package/packages/model-runtime/src/infiniai/index.ts +1 -1
- package/packages/model-runtime/src/ollama/index.test.ts +1 -1
- package/packages/model-runtime/src/ollama/index.ts +2 -3
- package/packages/model-runtime/src/openai/index.ts +16 -8
- package/packages/model-runtime/src/providerTestUtils.ts +1 -2
- package/packages/model-runtime/src/qiniu/index.test.ts +2 -3
- package/packages/model-runtime/src/qwen/index.ts +1 -1
- package/packages/model-runtime/src/siliconcloud/index.ts +2 -2
- package/packages/model-runtime/src/types/chat.ts +2 -22
- package/packages/model-runtime/src/{error.ts → types/error.ts} +29 -0
- package/packages/model-runtime/src/types/index.ts +4 -0
- package/packages/model-runtime/src/types/toolsCalling.ts +48 -0
- package/packages/model-runtime/src/types/type.ts +1 -1
- package/packages/model-runtime/src/types/usage.ts +27 -0
- package/packages/model-runtime/src/utils/anthropicHelpers.test.ts +2 -2
- package/packages/model-runtime/src/utils/anthropicHelpers.ts +1 -1
- package/packages/model-runtime/src/utils/createError.ts +1 -1
- package/packages/model-runtime/src/utils/errorResponse.test.ts +110 -0
- package/packages/model-runtime/src/utils/errorResponse.ts +64 -0
- package/packages/{utils/src → model-runtime/src/utils}/getFallbackModelProperty.ts +1 -1
- package/packages/model-runtime/src/utils/googleErrorParser.test.ts +1 -1
- package/packages/model-runtime/src/utils/googleErrorParser.ts +1 -1
- package/packages/model-runtime/src/utils/handleOpenAIError.ts +1 -1
- package/packages/model-runtime/src/utils/imageToBase64.test.ts +91 -0
- package/packages/model-runtime/src/utils/imageToBase64.ts +62 -0
- package/packages/model-runtime/src/utils/modelParse.test.ts +2 -2
- package/packages/model-runtime/src/utils/modelParse.ts +16 -10
- package/packages/model-runtime/src/utils/openaiCompatibleFactory/createImage.ts +1 -1
- package/packages/model-runtime/src/utils/openaiCompatibleFactory/index.ts +3 -3
- package/packages/model-runtime/src/utils/openaiHelpers.test.ts +2 -2
- package/packages/model-runtime/src/utils/openaiHelpers.ts +3 -4
- package/packages/model-runtime/src/utils/postProcessModelList.ts +2 -2
- package/packages/model-runtime/src/utils/safeParseJSON.test.ts +71 -0
- package/packages/model-runtime/src/utils/safeParseJSON.ts +12 -0
- package/packages/model-runtime/src/utils/streams/bedrock/claude.ts +1 -1
- package/packages/model-runtime/src/utils/streams/bedrock/llama.test.ts +1 -2
- package/packages/model-runtime/src/utils/streams/bedrock/llama.ts +1 -1
- package/packages/model-runtime/src/utils/streams/google-ai.test.ts +1 -1
- package/packages/model-runtime/src/utils/streams/google-ai.ts +1 -1
- package/packages/model-runtime/src/utils/streams/ollama.test.ts +1 -1
- package/packages/model-runtime/src/utils/streams/ollama.ts +2 -3
- package/packages/model-runtime/src/utils/streams/openai/openai.test.ts +1 -2
- package/packages/model-runtime/src/utils/streams/openai/openai.ts +1 -1
- package/packages/model-runtime/src/utils/streams/openai/responsesStream.ts +1 -1
- package/packages/model-runtime/src/utils/streams/protocol.ts +3 -3
- package/packages/model-runtime/src/utils/streams/vertex-ai.test.ts +1 -1
- package/packages/model-runtime/src/utils/streams/vertex-ai.ts +2 -2
- package/packages/model-runtime/src/utils/uuid.ts +7 -0
- package/packages/model-runtime/src/vertexai/index.ts +1 -1
- package/packages/types/src/agent/index.ts +2 -1
- package/packages/types/src/aiProvider.ts +10 -2
- package/packages/types/src/auth.ts +35 -0
- package/packages/types/src/discover/models.ts +1 -1
- package/packages/types/src/discover/providers.ts +1 -1
- package/packages/types/src/index.ts +4 -0
- package/packages/types/src/llm.ts +2 -47
- package/packages/types/src/session/agentSession.ts +3 -3
- package/packages/types/src/session/index.ts +2 -2
- package/packages/types/src/session/sessionGroup.ts +0 -2
- package/packages/types/src/user/settings/general.ts +1 -1
- package/packages/types/src/user/settings/modelProvider.ts +1 -1
- package/packages/utils/src/fetch/fetchSSE.ts +1 -1
- package/packages/utils/src/format.ts +2 -3
- package/packages/utils/src/index.ts +3 -1
- package/packages/utils/src/number.test.ts +1 -2
- package/packages/utils/src/number.ts +1 -2
- package/packages/utils/src/parseModels.test.ts +1 -2
- package/packages/utils/src/parseModels.ts +2 -3
- package/packages/utils/src/pricing.test.ts +1 -2
- package/packages/utils/src/pricing.ts +1 -1
- package/packages/utils/src/server/xor.ts +3 -1
- package/src/app/(backend)/middleware/auth/index.ts +1 -2
- package/src/app/(backend)/webapi/chat/vertexai/route.ts +1 -1
- package/src/app/(backend)/webapi/text-to-image/[provider]/route.ts +1 -2
- package/src/app/[variants]/(main)/(mobile)/me/settings/features/useCategory.tsx +2 -16
- package/src/app/[variants]/(main)/chat/@session/_layout/Desktop/SessionHeader.tsx +1 -3
- package/src/app/[variants]/(main)/chat/@session/_layout/Mobile/SessionHeader.tsx +1 -3
- package/src/app/[variants]/(main)/discover/(list)/model/features/List/ModelTypeIcon.tsx +1 -2
- package/src/app/[variants]/(main)/image/@menu/components/SeedNumberInput/index.tsx +1 -1
- package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/hooks/useAutoDimensions.ts +4 -3
- package/src/app/[variants]/(main)/settings/hooks/useCategory.tsx +3 -21
- package/src/app/[variants]/(main)/settings/provider/features/ModelList/CreateNewModelModal/Form.tsx +1 -1
- package/src/app/[variants]/(main)/settings/provider/features/ModelList/ModelItem.tsx +1 -1
- package/src/app/[variants]/(main)/settings/provider/features/ModelList/SortModelModal/ListItem.tsx +1 -1
- package/src/app/[variants]/(main)/settings/provider/features/ModelList/SortModelModal/index.tsx +1 -1
- package/src/components/ModelSelect/index.tsx +1 -1
- package/src/config/featureFlags/schema.test.ts +1 -2
- package/src/config/featureFlags/schema.ts +0 -6
- package/src/config/featureFlags/utils/parser.test.ts +7 -7
- package/src/database/_deprecated/core/index.ts +0 -1
- package/src/database/_deprecated/core/migrations/migrateSettingsToUser/type.ts +2 -5
- package/src/database/_deprecated/core/model.ts +4 -38
- package/src/database/_deprecated/models/message.ts +1 -1
- package/src/features/Conversation/Extras/Usage/UsageDetail/ModelCard.tsx +1 -1
- package/src/features/Conversation/Extras/Usage/UsageDetail/pricing.ts +3 -4
- package/src/features/Conversation/Extras/Usage/UsageDetail/tokens.test.ts +1 -1
- package/src/features/Conversation/Extras/Usage/UsageDetail/tokens.ts +3 -2
- package/src/layout/GlobalProvider/StoreInitialization.tsx +0 -3
- package/src/libs/trpc/async/context.ts +2 -1
- package/src/libs/trpc/edge/context.ts +2 -6
- package/src/libs/trpc/lambda/context.ts +1 -1
- package/src/migrations/FromV5ToV6/types/v5.ts +2 -2
- package/src/migrations/FromV5ToV6/types/v6.ts +2 -1
- package/src/server/globalConfig/genServerAiProviderConfig.ts +3 -3
- package/src/server/modules/ModelRuntime/index.test.ts +1 -1
- package/src/server/modules/ModelRuntime/index.ts +1 -1
- package/src/server/routers/async/caller.ts +2 -1
- package/src/server/routers/async/image.ts +2 -2
- package/src/server/routers/lambda/aiModel.ts +1 -1
- package/src/server/services/chunk/index.ts +2 -1
- package/src/server/services/generation/index.ts +2 -2
- package/src/services/_auth.ts +2 -1
- package/src/services/aiModel/server.test.ts +1 -1
- package/src/services/aiModel/type.ts +1 -1
- package/src/services/chat.ts +1 -1
- package/src/services/upload.ts +3 -3
- package/src/store/agent/slices/chat/action.ts +1 -1
- package/src/store/aiInfra/slices/aiModel/action.ts +6 -6
- package/src/store/aiInfra/slices/aiModel/initialState.ts +1 -1
- package/src/store/aiInfra/slices/aiModel/selectors.test.ts +1 -1
- package/src/store/aiInfra/slices/aiModel/selectors.ts +2 -1
- package/src/store/aiInfra/slices/aiProvider/__tests__/action.test.ts +7 -7
- package/src/store/aiInfra/slices/aiProvider/action.ts +8 -8
- package/src/store/aiInfra/slices/aiProvider/initialState.ts +2 -1
- package/src/store/electron/actions/app.ts +1 -1
- package/src/store/image/slices/generationConfig/action.test.ts +1 -1
- package/src/store/image/slices/generationConfig/action.ts +1 -1
- package/src/store/image/slices/generationConfig/hooks.test.ts +1 -1
- package/src/store/image/slices/generationConfig/hooks.ts +6 -3
- package/src/store/image/slices/generationConfig/selectors.test.ts +1 -1
- package/src/store/serverConfig/selectors.test.ts +0 -1
- package/src/store/user/initialState.ts +1 -4
- package/src/store/user/selectors.ts +0 -1
- package/src/store/user/slices/auth/action.ts +1 -1
- package/src/store/user/slices/auth/selectors.ts +3 -4
- package/src/store/user/slices/modelList/action.ts +8 -7
- package/src/store/user/slices/modelList/selectors/modelProvider.ts +8 -5
- package/src/store/user/slices/preference/selectors.ts +3 -2
- package/src/store/user/slices/settings/selectors/settings.ts +1 -2
- package/src/store/user/store.ts +1 -4
- package/docs/self-hosting/advanced/webrtc.mdx +0 -86
- package/docs/self-hosting/advanced/webrtc.zh-CN.mdx +0 -80
- package/packages/const/src/image.ts +0 -51
- package/src/app/[variants]/(main)/settings/sync/features/Alert.tsx +0 -53
- package/src/app/[variants]/(main)/settings/sync/features/DeviceInfo/Card.tsx +0 -42
- package/src/app/[variants]/(main)/settings/sync/features/DeviceInfo/DeviceName.tsx +0 -62
- package/src/app/[variants]/(main)/settings/sync/features/DeviceInfo/SystemIcon.tsx +0 -31
- package/src/app/[variants]/(main)/settings/sync/features/DeviceInfo/index.tsx +0 -103
- package/src/app/[variants]/(main)/settings/sync/features/WebRTC/ChannelNameInput.tsx +0 -45
- package/src/app/[variants]/(main)/settings/sync/features/WebRTC/SyncSwitch/index.css +0 -238
- package/src/app/[variants]/(main)/settings/sync/features/WebRTC/SyncSwitch/index.tsx +0 -79
- package/src/app/[variants]/(main)/settings/sync/features/WebRTC/generateRandomRoomName.ts +0 -4
- package/src/app/[variants]/(main)/settings/sync/features/WebRTC/index.tsx +0 -103
- package/src/app/[variants]/(main)/settings/sync/index.tsx +0 -17
- package/src/app/[variants]/(main)/settings/sync/page.tsx +0 -29
- package/src/database/_deprecated/core/sync.ts +0 -321
- package/src/features/SyncStatusInspector/DisableSync.tsx +0 -79
- package/src/features/SyncStatusInspector/EnableSync.tsx +0 -132
- package/src/features/SyncStatusInspector/EnableTag.tsx +0 -66
- package/src/features/SyncStatusInspector/index.tsx +0 -27
- package/src/hooks/useSyncData.ts +0 -50
- package/src/services/__tests__/sync.test.ts +0 -56
- package/src/services/sync.ts +0 -19
- package/src/store/user/slices/sync/action.test.ts +0 -164
- package/src/store/user/slices/sync/action.ts +0 -101
- package/src/store/user/slices/sync/initialState.ts +0 -13
- package/src/store/user/slices/sync/selectors.ts +0 -20
- /package/packages/{utils/src → model-runtime/src/utils}/getFallbackModelProperty.test.ts +0 -0
@@ -1,103 +0,0 @@
|
|
1
|
-
'use client';
|
2
|
-
|
3
|
-
import { SiWebrtc } from '@icons-pack/react-simple-icons';
|
4
|
-
import { Form, type FormGroupItemType, Input, InputPassword, Text, Tooltip } from '@lobehub/ui';
|
5
|
-
import { Form as AntForm, Switch } from 'antd';
|
6
|
-
import { memo } from 'react';
|
7
|
-
import { useTranslation } from 'react-i18next';
|
8
|
-
import { Flexbox } from 'react-layout-kit';
|
9
|
-
|
10
|
-
import { useSyncSettings } from '@/app/[variants]/(main)/settings/hooks/useSyncSettings';
|
11
|
-
import { FORM_STYLE } from '@/const/layoutTokens';
|
12
|
-
import SyncStatusInspector from '@/features/SyncStatusInspector';
|
13
|
-
import { useUserStore } from '@/store/user';
|
14
|
-
|
15
|
-
import ChannelNameInput from './ChannelNameInput';
|
16
|
-
|
17
|
-
const WebRTC = memo(() => {
|
18
|
-
const { t } = useTranslation('setting');
|
19
|
-
const [form] = Form.useForm();
|
20
|
-
|
21
|
-
const [setSettings] = useUserStore((s) => [s.setSettings]);
|
22
|
-
|
23
|
-
useSyncSettings(form);
|
24
|
-
|
25
|
-
const channelName = AntForm.useWatch(['sync', 'webrtc', 'channelName'], form);
|
26
|
-
const signaling = AntForm.useWatch(['sync', 'webrtc', 'signaling'], form);
|
27
|
-
|
28
|
-
const config: FormGroupItemType = {
|
29
|
-
children: [
|
30
|
-
{
|
31
|
-
children: <Input placeholder={t('sync.webrtc.signaling.placeholder')} />,
|
32
|
-
desc: t('sync.webrtc.signaling.desc'),
|
33
|
-
label: t('sync.webrtc.signaling.title'),
|
34
|
-
name: ['sync', 'webrtc', 'signaling'],
|
35
|
-
},
|
36
|
-
{
|
37
|
-
children: <ChannelNameInput form={form} />,
|
38
|
-
desc: t('sync.webrtc.channelName.desc'),
|
39
|
-
label: t('sync.webrtc.channelName.title'),
|
40
|
-
name: ['sync', 'webrtc', 'channelName'],
|
41
|
-
},
|
42
|
-
{
|
43
|
-
children: (
|
44
|
-
<InputPassword
|
45
|
-
autoComplete={'nw-password'}
|
46
|
-
placeholder={t('sync.webrtc.channelPassword.placeholder')}
|
47
|
-
/>
|
48
|
-
),
|
49
|
-
desc: t('sync.webrtc.channelPassword.desc'),
|
50
|
-
label: t('sync.webrtc.channelPassword.title'),
|
51
|
-
name: ['sync', 'webrtc', 'channelPassword'],
|
52
|
-
},
|
53
|
-
{
|
54
|
-
children:
|
55
|
-
!channelName || !signaling ? (
|
56
|
-
<Tooltip title={t('sync.webrtc.enabled.invalid')}>
|
57
|
-
<Switch disabled />
|
58
|
-
</Tooltip>
|
59
|
-
) : (
|
60
|
-
<Switch />
|
61
|
-
),
|
62
|
-
|
63
|
-
label: t('sync.webrtc.enabled.title'),
|
64
|
-
minWidth: undefined,
|
65
|
-
name: ['sync', 'webrtc', 'enabled'],
|
66
|
-
},
|
67
|
-
],
|
68
|
-
extra: (
|
69
|
-
<div
|
70
|
-
onClick={(e) => {
|
71
|
-
e.stopPropagation();
|
72
|
-
}}
|
73
|
-
>
|
74
|
-
<SyncStatusInspector hiddenActions hiddenEnableGuide />
|
75
|
-
</div>
|
76
|
-
),
|
77
|
-
title: (
|
78
|
-
<Flexbox align={'center'} gap={12} horizontal wrap={'wrap'}>
|
79
|
-
<Flexbox align={'center'} gap={8} horizontal>
|
80
|
-
<SiWebrtc style={{ flex: 'none' }} />
|
81
|
-
{t('sync.webrtc.title')}
|
82
|
-
</Flexbox>
|
83
|
-
<Text style={{ fontWeight: 'normal' }} type={'secondary'}>
|
84
|
-
{t('sync.webrtc.desc')}
|
85
|
-
</Text>
|
86
|
-
</Flexbox>
|
87
|
-
),
|
88
|
-
};
|
89
|
-
|
90
|
-
return (
|
91
|
-
<Form
|
92
|
-
form={form}
|
93
|
-
items={[config]}
|
94
|
-
itemsType={'group'}
|
95
|
-
onFinish={setSettings}
|
96
|
-
onValuesChange={setSettings}
|
97
|
-
variant={'borderless'}
|
98
|
-
{...FORM_STYLE}
|
99
|
-
/>
|
100
|
-
);
|
101
|
-
});
|
102
|
-
|
103
|
-
export default WebRTC;
|
@@ -1,17 +0,0 @@
|
|
1
|
-
import Alert from './features/Alert';
|
2
|
-
import DeviceInfo from './features/DeviceInfo';
|
3
|
-
import WebRTC from './features/WebRTC';
|
4
|
-
|
5
|
-
const Page = ({ browser, os, mobile }: { browser?: string; mobile?: boolean; os?: string }) => {
|
6
|
-
return (
|
7
|
-
<>
|
8
|
-
<DeviceInfo browser={browser} os={os} />
|
9
|
-
<WebRTC />
|
10
|
-
<Alert mobile={mobile} />
|
11
|
-
</>
|
12
|
-
);
|
13
|
-
};
|
14
|
-
|
15
|
-
Page.displayName = 'SyncSetting';
|
16
|
-
|
17
|
-
export default Page;
|
@@ -1,29 +0,0 @@
|
|
1
|
-
import { gerServerDeviceInfo } from '@lobechat/utils/server';
|
2
|
-
import { notFound } from 'next/navigation';
|
3
|
-
|
4
|
-
import { serverFeatureFlags } from '@/config/featureFlags';
|
5
|
-
import { metadataModule } from '@/server/metadata';
|
6
|
-
import { translation } from '@/server/translation';
|
7
|
-
import { DynamicLayoutProps } from '@/types/next';
|
8
|
-
import { RouteVariants } from '@/utils/server/routeVariants';
|
9
|
-
|
10
|
-
import Page from './index';
|
11
|
-
|
12
|
-
export const generateMetadata = async (props: DynamicLayoutProps) => {
|
13
|
-
const locale = await RouteVariants.getLocale(props);
|
14
|
-
const { t } = await translation('setting', locale);
|
15
|
-
return metadataModule.generate({
|
16
|
-
description: t('header.desc'),
|
17
|
-
title: t('tab.sync'),
|
18
|
-
url: '/settings/sync',
|
19
|
-
});
|
20
|
-
};
|
21
|
-
export default async (props: DynamicLayoutProps) => {
|
22
|
-
const enableWebrtc = serverFeatureFlags().enableWebrtc;
|
23
|
-
if (!enableWebrtc) return notFound();
|
24
|
-
|
25
|
-
const isMobile = await RouteVariants.getIsMobile(props);
|
26
|
-
const { os, browser } = await gerServerDeviceInfo();
|
27
|
-
|
28
|
-
return <Page browser={browser} mobile={isMobile} os={os} />;
|
29
|
-
};
|
@@ -1,321 +0,0 @@
|
|
1
|
-
import Debug from 'debug';
|
2
|
-
import { throttle, uniqBy } from 'lodash-es';
|
3
|
-
import type { WebrtcProvider } from 'y-webrtc';
|
4
|
-
import type { Doc, Transaction } from 'yjs';
|
5
|
-
|
6
|
-
import {
|
7
|
-
OnAwarenessChange,
|
8
|
-
OnSyncEvent,
|
9
|
-
OnSyncStatusChange,
|
10
|
-
PeerSyncStatus,
|
11
|
-
StartDataSyncParams,
|
12
|
-
} from '@/types/sync';
|
13
|
-
|
14
|
-
import { LobeDBSchemaMap, browserDB } from './db';
|
15
|
-
|
16
|
-
const LOG_NAME_SPACE = 'DataSync';
|
17
|
-
|
18
|
-
class DataSync {
|
19
|
-
private _ydoc: Doc | null = null;
|
20
|
-
private provider: WebrtcProvider | null = null;
|
21
|
-
|
22
|
-
private syncParams!: StartDataSyncParams;
|
23
|
-
private onAwarenessChange!: OnAwarenessChange;
|
24
|
-
|
25
|
-
private waitForConnecting: any;
|
26
|
-
|
27
|
-
logger = Debug(LOG_NAME_SPACE);
|
28
|
-
|
29
|
-
transact(fn: (transaction: Transaction) => unknown) {
|
30
|
-
this._ydoc?.transact(fn);
|
31
|
-
}
|
32
|
-
|
33
|
-
getYMap = (tableKey: keyof LobeDBSchemaMap) => {
|
34
|
-
return this._ydoc?.getMap(tableKey);
|
35
|
-
};
|
36
|
-
|
37
|
-
startDataSync = async (params: StartDataSyncParams) => {
|
38
|
-
this.syncParams = params;
|
39
|
-
this.onAwarenessChange = params.onAwarenessChange;
|
40
|
-
|
41
|
-
// 开发时由于存在 fast refresh 全局实例会缓存在运行时中
|
42
|
-
// 因此需要在每次重新连接时清理上一次的实例
|
43
|
-
if (window.__ONLY_USE_FOR_CLEANUP_IN_DEV) {
|
44
|
-
await this.cleanConnection(window.__ONLY_USE_FOR_CLEANUP_IN_DEV);
|
45
|
-
}
|
46
|
-
|
47
|
-
await this.connect(params);
|
48
|
-
};
|
49
|
-
|
50
|
-
connect = async (params: StartDataSyncParams) => {
|
51
|
-
const { channel, onSyncEvent, onSyncStatusChange, user, onAwarenessChange, signaling } = params;
|
52
|
-
// ====== 1. init yjs doc ====== //
|
53
|
-
|
54
|
-
await this.initYDoc();
|
55
|
-
|
56
|
-
this.logger('[YJS] start to listen sync event...');
|
57
|
-
this.initYjsObserve(onSyncEvent, onSyncStatusChange);
|
58
|
-
|
59
|
-
// ====== 2. init webrtc provider ====== //
|
60
|
-
this.logger(`[WebRTC] init provider... room: ${channel.name}`);
|
61
|
-
const { WebrtcProvider } = await import('y-webrtc');
|
62
|
-
|
63
|
-
// clients connected to the same room-name share document updates
|
64
|
-
this.provider = new WebrtcProvider(channel.name, this._ydoc!, {
|
65
|
-
password: channel.password,
|
66
|
-
signaling: [signaling],
|
67
|
-
});
|
68
|
-
|
69
|
-
// when fast refresh in dev, the provider will be cached in window
|
70
|
-
// so we need to clean it in destory
|
71
|
-
if (process.env.NODE_ENV === 'development') {
|
72
|
-
window.__ONLY_USE_FOR_CLEANUP_IN_DEV = this.provider;
|
73
|
-
}
|
74
|
-
|
75
|
-
this.logger(`[WebRTC] provider init success`);
|
76
|
-
|
77
|
-
// ====== 3. check signaling server connection ====== //
|
78
|
-
|
79
|
-
// 当本地设备正确连接到 WebRTC Provider 后,触发 status 事件
|
80
|
-
// 当开始连接,则开始监听事件
|
81
|
-
this.provider.on('status', async ({ connected }) => {
|
82
|
-
this.logger('[WebRTC] peer status:', connected);
|
83
|
-
if (connected) {
|
84
|
-
// this.initObserve(onSyncEvent, onSyncStatusChange);
|
85
|
-
onSyncStatusChange?.(PeerSyncStatus.Connecting);
|
86
|
-
}
|
87
|
-
});
|
88
|
-
|
89
|
-
// check the connection with signaling server
|
90
|
-
let connectionCheckCount = 0;
|
91
|
-
|
92
|
-
this.waitForConnecting = setInterval(() => {
|
93
|
-
const signalingConnection: IWebsocketClient = this.provider!.signalingConns[0];
|
94
|
-
|
95
|
-
if (signalingConnection.connected) {
|
96
|
-
onSyncStatusChange?.(PeerSyncStatus.Ready);
|
97
|
-
clearInterval(this.waitForConnecting);
|
98
|
-
return;
|
99
|
-
}
|
100
|
-
|
101
|
-
connectionCheckCount += 1;
|
102
|
-
|
103
|
-
// check for 5 times, or make it failed
|
104
|
-
if (connectionCheckCount > 5) {
|
105
|
-
onSyncStatusChange?.(PeerSyncStatus.Unconnected);
|
106
|
-
clearInterval(this.waitForConnecting);
|
107
|
-
}
|
108
|
-
}, 2000);
|
109
|
-
|
110
|
-
// ====== 4. handle data sync ====== //
|
111
|
-
|
112
|
-
// 当各方的数据均完成同步后,YJS 对象之间的数据已经一致时,触发 synced 事件
|
113
|
-
this.provider.on('synced', async ({ synced }) => {
|
114
|
-
this.logger('[WebRTC] peer sync status:', synced);
|
115
|
-
if (synced) {
|
116
|
-
this.logger('[WebRTC] start to init yjs data...');
|
117
|
-
onSyncStatusChange?.(PeerSyncStatus.Syncing);
|
118
|
-
await this.initSync();
|
119
|
-
onSyncStatusChange?.(PeerSyncStatus.Synced);
|
120
|
-
this.logger('[WebRTC] yjs data init success');
|
121
|
-
} else {
|
122
|
-
this.logger('[WebRTC] data not sync, try to reconnect in 1s...');
|
123
|
-
// await this.reconnect(params);
|
124
|
-
setTimeout(() => {
|
125
|
-
onSyncStatusChange?.(PeerSyncStatus.Syncing);
|
126
|
-
this.reconnect(params);
|
127
|
-
}, 1000);
|
128
|
-
}
|
129
|
-
});
|
130
|
-
|
131
|
-
// ====== 5. handle awareness ====== //
|
132
|
-
|
133
|
-
this.initAwareness({ onAwarenessChange, user });
|
134
|
-
|
135
|
-
return this.provider;
|
136
|
-
};
|
137
|
-
|
138
|
-
reconnect = async (params: StartDataSyncParams) => {
|
139
|
-
await this.cleanConnection(this.provider);
|
140
|
-
|
141
|
-
await this.connect(params);
|
142
|
-
};
|
143
|
-
|
144
|
-
async disconnect() {
|
145
|
-
await this.cleanConnection(this.provider);
|
146
|
-
}
|
147
|
-
|
148
|
-
private initYDoc = async () => {
|
149
|
-
if (typeof window === 'undefined') return;
|
150
|
-
|
151
|
-
this.logger('[YJS] init YDoc...');
|
152
|
-
const { Doc } = await import('yjs');
|
153
|
-
this._ydoc = new Doc();
|
154
|
-
};
|
155
|
-
|
156
|
-
private async cleanConnection(provider: WebrtcProvider | null) {
|
157
|
-
if (provider) {
|
158
|
-
this.logger(`[WebRTC] clean Connection...`);
|
159
|
-
this.logger(`[WebRTC] clean awareness...`);
|
160
|
-
provider.awareness.destroy();
|
161
|
-
|
162
|
-
this.logger(`[WebRTC] clean room...`);
|
163
|
-
provider.room?.disconnect();
|
164
|
-
provider.room?.destroy();
|
165
|
-
|
166
|
-
this.logger(`[WebRTC] clean provider...`);
|
167
|
-
provider.disconnect();
|
168
|
-
provider.destroy();
|
169
|
-
|
170
|
-
this.logger(`[WebRTC] clean yjs doc...`);
|
171
|
-
this._ydoc?.destroy();
|
172
|
-
|
173
|
-
this.logger(`[WebRTC] -------------------`);
|
174
|
-
}
|
175
|
-
}
|
176
|
-
|
177
|
-
private initSync = async () => {
|
178
|
-
await Promise.all(
|
179
|
-
['sessions', 'sessionGroups', 'topics', 'messages', 'plugins'].map(async (tableKey) =>
|
180
|
-
this.loadDataFromDBtoYjs(tableKey as keyof LobeDBSchemaMap),
|
181
|
-
),
|
182
|
-
);
|
183
|
-
};
|
184
|
-
|
185
|
-
private initYjsObserve = (onEvent: OnSyncEvent, onSyncStatusChange: OnSyncStatusChange) => {
|
186
|
-
['sessions', 'sessionGroups', 'topics', 'messages', 'plugins'].forEach((tableKey) => {
|
187
|
-
// listen yjs change
|
188
|
-
this.observeYMapChange(tableKey as keyof LobeDBSchemaMap, onEvent, onSyncStatusChange);
|
189
|
-
});
|
190
|
-
};
|
191
|
-
|
192
|
-
private observeYMapChange = (
|
193
|
-
tableKey: keyof LobeDBSchemaMap,
|
194
|
-
onEvent: OnSyncEvent,
|
195
|
-
onSyncStatusChange: OnSyncStatusChange,
|
196
|
-
) => {
|
197
|
-
const table = browserDB[tableKey];
|
198
|
-
const yItemMap = this.getYMap(tableKey);
|
199
|
-
const updateSyncEvent = throttle(onEvent, 1000);
|
200
|
-
|
201
|
-
// 定义一个变量来保存定时器的ID
|
202
|
-
// eslint-disable-next-line no-undef
|
203
|
-
let debounceTimer: NodeJS.Timeout;
|
204
|
-
|
205
|
-
yItemMap?.observe(async (event) => {
|
206
|
-
// abort local change
|
207
|
-
if (event.transaction.local) return;
|
208
|
-
|
209
|
-
// 每次有变更时,都先清除之前的定时器(如果有的话),然后设置新的定时器
|
210
|
-
clearTimeout(debounceTimer);
|
211
|
-
|
212
|
-
onSyncStatusChange(PeerSyncStatus.Syncing);
|
213
|
-
|
214
|
-
this.logger(`[YJS] observe ${tableKey} changes:`, event.keysChanged.size);
|
215
|
-
const pools = Array.from(event.keys).map(async ([id, payload]) => {
|
216
|
-
const item: any = yItemMap.get(id);
|
217
|
-
|
218
|
-
switch (payload.action) {
|
219
|
-
case 'add':
|
220
|
-
case 'update': {
|
221
|
-
await table.put(item, id);
|
222
|
-
|
223
|
-
break;
|
224
|
-
}
|
225
|
-
|
226
|
-
case 'delete': {
|
227
|
-
await table.delete(id);
|
228
|
-
break;
|
229
|
-
}
|
230
|
-
}
|
231
|
-
});
|
232
|
-
|
233
|
-
await Promise.all(pools);
|
234
|
-
|
235
|
-
updateSyncEvent(tableKey);
|
236
|
-
|
237
|
-
// 设置定时器,2000ms 后更新状态为'synced'
|
238
|
-
debounceTimer = setTimeout(() => {
|
239
|
-
onSyncStatusChange(PeerSyncStatus.Synced);
|
240
|
-
}, 2000);
|
241
|
-
});
|
242
|
-
};
|
243
|
-
|
244
|
-
private loadDataFromDBtoYjs = async (tableKey: keyof LobeDBSchemaMap) => {
|
245
|
-
const table = browserDB[tableKey];
|
246
|
-
const items = await table.toArray();
|
247
|
-
const yItemMap = this.getYMap(tableKey);
|
248
|
-
|
249
|
-
// 定义每批次最多包含的数据条数
|
250
|
-
const batchSize = 50;
|
251
|
-
|
252
|
-
// 计算总批次数
|
253
|
-
const totalBatches = Math.ceil(items.length / batchSize);
|
254
|
-
|
255
|
-
for (let i = 0; i < totalBatches; i++) {
|
256
|
-
// 计算当前批次的起始和结束索引
|
257
|
-
const start = i * batchSize;
|
258
|
-
const end = start + batchSize;
|
259
|
-
|
260
|
-
// 获取当前批次的数据
|
261
|
-
const batchItems = items.slice(start, end);
|
262
|
-
|
263
|
-
// 将当前批次的数据推送到 Yjs 中
|
264
|
-
this._ydoc?.transact(() => {
|
265
|
-
batchItems.forEach((item) => {
|
266
|
-
yItemMap!.set(item.id, item);
|
267
|
-
});
|
268
|
-
});
|
269
|
-
}
|
270
|
-
|
271
|
-
this.logger('[DB]:', tableKey, yItemMap?.size);
|
272
|
-
};
|
273
|
-
|
274
|
-
private initAwareness = ({ user }: Pick<StartDataSyncParams, 'user' | 'onAwarenessChange'>) => {
|
275
|
-
if (!this.provider) return;
|
276
|
-
|
277
|
-
const awareness = this.provider.awareness;
|
278
|
-
|
279
|
-
awareness.setLocalState({ clientID: awareness.clientID, user });
|
280
|
-
this.onAwarenessChange?.([{ ...user, clientID: awareness.clientID, current: true }]);
|
281
|
-
|
282
|
-
awareness.on('change', () => this.syncAwarenessToUI());
|
283
|
-
};
|
284
|
-
|
285
|
-
private syncAwarenessToUI = async () => {
|
286
|
-
const awareness = this.provider?.awareness;
|
287
|
-
|
288
|
-
if (!awareness) return;
|
289
|
-
|
290
|
-
const state = Array.from(awareness.getStates().values()).map((s) => ({
|
291
|
-
...s.user,
|
292
|
-
clientID: s.clientID,
|
293
|
-
current: s.clientID === awareness.clientID,
|
294
|
-
}));
|
295
|
-
|
296
|
-
this.onAwarenessChange?.(uniqBy(state, 'id'));
|
297
|
-
};
|
298
|
-
}
|
299
|
-
|
300
|
-
export const dataSync = new DataSync();
|
301
|
-
|
302
|
-
interface IWebsocketClient {
|
303
|
-
binaryType: 'arraybuffer' | 'blob' | null;
|
304
|
-
connect(): void;
|
305
|
-
connected: boolean;
|
306
|
-
connecting: boolean;
|
307
|
-
destroy(): void;
|
308
|
-
disconnect(): void;
|
309
|
-
lastMessageReceived: number;
|
310
|
-
send(message: any): void;
|
311
|
-
shouldConnect: boolean;
|
312
|
-
unsuccessfulReconnects: number;
|
313
|
-
url: string;
|
314
|
-
ws: WebSocket;
|
315
|
-
}
|
316
|
-
|
317
|
-
declare global {
|
318
|
-
interface Window {
|
319
|
-
__ONLY_USE_FOR_CLEANUP_IN_DEV?: WebrtcProvider | null;
|
320
|
-
}
|
321
|
-
}
|
@@ -1,79 +0,0 @@
|
|
1
|
-
import { Button, Icon, Tag } from '@lobehub/ui';
|
2
|
-
import { Badge, Popover } from 'antd';
|
3
|
-
import { TooltipPlacement } from 'antd/es/tooltip';
|
4
|
-
import { LucideCloudCog, LucideCloudy } from 'lucide-react';
|
5
|
-
import Link from 'next/link';
|
6
|
-
import { memo } from 'react';
|
7
|
-
import { useTranslation } from 'react-i18next';
|
8
|
-
import { Flexbox } from 'react-layout-kit';
|
9
|
-
|
10
|
-
import { useUserStore } from '@/store/user';
|
11
|
-
import { syncSettingsSelectors } from '@/store/user/selectors';
|
12
|
-
|
13
|
-
interface DisableSyncProps {
|
14
|
-
noPopover?: boolean;
|
15
|
-
placement?: TooltipPlacement;
|
16
|
-
}
|
17
|
-
|
18
|
-
const DisableSync = memo<DisableSyncProps>(({ noPopover, placement = 'bottomLeft' }) => {
|
19
|
-
const { t } = useTranslation('common');
|
20
|
-
const [haveConfig, setSettings] = useUserStore((s) => [
|
21
|
-
!!syncSettingsSelectors.webrtcConfig(s).channelName,
|
22
|
-
s.setSettings,
|
23
|
-
]);
|
24
|
-
|
25
|
-
const enableSync = () => {
|
26
|
-
setSettings({ sync: { webrtc: { enabled: true } } });
|
27
|
-
};
|
28
|
-
|
29
|
-
const tag = (
|
30
|
-
<div>
|
31
|
-
<Tag>
|
32
|
-
<Badge status="default" />
|
33
|
-
{t('sync.status.disabled')}
|
34
|
-
</Tag>
|
35
|
-
</div>
|
36
|
-
);
|
37
|
-
|
38
|
-
return noPopover ? (
|
39
|
-
tag
|
40
|
-
) : (
|
41
|
-
<Popover
|
42
|
-
arrow={false}
|
43
|
-
content={
|
44
|
-
<Flexbox gap={12} width={320}>
|
45
|
-
{t('sync.disabled.desc')}
|
46
|
-
{haveConfig ? (
|
47
|
-
<Flexbox gap={8} horizontal>
|
48
|
-
<Link href={'/settings/sync'}>
|
49
|
-
<Button block icon={<Icon icon={LucideCloudCog} />}>
|
50
|
-
{t('sync.disabled.actions.settings')}
|
51
|
-
</Button>
|
52
|
-
</Link>
|
53
|
-
<Button block onClick={enableSync} type={'primary'}>
|
54
|
-
{t('sync.disabled.actions.enable')}
|
55
|
-
</Button>
|
56
|
-
</Flexbox>
|
57
|
-
) : (
|
58
|
-
<Link href={'/settings/sync'}>
|
59
|
-
<Button block icon={<Icon icon={LucideCloudCog} />} type={'primary'}>
|
60
|
-
{t('sync.disabled.actions.settings')}
|
61
|
-
</Button>
|
62
|
-
</Link>
|
63
|
-
)}
|
64
|
-
</Flexbox>
|
65
|
-
}
|
66
|
-
placement={placement}
|
67
|
-
title={
|
68
|
-
<Flexbox gap={8} horizontal>
|
69
|
-
<Icon icon={LucideCloudy} />
|
70
|
-
{t('sync.disabled.title')}
|
71
|
-
</Flexbox>
|
72
|
-
}
|
73
|
-
>
|
74
|
-
{tag}
|
75
|
-
</Popover>
|
76
|
-
);
|
77
|
-
});
|
78
|
-
|
79
|
-
export default DisableSync;
|
@@ -1,132 +0,0 @@
|
|
1
|
-
import { ActionIcon, Avatar, Icon, Tag, Text } from '@lobehub/ui';
|
2
|
-
import { Divider, Popover, Switch } from 'antd';
|
3
|
-
import { createStyles } from 'antd-style';
|
4
|
-
import { TooltipPlacement } from 'antd/es/tooltip';
|
5
|
-
import isEqual from 'fast-deep-equal';
|
6
|
-
import { LucideCloudy, LucideLaptop, LucideSmartphone, SettingsIcon } from 'lucide-react';
|
7
|
-
import Link from 'next/link';
|
8
|
-
import { memo } from 'react';
|
9
|
-
import { useTranslation } from 'react-i18next';
|
10
|
-
import { Flexbox } from 'react-layout-kit';
|
11
|
-
|
12
|
-
import { useUserStore } from '@/store/user';
|
13
|
-
import { syncSettingsSelectors } from '@/store/user/selectors';
|
14
|
-
import { pathString } from '@/utils/url';
|
15
|
-
|
16
|
-
import EnableTag from './EnableTag';
|
17
|
-
|
18
|
-
const useStyles = createStyles(({ css, token, prefixCls }) => ({
|
19
|
-
text: css`
|
20
|
-
max-width: 100%;
|
21
|
-
color: ${token.colorTextTertiary};
|
22
|
-
.${prefixCls}-typography-copy {
|
23
|
-
color: ${token.colorTextTertiary};
|
24
|
-
}
|
25
|
-
`,
|
26
|
-
title: css`
|
27
|
-
color: ${token.colorTextTertiary};
|
28
|
-
`,
|
29
|
-
}));
|
30
|
-
|
31
|
-
interface EnableSyncProps {
|
32
|
-
hiddenActions?: boolean;
|
33
|
-
placement?: TooltipPlacement;
|
34
|
-
}
|
35
|
-
|
36
|
-
const EnableSync = memo<EnableSyncProps>(({ hiddenActions, placement = 'bottomLeft' }) => {
|
37
|
-
const { t } = useTranslation('common');
|
38
|
-
|
39
|
-
const { styles, theme } = useStyles();
|
40
|
-
const [syncStatus, isSyncing, channelName, enableWebRTC, setSettings] = useUserStore((s) => [
|
41
|
-
s.syncStatus,
|
42
|
-
s.syncStatus === 'syncing',
|
43
|
-
syncSettingsSelectors.webrtcChannelName(s),
|
44
|
-
syncSettingsSelectors.enableWebRTC(s),
|
45
|
-
s.setSettings,
|
46
|
-
]);
|
47
|
-
|
48
|
-
const users = useUserStore((s) => s.syncAwareness, isEqual);
|
49
|
-
|
50
|
-
const switchSync = (enabled: boolean) => {
|
51
|
-
setSettings({ sync: { webrtc: { enabled } } });
|
52
|
-
};
|
53
|
-
|
54
|
-
return (
|
55
|
-
<Popover
|
56
|
-
arrow={false}
|
57
|
-
content={
|
58
|
-
<Flexbox gap={16}>
|
59
|
-
<Flexbox align={'center'} gap={24} horizontal>
|
60
|
-
<Flexbox
|
61
|
-
align={'center'}
|
62
|
-
className={styles.title}
|
63
|
-
gap={4}
|
64
|
-
horizontal
|
65
|
-
style={{ paddingInlineEnd: 12 }}
|
66
|
-
>
|
67
|
-
{t('sync.channel')}
|
68
|
-
<Text className={styles.text}>{channelName}</Text>
|
69
|
-
</Flexbox>
|
70
|
-
</Flexbox>
|
71
|
-
<Divider dashed style={{ margin: 0 }} />
|
72
|
-
<Flexbox gap={12}>
|
73
|
-
{users.map((user) => (
|
74
|
-
<Flexbox gap={12} horizontal key={user.clientID}>
|
75
|
-
<Avatar
|
76
|
-
avatar={
|
77
|
-
<Icon
|
78
|
-
color={theme.purple}
|
79
|
-
icon={user.isMobile ? LucideSmartphone : LucideLaptop}
|
80
|
-
size={24}
|
81
|
-
/>
|
82
|
-
}
|
83
|
-
background={theme.purple1}
|
84
|
-
shape={'square'}
|
85
|
-
/>
|
86
|
-
|
87
|
-
<Flexbox>
|
88
|
-
<Flexbox gap={8} horizontal>
|
89
|
-
{user.name || user.id}
|
90
|
-
{user.current && (
|
91
|
-
<Flexbox horizontal>
|
92
|
-
<Tag bordered={false} color={'blue'}>
|
93
|
-
{t('sync.awareness.current')}
|
94
|
-
</Tag>
|
95
|
-
</Flexbox>
|
96
|
-
)}
|
97
|
-
</Flexbox>
|
98
|
-
<Text type={'secondary'}>
|
99
|
-
{user.os} · {user.browser}
|
100
|
-
</Text>
|
101
|
-
</Flexbox>
|
102
|
-
</Flexbox>
|
103
|
-
))}
|
104
|
-
</Flexbox>
|
105
|
-
</Flexbox>
|
106
|
-
}
|
107
|
-
placement={placement}
|
108
|
-
title={
|
109
|
-
<Flexbox distribution={'space-between'} horizontal>
|
110
|
-
<Flexbox align={'center'} gap={8} horizontal>
|
111
|
-
<Icon icon={LucideCloudy} />
|
112
|
-
{t('sync.title')}
|
113
|
-
{!hiddenActions && (
|
114
|
-
<Switch checked={enableWebRTC} onChange={switchSync} size={'small'} />
|
115
|
-
)}
|
116
|
-
</Flexbox>
|
117
|
-
{!hiddenActions && (
|
118
|
-
<Link href={pathString('/settings/sync')}>
|
119
|
-
<ActionIcon icon={SettingsIcon} title={t('sync.actions.settings')} />
|
120
|
-
</Link>
|
121
|
-
)}
|
122
|
-
</Flexbox>
|
123
|
-
}
|
124
|
-
>
|
125
|
-
<div>
|
126
|
-
<EnableTag isSyncing={isSyncing} status={syncStatus} />
|
127
|
-
</div>
|
128
|
-
</Popover>
|
129
|
-
);
|
130
|
-
});
|
131
|
-
|
132
|
-
export default EnableSync;
|