Feat/chat custom disclaimer (#4306)

This commit is contained in:
Patryk Garstecki
2024-05-18 04:52:48 +02:00
committed by GitHub
parent b1f003646b
commit aa13d14019
43 changed files with 350 additions and 159 deletions

View File

@@ -67,6 +67,7 @@ export type IChatProps = {
visionConfig?: VisionSettings
supportAnnotation?: boolean
allToolIcons?: Record<string, string | Emoji>
customDisclaimer?: string
}
const Chat: FC<IChatProps> = ({
@@ -102,6 +103,7 @@ const Chat: FC<IChatProps> = ({
supportAnnotation,
onChatListChange,
allToolIcons,
customDisclaimer,
}) => {
const { t } = useTranslation()
const { notify } = useContext(ToastContext)
@@ -358,44 +360,46 @@ const Chat: FC<IChatProps> = ({
</div>
</div>
)}
<div className={cn('p-[5.5px] max-h-[150px] bg-white border-[1.5px] border-gray-200 rounded-xl overflow-y-auto', isDragActive && 'border-primary-600')}>
{visionConfig?.enabled && (
<>
<div className='absolute bottom-2 left-2 flex items-center'>
<ChatImageUploader
settings={visionConfig}
onUpload={onUpload}
disabled={files.length >= visionConfig.number_limits}
/>
<div className='mx-1 w-[1px] h-4 bg-black/5' />
</div>
<div className='pl-[52px]'>
<ImageList
list={files}
onRemove={onRemove}
onReUpload={onReUpload}
onImageLinkLoadSuccess={onImageLinkLoadSuccess}
onImageLinkLoadError={onImageLinkLoadError}
/>
</div>
</>
)}
<Textarea
className={`
block w-full px-2 pr-[118px] py-[7px] leading-5 max-h-none text-sm text-gray-700 outline-none appearance-none resize-none
${visionConfig?.enabled && 'pl-12'}
`}
value={query}
onChange={handleContentChange}
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
onPaste={onPaste}
onDragEnter={onDragEnter}
onDragLeave={onDragLeave}
onDragOver={onDragOver}
onDrop={onDrop}
autoSize
/>
<div className='relative'>
<div className={cn('relative p-[5.5px] max-h-[150px] bg-white border-[1.5px] border-gray-200 rounded-xl overflow-y-auto', isDragActive && 'border-primary-600')}>
{visionConfig?.enabled && (
<>
<div className='absolute bottom-2 left-2 flex items-center'>
<ChatImageUploader
settings={visionConfig}
onUpload={onUpload}
disabled={files.length >= visionConfig.number_limits}
/>
<div className='mx-1 w-[1px] h-4 bg-black/5' />
</div>
<div className='pl-[52px]'>
<ImageList
list={files}
onRemove={onRemove}
onReUpload={onReUpload}
onImageLinkLoadSuccess={onImageLinkLoadSuccess}
onImageLinkLoadError={onImageLinkLoadError}
/>
</div>
</>
)}
<Textarea
className={`
block w-full px-2 pr-[118px] py-[7px] leading-5 max-h-none text-sm text-gray-700 outline-none appearance-none resize-none
${visionConfig?.enabled && 'pl-12'}
`}
value={query}
onChange={handleContentChange}
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
onPaste={onPaste}
onDragEnter={onDragEnter}
onDragLeave={onDragLeave}
onDragOver={onDragOver}
onDrop={onDrop}
autoSize
/>
</div>
<div className="absolute bottom-2 right-2 flex items-center h-8">
<div className={`${s.count} mr-4 h-5 leading-5 text-sm bg-gray-50 text-gray-500`}>{query.trim().length}</div>
{
@@ -440,6 +444,9 @@ const Chat: FC<IChatProps> = ({
/>
)}
</div>
{customDisclaimer && <div className='text-xs text-gray-500 mt-1 text-center'>
{customDisclaimer}
</div>}
</div>
)}
</div>

View File

@@ -31,6 +31,7 @@ export type ConfigParams = {
prompt_public: boolean
copyright: string
privacy_policy: string
custom_disclaimer: string
icon: string
icon_background: string
}
@@ -46,8 +47,8 @@ const SettingsModal: FC<ISettingsModalProps> = ({
const { notify } = useToastContext()
const [isShowMore, setIsShowMore] = useState(false)
const { icon, icon_background } = appInfo
const { title, description, copyright, privacy_policy, default_language } = appInfo.site
const [inputInfo, setInputInfo] = useState({ title, desc: description, copyright, privacyPolicy: privacy_policy })
const { title, description, copyright, privacy_policy, custom_disclaimer, default_language } = appInfo.site
const [inputInfo, setInputInfo] = useState({ title, desc: description, copyright, privacyPolicy: privacy_policy, customDisclaimer: custom_disclaimer })
const [language, setLanguage] = useState(default_language)
const [saveLoading, setSaveLoading] = useState(false)
const { t } = useTranslation()
@@ -56,7 +57,7 @@ const SettingsModal: FC<ISettingsModalProps> = ({
const [emoji, setEmoji] = useState({ icon, icon_background })
useEffect(() => {
setInputInfo({ title, desc: description, copyright, privacyPolicy: privacy_policy })
setInputInfo({ title, desc: description, copyright, privacyPolicy: privacy_policy, customDisclaimer: custom_disclaimer })
setLanguage(default_language)
setEmoji({ icon, icon_background })
}, [appInfo])
@@ -81,6 +82,7 @@ const SettingsModal: FC<ISettingsModalProps> = ({
prompt_public: false,
copyright: inputInfo.copyright,
privacy_policy: inputInfo.privacyPolicy,
custom_disclaimer: inputInfo.customDisclaimer,
icon: emoji.icon,
icon_background: emoji.icon_background,
}
@@ -161,6 +163,13 @@ const SettingsModal: FC<ISettingsModalProps> = ({
onChange={onChange('privacyPolicy')}
placeholder={t(`${prefixSettings}.more.privacyPolicyPlaceholder`) as string}
/>
<div className={`mt-8 font-medium ${s.settingTitle} text-gray-900`}>{t(`${prefixSettings}.more.customDisclaimer`)}</div>
<p className={`mt-1 ${s.settingsTip} text-gray-500`}>{t(`${prefixSettings}.more.customDisclaimerTip`)}</p>
<input className={`w-full mt-2 rounded-lg h-10 box-border px-3 ${s.projectName} bg-gray-100`}
value={inputInfo.customDisclaimer}
onChange={onChange('customDisclaimer')}
placeholder={t(`${prefixSettings}.more.customDisclaimerPlaceholder`) as string}
/>
</>}
<div className='mt-10 flex justify-end'>
<Button className='mr-2 flex-shrink-0 !text-sm' onClick={onHide}>{t('common.operation.cancel')}</Button>

View File

@@ -14,6 +14,7 @@ import type {
VisionConfig,
} from '../types'
import { TransferMethod } from '../types'
import { useChatWithHistoryContext } from '../chat-with-history/context'
import TooltipPlus from '@/app/components/base/tooltip-plus'
import { ToastContext } from '@/app/components/base/toast'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
@@ -40,6 +41,7 @@ const ChatInput: FC<ChatInputProps> = ({
speechToTextConfig,
onSend,
}) => {
const { appData } = useChatWithHistoryContext()
const { t } = useTranslation()
const { notify } = useContext(ToastContext)
const [voiceInputShow, setVoiceInputShow] = useState(false)
@@ -127,101 +129,106 @@ const ChatInput: FC<ChatInputProps> = ({
)
return (
<div className='relative'>
<div
className={`
p-[5.5px] max-h-[150px] bg-white border-[1.5px] border-gray-200 rounded-xl overflow-y-auto
${isDragActive && 'border-primary-600'}
`}
>
{
visionConfig?.enabled && (
<>
<div className='absolute bottom-2 left-2 flex items-center'>
<ChatImageUploader
settings={visionConfig}
onUpload={onUpload}
disabled={files.length >= visionConfig.number_limits}
/>
<div className='mx-1 w-[1px] h-4 bg-black/5' />
</div>
<div className='pl-[52px]'>
<ImageList
list={files}
onRemove={onRemove}
onReUpload={onReUpload}
onImageLinkLoadSuccess={onImageLinkLoadSuccess}
onImageLinkLoadError={onImageLinkLoadError}
/>
</div>
</>
)
}
<Textarea
<>
<div className='relative'>
<div
className={`
block w-full px-2 pr-[118px] py-[7px] leading-5 max-h-none text-sm text-gray-700 outline-none appearance-none resize-none
${visionConfig?.enabled && 'pl-12'}
p-[5.5px] max-h-[150px] bg-white border-[1.5px] border-gray-200 rounded-xl overflow-y-auto
${isDragActive && 'border-primary-600'} mb-2
`}
value={query}
onChange={handleContentChange}
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
onPaste={onPaste}
onDragEnter={onDragEnter}
onDragLeave={onDragLeave}
onDragOver={onDragOver}
onDrop={onDrop}
autoSize
/>
<div className='absolute bottom-[7px] right-2 flex items-center h-8'>
<div className='flex items-center px-1 h-5 rounded-md bg-gray-100 text-xs font-medium text-gray-500'>
{query.trim().length}
</div>
>
{
query
? (
<div className='flex justify-center items-center ml-2 w-8 h-8 cursor-pointer hover:bg-gray-100 rounded-lg' onClick={() => setQuery('')}>
<XCircle className='w-4 h-4 text-[#98A2B3]' />
visionConfig?.enabled && (
<>
<div className='absolute bottom-2 left-2 flex items-center'>
<ChatImageUploader
settings={visionConfig}
onUpload={onUpload}
disabled={files.length >= visionConfig.number_limits}
/>
<div className='mx-1 w-[1px] h-4 bg-black/5' />
</div>
)
: speechToTextConfig?.enabled
<div className='pl-[52px]'>
<ImageList
list={files}
onRemove={onRemove}
onReUpload={onReUpload}
onImageLinkLoadSuccess={onImageLinkLoadSuccess}
onImageLinkLoadError={onImageLinkLoadError}
/>
</div>
</>
)
}
<Textarea
className={`
block w-full px-2 pr-[118px] py-[7px] leading-5 max-h-none text-sm text-gray-700 outline-none appearance-none resize-none
${visionConfig?.enabled && 'pl-12'}
`}
value={query}
onChange={handleContentChange}
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
onPaste={onPaste}
onDragEnter={onDragEnter}
onDragLeave={onDragLeave}
onDragOver={onDragOver}
onDrop={onDrop}
autoSize
/>
<div className='absolute bottom-[7px] right-2 flex items-center h-8'>
<div className='flex items-center px-1 h-5 rounded-md bg-gray-100 text-xs font-medium text-gray-500'>
{query.trim().length}
</div>
{
query
? (
<div
className='group flex justify-center items-center ml-2 w-8 h-8 hover:bg-primary-50 rounded-lg cursor-pointer'
onClick={handleVoiceInputShow}
>
<Microphone01 className='block w-4 h-4 text-gray-500 group-hover:hidden' />
<Microphone01Solid className='hidden w-4 h-4 text-primary-600 group-hover:block' />
<div className='flex justify-center items-center ml-2 w-8 h-8 cursor-pointer hover:bg-gray-100 rounded-lg' onClick={() => setQuery('')}>
<XCircle className='w-4 h-4 text-[#98A2B3]' />
</div>
)
: null
: speechToTextConfig?.enabled
? (
<div
className='group flex justify-center items-center ml-2 w-8 h-8 hover:bg-primary-50 rounded-lg cursor-pointer'
onClick={handleVoiceInputShow}
>
<Microphone01 className='block w-4 h-4 text-gray-500 group-hover:hidden' />
<Microphone01Solid className='hidden w-4 h-4 text-primary-600 group-hover:block' />
</div>
)
: null
}
<div className='mx-2 w-[1px] h-4 bg-black opacity-5' />
{isMobile
? sendBtn
: (
<TooltipPlus
popupContent={
<div>
<div>{t('common.operation.send')} Enter</div>
<div>{t('common.operation.lineBreak')} Shift Enter</div>
</div>
}
>
{sendBtn}
</TooltipPlus>
)}
</div>
{
voiceInputShow && (
<VoiceInput
onCancel={() => setVoiceInputShow(false)}
onConverted={text => setQuery(text)}
/>
)
}
<div className='mx-2 w-[1px] h-4 bg-black opacity-5' />
{isMobile
? sendBtn
: (
<TooltipPlus
popupContent={
<div>
<div>{t('common.operation.send')} Enter</div>
<div>{t('common.operation.lineBreak')} Shift Enter</div>
</div>
}
>
{sendBtn}
</TooltipPlus>
)}
</div>
{
voiceInputShow && (
<VoiceInput
onCancel={() => setVoiceInputShow(false)}
onConverted={text => setQuery(text)}
/>
)
}
</div>
</div>
{appData?.site?.custom_disclaimer && <div className='text-xs text-gray-500 mt-1 text-center'>
{appData.site.custom_disclaimer}
</div>}
</>
)
}

View File

@@ -929,6 +929,7 @@ const Main: FC<IMainProps> = ({
image_file_size_limit: fileUploadConfigResponse ? fileUploadConfigResponse.image_file_size_limit : visionConfig.image_file_size_limit,
}}
allToolIcons={appMeta?.tool_icons || {}}
customDisclaimer={siteInfo.custom_disclaimer}
/>
</div>
</div>)

View File

@@ -800,6 +800,7 @@ const Main: FC<IMainProps> = ({
answerIcon={<LogoAvatar className='relative shrink-0' />}
visionConfig={visionConfig}
allToolIcons={appMeta?.tool_icons || {}}
customDisclaimer={siteInfo.custom_disclaimer}
/>
</div>
</div>)

View File

@@ -267,6 +267,19 @@ const EditCustomCollectionModal: FC<Props> = ({
className='w-full h-10 px-3 text-sm font-normal bg-gray-100 rounded-lg grow' placeholder={t('tools.createTool.privacyPolicyPlaceholder') || ''} />
</div>
<div>
<div className={fieldNameClassNames}>{t('tools.createTool.customDisclaimer')}</div>
<input
value={customCollection.custom_disclaimer}
onChange={(e) => {
const newCollection = produce(customCollection, (draft) => {
draft.custom_disclaimer = e.target.value
})
setCustomCollection(newCollection)
}}
className='w-full h-10 px-3 text-sm font-normal bg-gray-100 rounded-lg grow' placeholder={t('tools.createTool.customDisclaimerPlaceholder') || ''} />
</div>
</div>
<div className={cn(isEdit ? 'justify-between' : 'justify-end', 'mt-2 shrink-0 flex py-4 px-6 rounded-b-[10px] bg-gray-50 border-t border-black/5')} >
{

View File

@@ -89,6 +89,7 @@ export type CustomCollectionBackend = {
schema_type: string
schema: string
privacy_policy: string
custom_disclaimer: string
tools?: ParamItem[]
}

View File

@@ -50,6 +50,9 @@ const translation = {
privacyPolicy: 'Datenschutzrichtlinie',
privacyPolicyPlaceholder: 'Geben Sie den Link zur Datenschutzrichtlinie ein',
privacyPolicyTip: 'Hilft Besuchern zu verstehen, welche Daten die Anwendung sammelt, siehe Difys <privacyPolicyLink>Datenschutzrichtlinie</privacyPolicyLink>.',
customDisclaimer: 'Benutzerdefinierte Haftungsausschluss',
customDisclaimerPlaceholder: 'Geben Sie den benutzerdefinierten Haftungsausschluss-Text ein',
customDisclaimerTip: 'Der ben userdefinierte Haftungsausschluss-Text wird auf der Clientseite angezeigt und bietet zusätzliche Informationen über die Anwendung',
},
},
embedded: {

View File

@@ -71,6 +71,8 @@ const translation = {
},
privacyPolicy: 'Datenschutzrichtlinie',
privacyPolicyPlaceholder: 'Bitte Datenschutzrichtlinie eingeben',
customDisclaimer: 'Benutzer Haftungsausschluss',
customDisclaimerPlaceholder: 'Bitte benutzerdefinierten Haftungsausschluss eingeben',
},
test: {
title: 'Test',

View File

@@ -50,6 +50,9 @@ const translation = {
privacyPolicy: 'Privacy Policy',
privacyPolicyPlaceholder: 'Enter the privacy policy link',
privacyPolicyTip: 'Helps visitors understand the data the application collects, see Dify\'s <privacyPolicyLink>Privacy Policy</privacyPolicyLink>.',
customDisclaimer: 'Custom Disclaimer',
customDisclaimerPlaceholder: 'Enter the custom disclaimer text',
customDisclaimerTip: 'Custom disclaimer text will be displayed on the client side, providing additional information about the application',
},
},
embedded: {

View File

@@ -71,6 +71,8 @@ const translation = {
},
privacyPolicy: 'Privacy policy',
privacyPolicyPlaceholder: 'Please enter privacy policy',
customDisclaimer: 'Custom disclaimer',
customDisclaimerPlaceholder: 'Please enter custom disclaimer',
},
test: {
title: 'Test',

View File

@@ -50,6 +50,9 @@ const translation = {
privacyPolicy: 'Politique de confidentialité',
privacyPolicyPlaceholder: 'Entrez le lien de la politique de confidentialité',
privacyPolicyTip: 'Aide les visiteurs à comprendre les données collectées par l\'application, voir la <privacyPolicyLink>Politique de confidentialité</privacyPolicyLink> de Dify.',
customDisclaimer: 'Clause de non-responsabilité personnalisée',
customDisclaimerPlaceholder: 'Entrez le texte de la clause de non-responsabilité personnalisée',
customDisclaimerTip: 'Le texte de la clause de non-responsabilité personnalisée sera affiché côté client, fournissant des informations supplémentaires sur l\'application',
},
},
embedded: {

View File

@@ -71,6 +71,8 @@ const translation = {
},
privacyPolicy: 'Politique de confidentialité',
privacyPolicyPlaceholder: 'Veuillez entrer la politique de confidentialité',
customDisclaimer: 'Clause de non-responsabilité personnalisée',
customDisclaimerPlaceholder: 'Entrez le texte de la clause de non-responsabilité personnalisée',
},
test: {
title: 'Test',

View File

@@ -50,6 +50,9 @@ const translation = {
privacyPolicy: 'プライバシーポリシー',
privacyPolicyPlaceholder: 'プライバシーポリシーリンクを入力してください',
privacyPolicyTip: '訪問者がアプリケーションが収集するデータを理解し、Difyの<privacyPolicyLink>プライバシーポリシー</privacyPolicyLink>を参照できるようにします。',
customDisclaimer: 'カスタム免責事項',
customDisclaimerPlaceholder: '免責事項を入力してください',
customDisclaimerTip: 'アプリケーションの使用に関する免責事項を提供します。',
},
},
embedded: {

View File

@@ -71,6 +71,8 @@ const translation = {
},
privacyPolicy: 'プライバシーポリシー',
privacyPolicyPlaceholder: 'プライバシーポリシーを入力してください',
customDisclaimer: 'カスタム免責事項',
customDisclaimerPlaceholder: 'カスタム免責事項を入力してください',
},
test: {
title: 'テスト',

View File

@@ -55,6 +55,9 @@ const translation = {
privacyPolicyPlaceholder: 'Wprowadź link do polityki prywatności',
privacyPolicyTip:
'Pomaga odwiedzającym zrozumieć, jakie dane zbiera aplikacja, zobacz <privacyPolicyLink>Politykę prywatności Dify</privacyPolicyLink>.',
customDisclaimer: 'Oświadczenie o ochronie danych',
customDisclaimerPlaceholder: 'Wprowadź oświadczenie o ochronie danych',
customDisclaimerTip: 'Niestandardowy tekst oświadczenia będzie wyświetlany po stronie klienta, dostarczając dodatkowych informacji o aplikacji.',
},
},
embedded: {

View File

@@ -73,6 +73,8 @@ const translation = {
},
privacyPolicy: 'Polityka prywatności',
privacyPolicyPlaceholder: 'Proszę wprowadzić politykę prywatności',
customDisclaimer: 'Oświadczenie niestandardowe',
customDisclaimerPlaceholder: 'Proszę wprowadzić oświadczenie niestandardowe',
},
test: {
title: 'Test',

View File

@@ -50,6 +50,9 @@ const translation = {
privacyPolicy: 'Política de Privacidade',
privacyPolicyPlaceholder: 'Insira o link da política de privacidade',
privacyPolicyTip: 'Ajuda os visitantes a entender os dados coletados pelo aplicativo, consulte a <privacyPolicyLink>Política de Privacidade</privacyPolicyLink> do Dify.',
customDisclaimer: 'Aviso Legal Personalizado',
customDisclaimerPlaceholder: 'Insira o texto do aviso legal',
customDisclaimerTip: 'O texto do aviso legal personalizado será exibido no lado do cliente, fornecendo informações adicionais sobre o aplicativo',
},
},
embedded: {

View File

@@ -71,6 +71,8 @@ const translation = {
},
privacyPolicy: 'Política de Privacidade',
privacyPolicyPlaceholder: 'Digite a política de privacidade',
customDisclaimer: 'Aviso Personalizado',
customDisclaimerPlaceholder: 'Digite o aviso personalizado',
},
test: {
title: 'Testar',

View File

@@ -50,6 +50,9 @@ const translation = {
privacyPolicy: 'Політика конфіденційності',
privacyPolicyPlaceholder: 'Введіть посилання на політику конфіденційності',
privacyPolicyTip: 'Допомагає відвідувачам зрозуміти дані, зібрані додатком, див. <privacyPolicyLink>Політику конфіденційності</privacyPolicyLink> Dify.',
customDisclaimer: 'Відмова від відповідальності',
customDisclaimerPlaceholder: 'Введіть відмову від відповідальності',
customDisclaimerTip: 'Відображається на клієнтському боці, щоб визначити відповідальність за використання додатка',
},
},
embedded: {

View File

@@ -70,6 +70,8 @@ const translation = {
},
privacyPolicy: 'Політика конфіденційності',
privacyPolicyPlaceholder: 'Введіть політику конфіденційності',
customDisclaimer: 'Власний відомості',
customDisclaimerPlaceholder: 'Введіть власні відомості',
},
test: {

View File

@@ -50,6 +50,9 @@ const translation = {
privacyPolicy: 'Chính sách bảo mật',
privacyPolicyPlaceholder: 'Nhập liên kết chính sách bảo mật',
privacyPolicyTip: 'Giúp khách truy cập hiểu được dữ liệu mà ứng dụng thu thập, xem <privacyPolicyLink>Chính sách bảo mật</privacyPolicyLink> của Dify.',
customDisclaimer: 'Tùy chỉnh từ chối trách nhiệm',
customDisclaimerPlaceholder: 'Nhập liên kết từ chối trách nhiệm',
customDisclaimerTip: 'Liên kết này sẽ được hiển thị ở phía máy khách, cung cấp thông tin về trách nhiệm của ứng dụng',
},
},
embedded: {

View File

@@ -71,6 +71,8 @@ const translation = {
},
privacyPolicy: 'Chính sách bảo mật',
privacyPolicyPlaceholder: 'Vui lòng nhập chính sách bảo mật',
customDisclaimer: 'Tuyên bố Tùy chỉnh',
customDisclaimerPlaceholder: 'Vui lòng nhập tuyên bố tùy chỉnh',
},
test: {
title: 'Kiểm tra',

View File

@@ -50,6 +50,9 @@ const translation = {
privacyPolicy: '隐私政策',
privacyPolicyPlaceholder: '请输入隐私政策链接',
privacyPolicyTip: '帮助访问者了解该应用收集的数据,可参考 Dify 的<privacyPolicyLink>隐私政策</privacyPolicyLink>。',
customDisclaimer: '自定义免责声明',
customDisclaimerPlaceholder: '请输入免责声明',
customDisclaimerTip: '在应用中展示免责声明,可用于告知用户 AI 的局限性。',
},
},
embedded: {

View File

@@ -71,6 +71,8 @@ const translation = {
},
privacyPolicy: '隐私协议',
privacyPolicyPlaceholder: '请输入隐私协议',
customDisclaimer: '自定义免责声明',
customDisclaimerPlaceholder: '请输入自定义免责声明',
},
test: {
title: '测试',

View File

@@ -50,6 +50,9 @@ const translation = {
privacyPolicy: '隱私政策',
privacyPolicyPlaceholder: '請輸入隱私政策連結',
privacyPolicyTip: '幫助訪問者瞭解該應用收集的資料,可參考 Dify 的<privacyPolicyLink>隱私政策</privacyPolicyLink>。',
customDisclaimer: '自定義免責聲明',
customDisclaimerPlaceholder: '請輸入免責聲明',
customDisclaimerTip: '客製化的免責聲明文字將在客戶端顯示,提供有關應用程式的額外資訊。',
},
},
embedded: {

View File

@@ -71,6 +71,8 @@ const translation = {
},
privacyPolicy: '隱私協議',
privacyPolicyPlaceholder: '請輸入隱私協議',
customDisclaimer: '自定義免責聲明',
customDisclaimerPlaceholder: '請輸入自定義免責聲明',
},
test: {
title: '測試',

View File

@@ -16,6 +16,7 @@ export type App = {
description: string
copyright: string
privacy_policy: string | null
custom_disclaimer: string | null
category: AppCategory
position: number
is_listed: boolean

View File

@@ -18,6 +18,7 @@ export type SiteInfo = {
prompt_public?: boolean
copyright?: string
privacy_policy?: string
custom_disclaimer?: string
}
export type AppMeta = {

View File

@@ -269,6 +269,8 @@ export type SiteConfig = {
copyright: string
/** Privacy Policy */
privacy_policy: string
/** Custom Disclaimer */
custom_disclaimer: string
icon: string
icon_background: string