Skip to content

Commit

Permalink
feat: translate EmbedModal #345 (#455)
Browse files Browse the repository at this point in the history
### What problem does this PR solve?

Embed the chat window into other websites through iframe

#345 

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
  • Loading branch information
cike8899 committed Apr 19, 2024
1 parent 962c667 commit cda7b60
Show file tree
Hide file tree
Showing 14 changed files with 314 additions and 106 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import Markdown from 'react-markdown';
import SyntaxHighlighter from 'react-syntax-highlighter';
import remarkGfm from 'remark-gfm';

const SharedMarkdown = ({ content }: { content: string }) => {
const HightLightMarkdown = ({
children,
}: {
children: string | null | undefined;
}) => {
return (
<Markdown
remarkPlugins={[remarkGfm]}
Expand All @@ -24,9 +28,9 @@ const SharedMarkdown = ({ content }: { content: string }) => {
} as any
}
>
{content}
{children}
</Markdown>
);
};

export default SharedMarkdown;
export default HightLightMarkdown;
25 changes: 1 addition & 24 deletions web/src/hooks/chatHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
IStats,
IToken,
} from '@/interfaces/database/chat';
import { useCallback, useEffect, useState } from 'react';
import { useCallback } from 'react';
import { useDispatch, useSelector } from 'umi';

export const useFetchDialogList = () => {
Expand Down Expand Up @@ -299,27 +299,4 @@ export const useCompleteSharedConversation = () => {
return completeSharedConversation;
};

export const useCreatePublicUrlToken = (dialogId: string, visible: boolean) => {
const [token, setToken] = useState();
const createToken = useCreateToken(dialogId);
const { protocol, host } = window.location;

const urlWithToken = `${protocol}//${host}/chat/share?shared_id=${token}`;

const createUrlToken = useCallback(async () => {
if (visible) {
const data = await createToken();
const urlToken = data.data?.token;
if (urlToken) {
setToken(urlToken);
}
}
}, [createToken, visible]);

useEffect(() => {
createUrlToken();
}, [createUrlToken]);

return { token, createUrlToken, urlWithToken };
};
//#endregion
9 changes: 9 additions & 0 deletions web/src/less/mixins.less
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,12 @@
.pointerCursor() {
cursor: pointer;
}

.clearCardBody() {
:global {
.ant-card-body {
padding: 0;
margin: 0;
}
}
}
10 changes: 9 additions & 1 deletion web/src/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ export default {
'This sets the maximum length of the model’s output, measured in the number of tokens (words or pieces of words).',
quote: 'Show Quote',
quoteTip: 'Should the source of the original text be displayed?',
overview: 'Overview',
overview: 'API',
pv: 'Number of messages',
uv: 'Active user number',
speed: 'Token output speed',
Expand All @@ -367,6 +367,14 @@ export default {
createNewKey: 'Create new key',
created: 'Created',
action: 'Action',
embedModalTitle: 'Embed into website',
comingSoon: 'Coming Soon',
fullScreenTitle: 'Full Embed',
fullScreenDescription:
'Embed the following iframe into your website at the desired location',
partialTitle: 'Partial Embed',
extensionTitle: 'Chrome Extension',
tokenError: 'Please create API Token first!',
},
setting: {
profile: 'Profile',
Expand Down
9 changes: 8 additions & 1 deletion web/src/locales/zh-traditional.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ export default {
'這設置了模型輸出的最大長度,以標記(單詞或單詞片段)的數量來衡量。',
quote: '顯示引文',
quoteTip: '是否應該顯示原文出處?',
overview: '概覽',
overview: 'API',
pv: '消息數',
uv: '活躍用戶數',
speed: 'Token 輸出速度',
Expand All @@ -339,6 +339,13 @@ export default {
createNewKey: '創建新密鑰',
created: '創建於',
action: '操作',
embedModalTitle: '嵌入網站',
comingSoon: '即將推出',
fullScreenTitle: '全屏嵌入',
fullScreenDescription: '將以下iframe嵌入您的網站處於所需位置',
partialTitle: '部分嵌入',
extensionTitle: 'Chrome 插件',
tokenError: '請先創建 Api Token!',
},
setting: {
profile: '概述',
Expand Down
9 changes: 8 additions & 1 deletion web/src/locales/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ export default {
'这设置了模型输出的最大长度,以标记(单词或单词片段)的数量来衡量。',
quote: '显示引文',
quoteTip: '是否应该显示原文出处?',
overview: '概览',
overview: 'API',
pv: '消息数',
uv: '活跃用户数',
speed: 'Token 输出速度',
Expand All @@ -356,6 +356,13 @@ export default {
createNewKey: '创建新密钥',
created: '创建于',
action: '操作',
embedModalTitle: '嵌入网站',
comingSoon: '即将推出',
fullScreenTitle: '全屏嵌入',
fullScreenDescription: '将以下iframe嵌入您的网站处于所需位置',
partialTitle: '部分嵌入',
extensionTitle: 'Chrome 插件',
tokenError: '请先创建 Api Token!',
},
setting: {
profile: '概要',
Expand Down
78 changes: 48 additions & 30 deletions web/src/pages/chat/chat-overview-modal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import CopyToClipboard from '@/components/copy-to-clipboard';
import LineChart from '@/components/line-chart';
import { useCreatePublicUrlToken } from '@/hooks/chatHooks';
import { useSetModalState, useTranslate } from '@/hooks/commonHooks';
import { IModalProps } from '@/interfaces/common';
import { IDialog, IStats } from '@/interfaces/database/chat';
import { ReloadOutlined } from '@ant-design/icons';
import { Button, Card, DatePicker, Flex, Modal, Space, Typography } from 'antd';
import { RangePickerProps } from 'antd/es/date-picker';
import dayjs from 'dayjs';
import camelCase from 'lodash/camelCase';
import { Link } from 'umi';
import ChatApiKeyModal from '../chat-api-key-modal';
import { useFetchStatsOnMount, useSelectChartStatsList } from '../hooks';
import EmbedModal from '../embed-modal';
import {
useFetchStatsOnMount,
usePreviewChat,
useSelectChartStatsList,
useShowEmbedModal,
} from '../hooks';
import styles from './index.less';

const { Paragraph } = Typography;
Expand All @@ -24,23 +26,27 @@ const ChatOverviewModal = ({
}: IModalProps<any> & { dialog: IDialog }) => {
const { t } = useTranslate('chat');
const chartList = useSelectChartStatsList();
const { urlWithToken, createUrlToken, token } = useCreatePublicUrlToken(
dialog.id,
visible,
);

const {
visible: apiKeyVisible,
hideModal: hideApiKeyModal,
showModal: showApiKeyModal,
} = useSetModalState();
const {
embedVisible,
hideEmbedModal,
showEmbedModal,
embedToken,
errorContextHolder,
} = useShowEmbedModal(dialog.id);

const { pickerValue, setPickerValue } = useFetchStatsOnMount(visible);

const disabledDate: RangePickerProps['disabledDate'] = (current) => {
return current && current > dayjs().endOf('day');
};

const { handlePreview, contextHolder } = usePreviewChat(dialog.id);

return (
<>
<Modal
Expand All @@ -50,36 +56,41 @@ const ChatOverviewModal = ({
width={'100vw'}
>
<Flex vertical gap={'middle'}>
<Card title={dialog.name}>
<Flex gap={8} vertical>
{t('publicUrl')}
<Flex className={styles.linkText} gap={10}>
<span>{urlWithToken}</span>
<CopyToClipboard text={urlWithToken}></CopyToClipboard>
<ReloadOutlined onClick={createUrlToken} />
</Flex>
<Space size={'middle'}>
<Button>
<Link to={`/chat/share?shared_id=${token}`} target="_blank">
{t('preview')}
</Link>
</Button>
<Button>{t('embedded')}</Button>
</Space>
</Flex>
</Card>
<Card title={t('backendServiceApi')}>
<Flex gap={8} vertical>
{t('serviceApiEndpoint')}
<Paragraph copyable className={styles.linkText}>
This is a copyable text.
https://demo.ragflow.io/v1/api/
</Paragraph>
</Flex>
<Space size={'middle'}>
<Button onClick={showApiKeyModal}>{t('apiKey')}</Button>
<Button>{t('apiReference')}</Button>
<a
href={
'https://github.com/infiniflow/ragflow/blob/main/docs/conversation_api.md'
}
target="_blank"
rel="noreferrer"
>
<Button>{t('apiReference')}</Button>
</a>
</Space>
</Card>
<Card title={dialog.name}>
<Flex gap={8} vertical>
{t('publicUrl')}
{/* <Flex className={styles.linkText} gap={10}>
<span>{urlWithToken}</span>
<CopyToClipboard text={urlWithToken}></CopyToClipboard>
<ReloadOutlined onClick={createUrlToken} />
</Flex> */}
<Space size={'middle'}>
<Button onClick={handlePreview}>{t('preview')}</Button>
<Button onClick={showEmbedModal}>{t('embedded')}</Button>
</Space>
</Flex>
</Card>

<Space>
<b>{t('dateRange')}</b>
<RangePicker
Expand All @@ -103,6 +114,13 @@ const ChatOverviewModal = ({
hideModal={hideApiKeyModal}
dialogId={dialog.id}
></ChatApiKeyModal>
<EmbedModal
token={embedToken}
visible={embedVisible}
hideModal={hideEmbedModal}
></EmbedModal>
{contextHolder}
{errorContextHolder}
</Modal>
</>
);
Expand Down
8 changes: 8 additions & 0 deletions web/src/pages/chat/embed-modal/index.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.codeCard {
.clearCardBody();
}

.codeText {
padding: 10px;
background-color: #e8e8ea;
}
70 changes: 70 additions & 0 deletions web/src/pages/chat/embed-modal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import CopyToClipboard from '@/components/copy-to-clipboard';
import HightLightMarkdown from '@/components/highlight-markdown';
import { useTranslate } from '@/hooks/commonHooks';
import { IModalProps } from '@/interfaces/common';
import { Card, Modal, Tabs, TabsProps } from 'antd';
import styles from './index.less';

const EmbedModal = ({
visible,
hideModal,
token = '',
}: IModalProps<any> & { token: string }) => {
const { t } = useTranslate('chat');

const text = `
~~~ html
<iframe
src="https://demo.ragflow.io/chat/share?shared_id=${token}"
style="width: 100%; height: 100%; min-height: 600px"
frameborder="0"
>
</iframe>
~~~
`;

const items: TabsProps['items'] = [
{
key: '1',
label: t('fullScreenTitle'),
children: (
<Card
title={t('fullScreenDescription')}
extra={<CopyToClipboard text={text}></CopyToClipboard>}
className={styles.codeCard}
>
<HightLightMarkdown>{text}</HightLightMarkdown>
</Card>
),
},
{
key: '2',
label: t('partialTitle'),
children: t('comingSoon'),
},
{
key: '3',
label: t('extensionTitle'),
children: t('comingSoon'),
},
];

const onChange = (key: string) => {
console.log(key);
};

return (
<Modal
title={t('embedModalTitle')}
open={visible}
style={{ top: 300 }}
width={'50vw'}
onOk={hideModal}
onCancel={hideModal}
>
<Tabs defaultActiveKey="1" items={items} onChange={onChange} />
</Modal>
);
};

export default EmbedModal;
Loading

0 comments on commit cda7b60

Please sign in to comment.