mirror of https://gitee.com/answerdev/answer.git
Merge remote-tracking branch 'github/feat/1.1.2/ui' into feat/1.1.2/user-center
This commit is contained in:
commit
673ebfbb78
|
@ -1478,6 +1478,9 @@ ui:
|
||||||
footer:
|
footer:
|
||||||
label: Footer
|
label: Footer
|
||||||
text: This will insert before </html>.
|
text: This will insert before </html>.
|
||||||
|
sidebar:
|
||||||
|
label: Sidebar
|
||||||
|
text: This will insert in sidebar.
|
||||||
login:
|
login:
|
||||||
page_title: Login
|
page_title: Login
|
||||||
membership:
|
membership:
|
||||||
|
|
|
@ -384,6 +384,7 @@ export interface AdminSettingsCustom {
|
||||||
custom_head: string;
|
custom_head: string;
|
||||||
custom_header: string;
|
custom_header: string;
|
||||||
custom_footer: string;
|
custom_footer: string;
|
||||||
|
custom_sidebar: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AdminSettingsLogin {
|
export interface AdminSettingsLogin {
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
const pattern = {
|
const pattern = {
|
||||||
email:
|
email:
|
||||||
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+\.)+[a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{2,}))$/,
|
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+\.)+[a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{2,}))$/,
|
||||||
|
wx: /micromessenger/,
|
||||||
|
wxwork: /wxwork/,
|
||||||
|
dingtalk: /dingtalk/,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default pattern;
|
export default pattern;
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { memo } from 'react';
|
||||||
|
|
||||||
|
import { customizeStore } from '@/stores';
|
||||||
|
|
||||||
|
const Index = () => {
|
||||||
|
const { custom_sidebar } = customizeStore((state) => state);
|
||||||
|
if (!custom_sidebar) return null;
|
||||||
|
return <div dangerouslySetInnerHTML={{ __html: custom_sidebar }} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(Index);
|
|
@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { usePageTags } from '@/hooks';
|
import { usePageTags } from '@/hooks';
|
||||||
|
|
||||||
const Index = ({ httpCode = '', errMsg = '', showErroCode = true }) => {
|
const Index = ({ httpCode = '', errMsg = '', showErrorCode = true }) => {
|
||||||
const { t } = useTranslation('translation', { keyPrefix: 'page_error' });
|
const { t } = useTranslation('translation', { keyPrefix: 'page_error' });
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// auto height of container
|
// auto height of container
|
||||||
|
@ -31,7 +31,7 @@ const Index = ({ httpCode = '', errMsg = '', showErroCode = true }) => {
|
||||||
style={{ fontSize: '120px', lineHeight: 1.2 }}>
|
style={{ fontSize: '120px', lineHeight: 1.2 }}>
|
||||||
(=‘x‘=)
|
(=‘x‘=)
|
||||||
</div>
|
</div>
|
||||||
{showErroCode && (
|
{showErrorCode && (
|
||||||
<h4 className="text-center">{t('http_error', { code: httpCode })}</h4>
|
<h4 className="text-center">{t('http_error', { code: httpCode })}</h4>
|
||||||
)}
|
)}
|
||||||
<div className="text-center mb-3 fs-5">
|
<div className="text-center mb-3 fs-5">
|
||||||
|
|
|
@ -38,6 +38,7 @@ import Counts from './Counts';
|
||||||
import QuestionList from './QuestionList';
|
import QuestionList from './QuestionList';
|
||||||
import HotQuestions from './HotQuestions';
|
import HotQuestions from './HotQuestions';
|
||||||
import HttpErrorContent from './HttpErrorContent';
|
import HttpErrorContent from './HttpErrorContent';
|
||||||
|
import CustomSidebar from './CustomSidebar';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Avatar,
|
Avatar,
|
||||||
|
@ -82,5 +83,6 @@ export {
|
||||||
QuestionList,
|
QuestionList,
|
||||||
HotQuestions,
|
HotQuestions,
|
||||||
HttpErrorContent,
|
HttpErrorContent,
|
||||||
|
CustomSidebar,
|
||||||
};
|
};
|
||||||
export type { EditorRef, JSONSchema, UISchema };
|
export type { EditorRef, JSONSchema, UISchema };
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { HttpErrorContent } from '@/components';
|
||||||
const Index = () => {
|
const Index = () => {
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const msg = searchParams.get('msg') || '';
|
const msg = searchParams.get('msg') || '';
|
||||||
return <HttpErrorContent httpCode="50X" errMsg={msg} showErroCode={!msg} />;
|
return <HttpErrorContent httpCode="50X" errMsg={msg} showErrorCode={!msg} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Index;
|
export default Index;
|
||||||
|
|
|
@ -31,6 +31,11 @@ const Index: FC = () => {
|
||||||
title: t('header.label'),
|
title: t('header.label'),
|
||||||
description: t('header.text'),
|
description: t('header.text'),
|
||||||
},
|
},
|
||||||
|
custom_sidebar: {
|
||||||
|
type: 'string',
|
||||||
|
title: t('sidebar.label'),
|
||||||
|
description: t('sidebar.text'),
|
||||||
|
},
|
||||||
custom_footer: {
|
custom_footer: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
title: t('footer.label'),
|
title: t('footer.label'),
|
||||||
|
@ -60,6 +65,13 @@ const Index: FC = () => {
|
||||||
className: ['fs-14', 'font-monospace'],
|
className: ['fs-14', 'font-monospace'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
custom_sidebar: {
|
||||||
|
'ui:widget': 'textarea',
|
||||||
|
'ui:options': {
|
||||||
|
rows: 10,
|
||||||
|
className: ['fs-14', 'font-monospace'],
|
||||||
|
},
|
||||||
|
},
|
||||||
custom_footer: {
|
custom_footer: {
|
||||||
'ui:widget': 'textarea',
|
'ui:widget': 'textarea',
|
||||||
'ui:options': {
|
'ui:options': {
|
||||||
|
@ -77,6 +89,7 @@ const Index: FC = () => {
|
||||||
custom_css: formData.custom_css.value,
|
custom_css: formData.custom_css.value,
|
||||||
custom_head: formData.custom_head.value,
|
custom_head: formData.custom_head.value,
|
||||||
custom_header: formData.custom_header.value,
|
custom_header: formData.custom_header.value,
|
||||||
|
custom_sidebar: formData.custom_sidebar.value,
|
||||||
custom_footer: formData.custom_footer.value,
|
custom_footer: formData.custom_footer.value,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -103,6 +116,7 @@ const Index: FC = () => {
|
||||||
formMeta.custom_css.value = setting.custom_css;
|
formMeta.custom_css.value = setting.custom_css;
|
||||||
formMeta.custom_head.value = setting.custom_head;
|
formMeta.custom_head.value = setting.custom_head;
|
||||||
formMeta.custom_header.value = setting.custom_header;
|
formMeta.custom_header.value = setting.custom_header;
|
||||||
|
formMeta.custom_sidebar.value = setting.custom_sidebar;
|
||||||
formMeta.custom_footer.value = setting.custom_footer;
|
formMeta.custom_footer.value = setting.custom_footer;
|
||||||
setFormData(formMeta);
|
setFormData(formMeta);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
import { memo } from 'react';
|
|
||||||
import { Container, Card, Col, Carousel } from 'react-bootstrap';
|
|
||||||
|
|
||||||
const data = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
url: require('@/assets/images/carousel-wecom-1.jpg'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
url: require('@/assets/images/carousel-wecom-2.jpg'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
url: require('@/assets/images/carousel-wecom-3.jpg'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
url: require('@/assets/images/carousel-wecom-4.jpg'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
url: require('@/assets/images/carousel-wecom-5.jpg'),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const Index = () => {
|
|
||||||
return (
|
|
||||||
<Container>
|
|
||||||
<Col lg={4} className="mx-auto mt-3 py-5">
|
|
||||||
<Card>
|
|
||||||
<Card.Body>
|
|
||||||
<h3 className="text-center pt-3 mb-3">WeCome Login</h3>
|
|
||||||
<p className="text-danger text-center">
|
|
||||||
Login failed, please allow this app to access your email
|
|
||||||
information before try again.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<Carousel controls={false}>
|
|
||||||
{data.map((item) => (
|
|
||||||
<Carousel.Item key={item.id}>
|
|
||||||
<img
|
|
||||||
className="d-block w-100"
|
|
||||||
src={item.url}
|
|
||||||
alt="First slide"
|
|
||||||
/>
|
|
||||||
</Carousel.Item>
|
|
||||||
))}
|
|
||||||
</Carousel>
|
|
||||||
</Card.Body>
|
|
||||||
</Card>
|
|
||||||
</Col>
|
|
||||||
</Container>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(Index);
|
|
|
@ -8,7 +8,7 @@ import {
|
||||||
} from 'react-router-dom';
|
} from 'react-router-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { Pagination } from '@/components';
|
import { Pagination, CustomSidebar } from '@/components';
|
||||||
import { loggedUserInfoStore, toastStore } from '@/stores';
|
import { loggedUserInfoStore, toastStore } from '@/stores';
|
||||||
import { scrollToElementTop } from '@/utils';
|
import { scrollToElementTop } from '@/utils';
|
||||||
import { usePageTags, usePageUsers } from '@/hooks';
|
import { usePageTags, usePageUsers } from '@/hooks';
|
||||||
|
@ -256,6 +256,7 @@ const Index = () => {
|
||||||
)}
|
)}
|
||||||
</Col>
|
</Col>
|
||||||
<Col xxl={3} lg={4} sm={12} className="mt-5 mt-lg-0">
|
<Col xxl={3} lg={4} sm={12} className="mt-5 mt-lg-0">
|
||||||
|
<CustomSidebar />
|
||||||
<RelatedQuestions id={question?.id || ''} />
|
<RelatedQuestions id={question?.id || ''} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
|
@ -4,7 +4,12 @@ import { useMatch, Link, useSearchParams } from 'react-router-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { usePageTags } from '@/hooks';
|
import { usePageTags } from '@/hooks';
|
||||||
import { FollowingTags, QuestionList, HotQuestions } from '@/components';
|
import {
|
||||||
|
FollowingTags,
|
||||||
|
QuestionList,
|
||||||
|
HotQuestions,
|
||||||
|
CustomSidebar,
|
||||||
|
} from '@/components';
|
||||||
import { siteInfoStore, loggedUserInfoStore } from '@/stores';
|
import { siteInfoStore, loggedUserInfoStore } from '@/stores';
|
||||||
import { useQuestionList } from '@/services';
|
import { useQuestionList } from '@/services';
|
||||||
import * as Type from '@/common/interface';
|
import * as Type from '@/common/interface';
|
||||||
|
@ -44,6 +49,7 @@ const Questions: FC = () => {
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xxl={3} lg={4} sm={12} className="mt-5 mt-lg-0">
|
<Col xxl={3} lg={4} sm={12} className="mt-5 mt-lg-0">
|
||||||
|
<CustomSidebar />
|
||||||
{!loggedUser.username && (
|
{!loggedUser.username && (
|
||||||
<div className="card mb-4">
|
<div className="card mb-4">
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { usePageTags } from '@/hooks';
|
import { usePageTags } from '@/hooks';
|
||||||
import * as Type from '@/common/interface';
|
import * as Type from '@/common/interface';
|
||||||
import { FollowingTags } from '@/components';
|
import { FollowingTags, CustomSidebar } from '@/components';
|
||||||
import {
|
import {
|
||||||
useTagInfo,
|
useTagInfo,
|
||||||
useFollow,
|
useFollow,
|
||||||
|
@ -152,6 +152,7 @@ const Questions: FC = () => {
|
||||||
<QuestionList source="tag" data={listData} isLoading={listLoading} />
|
<QuestionList source="tag" data={listData} isLoading={listLoading} />
|
||||||
</Col>
|
</Col>
|
||||||
<Col xxl={3} lg={4} sm={12} className="mt-5 mt-lg-0">
|
<Col xxl={3} lg={4} sm={12} className="mt-5 mt-lg-0">
|
||||||
|
<CustomSidebar />
|
||||||
<FollowingTags />
|
<FollowingTags />
|
||||||
<HotQuestions />
|
<HotQuestions />
|
||||||
</Col>
|
</Col>
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
import { Card, Col, Carousel } from 'react-bootstrap';
|
||||||
|
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
url: require('@/assets/images/carousel-wecom-1.jpg'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
url: require('@/assets/images/carousel-wecom-2.jpg'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
url: require('@/assets/images/carousel-wecom-3.jpg'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
url: require('@/assets/images/carousel-wecom-4.jpg'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
url: require('@/assets/images/carousel-wecom-5.jpg'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const Index: FC = () => {
|
||||||
|
return (
|
||||||
|
<Col lg={4} className="mx-auto mt-3 py-5">
|
||||||
|
<Card>
|
||||||
|
<Card.Body>
|
||||||
|
<h3 className="text-center pt-3 mb-3">WeCom Login</h3>
|
||||||
|
<p className="text-danger text-center">
|
||||||
|
Login failed, please allow this app to access your email information
|
||||||
|
before try again.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<Carousel controls={false}>
|
||||||
|
{data.map((item) => (
|
||||||
|
<Carousel.Item key={item.id}>
|
||||||
|
<img
|
||||||
|
className="d-block w-100"
|
||||||
|
src={item.url}
|
||||||
|
alt="First slide"
|
||||||
|
/>
|
||||||
|
</Carousel.Item>
|
||||||
|
))}
|
||||||
|
</Carousel>
|
||||||
|
</Card.Body>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Index;
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { memo } from 'react';
|
||||||
|
import { Container } from 'react-bootstrap';
|
||||||
|
import { useSearchParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { userCenterStore } from '@/stores';
|
||||||
|
|
||||||
|
import WeCom from './components/WeCom';
|
||||||
|
|
||||||
|
const Index = () => {
|
||||||
|
const [searchParam] = useSearchParams();
|
||||||
|
const { agent: ucAgent } = userCenterStore();
|
||||||
|
let agentName = ucAgent?.agent_info.name || '';
|
||||||
|
if (searchParam.get('agent_name')) {
|
||||||
|
agentName = searchParam.get('agent_name') || '';
|
||||||
|
}
|
||||||
|
return <Container>{/^WeCom/i.test(agentName) ? <WeCom /> : null}</Container>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(Index);
|
|
@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next';
|
||||||
import QrCode from 'qrcode';
|
import QrCode from 'qrcode';
|
||||||
|
|
||||||
import { userCenterStore } from '@/stores';
|
import { userCenterStore } from '@/stores';
|
||||||
import { guard } from '@/utils';
|
import { guard, getUserAgentType } from '@/utils';
|
||||||
|
|
||||||
import { getLoginConf, checkLoginResult } from './wecom.service';
|
import { getLoginConf, checkLoginResult } from './wecom.service';
|
||||||
|
|
||||||
|
@ -47,7 +47,11 @@ const Index: FC = () => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
getLoginConf().then((res) => {
|
getLoginConf().then((res) => {
|
||||||
handleQrCode(res?.redirect_url);
|
if (getUserAgentType() === 'wxwork') {
|
||||||
|
window.location.replace(res?.redirect_url);
|
||||||
|
} else {
|
||||||
|
handleQrCode(res?.redirect_url);
|
||||||
|
}
|
||||||
handleLoginResult(res?.key);
|
handleLoginResult(res?.key);
|
||||||
});
|
});
|
||||||
}, [agentName]);
|
}, [agentName]);
|
||||||
|
@ -56,7 +60,7 @@ const Index: FC = () => {
|
||||||
clearTimeout(checkTimer);
|
clearTimeout(checkTimer);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
if (/WeCom/i.test(agentName)) {
|
if (/WeCom/i.test(agentName) && getUserAgentType() !== 'wxwork') {
|
||||||
return (
|
return (
|
||||||
<Card className="text-center">
|
<Card className="text-center">
|
||||||
<Card.Body>
|
<Card.Body>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { memo, FC } from 'react';
|
import { memo, FC } from 'react';
|
||||||
import { Button } from 'react-bootstrap';
|
import { Button } from 'react-bootstrap';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
|
|
@ -362,6 +362,10 @@ const routes: RouteNode[] = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/user-center/auth-failed',
|
||||||
|
page: 'pages/UserCenter/AuthFailed',
|
||||||
|
},
|
||||||
// for review
|
// for review
|
||||||
{
|
{
|
||||||
path: 'review',
|
path: 'review',
|
||||||
|
@ -375,10 +379,6 @@ const routes: RouteNode[] = [
|
||||||
path: '50x',
|
path: '50x',
|
||||||
page: 'pages/50X',
|
page: 'pages/50X',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'login-fail',
|
|
||||||
page: 'pages/LoginFail',
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,11 +5,13 @@ interface IType {
|
||||||
custom_head: string;
|
custom_head: string;
|
||||||
custom_header: string;
|
custom_header: string;
|
||||||
custom_footer: string;
|
custom_footer: string;
|
||||||
|
custom_sidebar: string;
|
||||||
update: (params: {
|
update: (params: {
|
||||||
custom_css?: string;
|
custom_css?: string;
|
||||||
custom_head?: string;
|
custom_head?: string;
|
||||||
custom_header?: string;
|
custom_header?: string;
|
||||||
custom_footer?: string;
|
custom_footer?: string;
|
||||||
|
custom_sidebar?: string;
|
||||||
}) => void;
|
}) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +20,7 @@ const loginSetting = create<IType>((set) => ({
|
||||||
custom_head: '',
|
custom_head: '',
|
||||||
custom_header: '',
|
custom_header: '',
|
||||||
custom_footer: '',
|
custom_footer: '',
|
||||||
|
custom_sidebar: '',
|
||||||
update: (params) =>
|
update: (params) =>
|
||||||
set((state) => {
|
set((state) => {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import i18next from 'i18next';
|
import i18next from 'i18next';
|
||||||
|
|
||||||
|
import pattern from '@/common/pattern';
|
||||||
|
|
||||||
const Diff = require('diff');
|
const Diff = require('diff');
|
||||||
|
|
||||||
function thousandthDivision(num) {
|
function thousandthDivision(num) {
|
||||||
|
@ -254,6 +256,23 @@ function base64ToSvg(base64: string) {
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine whether the user is in WeChat or Enterprise WeChat or DingTalk, and return the corresponding type
|
||||||
|
|
||||||
|
function getUserAgentType() {
|
||||||
|
const ua = navigator.userAgent.toLowerCase();
|
||||||
|
if (pattern.wxwork.test(ua)) {
|
||||||
|
return 'wxwork';
|
||||||
|
}
|
||||||
|
// if (pattern.wx.test(ua)) {
|
||||||
|
// return 'weixin';
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (pattern.dingtalk.test(ua)) {
|
||||||
|
// return 'dingtalk';
|
||||||
|
// }
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
thousandthDivision,
|
thousandthDivision,
|
||||||
formatCount,
|
formatCount,
|
||||||
|
@ -270,4 +289,5 @@ export {
|
||||||
handleFormError,
|
handleFormError,
|
||||||
diffText,
|
diffText,
|
||||||
base64ToSvg,
|
base64ToSvg,
|
||||||
|
getUserAgentType,
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue