mirror of
https://github.com/langgenius/dify.git
synced 2024-11-16 11:42:29 +08:00
Feature/add emoji to webapp (#345)
This commit is contained in:
parent
cd136fb293
commit
433f8cb57e
|
@ -52,7 +52,7 @@ const NewAppDialog = ({ show, onSuccess, onClose }: NewAppDialogProps) => {
|
||||||
mutateTemplates()
|
mutateTemplates()
|
||||||
setIsWithTemplate(false)
|
setIsWithTemplate(false)
|
||||||
}
|
}
|
||||||
}, [show])
|
}, [mutateTemplates, show])
|
||||||
|
|
||||||
const isCreatingRef = useRef(false)
|
const isCreatingRef = useRef(false)
|
||||||
const onCreate: MouseEventHandler = useCallback(async () => {
|
const onCreate: MouseEventHandler = useCallback(async () => {
|
||||||
|
@ -97,7 +97,6 @@ const NewAppDialog = ({ show, onSuccess, onClose }: NewAppDialogProps) => {
|
||||||
return <>
|
return <>
|
||||||
{showEmojiPicker && <EmojiPicker
|
{showEmojiPicker && <EmojiPicker
|
||||||
onSelect={(icon, icon_background) => {
|
onSelect={(icon, icon_background) => {
|
||||||
console.log(icon, icon_background)
|
|
||||||
setEmoji({ icon, icon_background })
|
setEmoji({ icon, icon_background })
|
||||||
setShowEmojiPicker(false)
|
setShowEmojiPicker(false)
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
'use client'
|
'use client'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { AppMode } from '@/types/app'
|
|
||||||
import { ArrowTopRightOnSquareIcon } from '@heroicons/react/24/outline'
|
import { ArrowTopRightOnSquareIcon } from '@heroicons/react/24/outline'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
import { useContext } from 'use-context-selector'
|
||||||
|
import type { AppMode } from '@/types/app'
|
||||||
import I18n from '@/context/i18n'
|
import I18n from '@/context/i18n'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Modal from '@/app/components/base/modal'
|
import Modal from '@/app/components/base/modal'
|
||||||
|
@ -23,8 +23,6 @@ const StepNum: FC<{ children: React.ReactNode }> = ({ children }) =>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const GithubIcon = ({ className }: { className: string }) => {
|
const GithubIcon = ({ className }: { className: string }) => {
|
||||||
return (
|
return (
|
||||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" className={className}>
|
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" className={className}>
|
||||||
|
@ -73,7 +71,7 @@ const CustomizeModal: FC<IShareLinkProps> = ({
|
||||||
<div className='text-gray-500 text-xs mt-1 mb-2'>{t(`${prefixCustomize}.way1.step2Tip`)}</div>
|
<div className='text-gray-500 text-xs mt-1 mb-2'>{t(`${prefixCustomize}.way1.step2Tip`)}</div>
|
||||||
<pre className='box-border py-3 px-4 bg-gray-100 text-xs font-medium rounded-lg select-text'>
|
<pre className='box-border py-3 px-4 bg-gray-100 text-xs font-medium rounded-lg select-text'>
|
||||||
export const APP_ID = '{appId}'<br />
|
export const APP_ID = '{appId}'<br />
|
||||||
export const API_KEY = {`'<Web API Key From Dify>'`}
|
export const API_KEY = {'\'<Web API Key From Dify>\''}
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,11 +7,11 @@ import { Trans, useTranslation } from 'react-i18next'
|
||||||
import s from './style.module.css'
|
import s from './style.module.css'
|
||||||
import Modal from '@/app/components/base/modal'
|
import Modal from '@/app/components/base/modal'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Switch from '@/app/components/base/switch'
|
|
||||||
import AppIcon from '@/app/components/base/app-icon'
|
import AppIcon from '@/app/components/base/app-icon'
|
||||||
import { SimpleSelect } from '@/app/components/base/select'
|
import { SimpleSelect } from '@/app/components/base/select'
|
||||||
import type { AppDetailResponse } from '@/models/app'
|
import type { AppDetailResponse } from '@/models/app'
|
||||||
import type { Language } from '@/types/app'
|
import type { Language } from '@/types/app'
|
||||||
|
import EmojiPicker from '@/app/components/base/emoji-picker'
|
||||||
|
|
||||||
export type ISettingsModalProps = {
|
export type ISettingsModalProps = {
|
||||||
appInfo: AppDetailResponse
|
appInfo: AppDetailResponse
|
||||||
|
@ -42,11 +42,14 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
||||||
onSave,
|
onSave,
|
||||||
}) => {
|
}) => {
|
||||||
const [isShowMore, setIsShowMore] = useState(false)
|
const [isShowMore, setIsShowMore] = useState(false)
|
||||||
const { title, description, copyright, privacy_policy, default_language } = appInfo.site
|
const { title, description, copyright, privacy_policy, default_language, icon, icon_background } = appInfo.site
|
||||||
const [inputInfo, setInputInfo] = useState({ title, desc: description, copyright, privacyPolicy: privacy_policy })
|
const [inputInfo, setInputInfo] = useState({ title, desc: description, copyright, privacyPolicy: privacy_policy })
|
||||||
const [language, setLanguage] = useState(default_language)
|
const [language, setLanguage] = useState(default_language)
|
||||||
const [saveLoading, setSaveLoading] = useState(false)
|
const [saveLoading, setSaveLoading] = useState(false)
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
// Emoji Picker
|
||||||
|
const [showEmojiPicker, setShowEmojiPicker] = useState(false)
|
||||||
|
const [emoji, setEmoji] = useState({ icon, icon_background })
|
||||||
|
|
||||||
const onHide = () => {
|
const onHide = () => {
|
||||||
onClose()
|
onClose()
|
||||||
|
@ -64,6 +67,8 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
||||||
prompt_public: false,
|
prompt_public: false,
|
||||||
copyright: inputInfo.copyright,
|
copyright: inputInfo.copyright,
|
||||||
privacy_policy: inputInfo.privacyPolicy,
|
privacy_policy: inputInfo.privacyPolicy,
|
||||||
|
icon: emoji.icon,
|
||||||
|
icon_background: emoji.icon_background,
|
||||||
}
|
}
|
||||||
await onSave(params)
|
await onSave(params)
|
||||||
setSaveLoading(false)
|
setSaveLoading(false)
|
||||||
|
@ -77,69 +82,88 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<>
|
||||||
title={t(`${prefixSettings}.title`)}
|
{showEmojiPicker && <EmojiPicker
|
||||||
isShow={isShow}
|
onSelect={(icon, icon_background) => {
|
||||||
onClose={onHide}
|
console.log(icon, icon_background)
|
||||||
className={`${s.settingsModal}`}
|
setEmoji({ icon, icon_background })
|
||||||
>
|
setShowEmojiPicker(false)
|
||||||
<div className={`mt-6 font-medium ${s.settingTitle} text-gray-900`}>{t(`${prefixSettings}.webName`)}</div>
|
}}
|
||||||
<div className='flex mt-2'>
|
onClose={() => {
|
||||||
<AppIcon className='!mr-3 self-center' />
|
setEmoji({ icon: '🤖', icon_background: '#FFEAD5' })
|
||||||
<input className={`flex-grow rounded-lg h-10 box-border px-3 ${s.projectName} bg-gray-100`}
|
setShowEmojiPicker(false)
|
||||||
value={inputInfo.title}
|
}}
|
||||||
onChange={onChange('title')} />
|
/>}
|
||||||
</div>
|
<Modal
|
||||||
<div className={`mt-6 font-medium ${s.settingTitle} text-gray-900 `}>{t(`${prefixSettings}.webDesc`)}</div>
|
title={t(`${prefixSettings}.title`)}
|
||||||
<p className={`mt-1 ${s.settingsTip} text-gray-500`}>{t(`${prefixSettings}.webDescTip`)}</p>
|
isShow={isShow}
|
||||||
<textarea
|
onClose={onHide}
|
||||||
rows={3}
|
className={`${s.settingsModal}`}
|
||||||
className={`mt-2 pt-2 pb-2 px-3 rounded-lg bg-gray-100 w-full ${s.settingsTip} text-gray-900`}
|
>
|
||||||
value={inputInfo.desc}
|
<div className={`mt-6 font-medium ${s.settingTitle} text-gray-900`}>{t(`${prefixSettings}.webName`)}</div>
|
||||||
onChange={onChange('desc')}
|
<div className='flex mt-2'>
|
||||||
placeholder={t(`${prefixSettings}.webDescPlaceholder`) as string}
|
<AppIcon size='large'
|
||||||
/>
|
onClick={() => { setShowEmojiPicker(true) }}
|
||||||
<div className={`mt-6 mb-2 font-medium ${s.settingTitle} text-gray-900 `}>{t(`${prefixSettings}.language`)}</div>
|
className='cursor-pointer !mr-3 self-center'
|
||||||
<SimpleSelect
|
icon={emoji.icon}
|
||||||
items={Object.keys(LANGUAGE_MAP).map(lang => ({ name: LANGUAGE_MAP[lang as Language], value: lang }))}
|
background={emoji.icon_background}
|
||||||
defaultValue={language}
|
|
||||||
onSelect={item => setLanguage(item.value as Language)}
|
|
||||||
/>
|
|
||||||
{!isShowMore && <div className='w-full cursor-pointer mt-8' onClick={() => setIsShowMore(true)}>
|
|
||||||
<div className='flex justify-between'>
|
|
||||||
<div className={`font-medium ${s.settingTitle} flex-grow text-gray-900`}>{t(`${prefixSettings}.more.entry`)}</div>
|
|
||||||
<div className='flex-shrink-0 w-4 h-4 text-gray-500'>
|
|
||||||
<ChevronRightIcon />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p className={`mt-1 ${s.policy} text-gray-500`}>{t(`${prefixSettings}.more.copyright`)} & {t(`${prefixSettings}.more.privacyPolicy`)}</p>
|
|
||||||
</div>}
|
|
||||||
{isShowMore && <>
|
|
||||||
<hr className='w-full mt-6' />
|
|
||||||
<div className={`mt-6 font-medium ${s.settingTitle} text-gray-900`}>{t(`${prefixSettings}.more.copyright`)}</div>
|
|
||||||
<input className={`w-full mt-2 rounded-lg h-10 box-border px-3 ${s.projectName} bg-gray-100`}
|
|
||||||
value={inputInfo.copyright}
|
|
||||||
onChange={onChange('copyright')}
|
|
||||||
placeholder={t(`${prefixSettings}.more.copyRightPlaceholder`) as string}
|
|
||||||
/>
|
|
||||||
<div className={`mt-8 font-medium ${s.settingTitle} text-gray-900`}>{t(`${prefixSettings}.more.privacyPolicy`)}</div>
|
|
||||||
<p className={`mt-1 ${s.settingsTip} text-gray-500`}>
|
|
||||||
<Trans
|
|
||||||
i18nKey={`${prefixSettings}.more.privacyPolicyTip`}
|
|
||||||
components={{ privacyPolicyLink: <Link href={'https://langgenius.ai/privacy-policy'} target='_blank' className='text-primary-600' /> }}
|
|
||||||
/>
|
/>
|
||||||
</p>
|
<input className={`flex-grow rounded-lg h-10 box-border px-3 ${s.projectName} bg-gray-100`}
|
||||||
<input className={`w-full mt-2 rounded-lg h-10 box-border px-3 ${s.projectName} bg-gray-100`}
|
value={inputInfo.title}
|
||||||
value={inputInfo.privacyPolicy}
|
onChange={onChange('title')} />
|
||||||
onChange={onChange('privacyPolicy')}
|
</div>
|
||||||
placeholder={t(`${prefixSettings}.more.privacyPolicyPlaceholder`) as string}
|
<div className={`mt-6 font-medium ${s.settingTitle} text-gray-900 `}>{t(`${prefixSettings}.webDesc`)}</div>
|
||||||
|
<p className={`mt-1 ${s.settingsTip} text-gray-500`}>{t(`${prefixSettings}.webDescTip`)}</p>
|
||||||
|
<textarea
|
||||||
|
rows={3}
|
||||||
|
className={`mt-2 pt-2 pb-2 px-3 rounded-lg bg-gray-100 w-full ${s.settingsTip} text-gray-900`}
|
||||||
|
value={inputInfo.desc}
|
||||||
|
onChange={onChange('desc')}
|
||||||
|
placeholder={t(`${prefixSettings}.webDescPlaceholder`) as string}
|
||||||
/>
|
/>
|
||||||
</>}
|
<div className={`mt-6 mb-2 font-medium ${s.settingTitle} text-gray-900 `}>{t(`${prefixSettings}.language`)}</div>
|
||||||
<div className='mt-10 flex justify-end'>
|
<SimpleSelect
|
||||||
<Button className='mr-2 flex-shrink-0' onClick={onHide}>{t('common.operation.cancel')}</Button>
|
items={Object.keys(LANGUAGE_MAP).map(lang => ({ name: LANGUAGE_MAP[lang as Language], value: lang }))}
|
||||||
<Button type='primary' className='flex-shrink-0' onClick={onClickSave} loading={saveLoading}>{t('common.operation.save')}</Button>
|
defaultValue={language}
|
||||||
</div>
|
onSelect={item => setLanguage(item.value as Language)}
|
||||||
</Modal >
|
/>
|
||||||
|
{!isShowMore && <div className='w-full cursor-pointer mt-8' onClick={() => setIsShowMore(true)}>
|
||||||
|
<div className='flex justify-between'>
|
||||||
|
<div className={`font-medium ${s.settingTitle} flex-grow text-gray-900`}>{t(`${prefixSettings}.more.entry`)}</div>
|
||||||
|
<div className='flex-shrink-0 w-4 h-4 text-gray-500'>
|
||||||
|
<ChevronRightIcon />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className={`mt-1 ${s.policy} text-gray-500`}>{t(`${prefixSettings}.more.copyright`)} & {t(`${prefixSettings}.more.privacyPolicy`)}</p>
|
||||||
|
</div>}
|
||||||
|
{isShowMore && <>
|
||||||
|
<hr className='w-full mt-6' />
|
||||||
|
<div className={`mt-6 font-medium ${s.settingTitle} text-gray-900`}>{t(`${prefixSettings}.more.copyright`)}</div>
|
||||||
|
<input className={`w-full mt-2 rounded-lg h-10 box-border px-3 ${s.projectName} bg-gray-100`}
|
||||||
|
value={inputInfo.copyright}
|
||||||
|
onChange={onChange('copyright')}
|
||||||
|
placeholder={t(`${prefixSettings}.more.copyRightPlaceholder`) as string}
|
||||||
|
/>
|
||||||
|
<div className={`mt-8 font-medium ${s.settingTitle} text-gray-900`}>{t(`${prefixSettings}.more.privacyPolicy`)}</div>
|
||||||
|
<p className={`mt-1 ${s.settingsTip} text-gray-500`}>
|
||||||
|
<Trans
|
||||||
|
i18nKey={`${prefixSettings}.more.privacyPolicyTip`}
|
||||||
|
components={{ privacyPolicyLink: <Link href={'https://langgenius.ai/privacy-policy'} target='_blank' className='text-primary-600' /> }}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<input className={`w-full mt-2 rounded-lg h-10 box-border px-3 ${s.projectName} bg-gray-100`}
|
||||||
|
value={inputInfo.privacyPolicy}
|
||||||
|
onChange={onChange('privacyPolicy')}
|
||||||
|
placeholder={t(`${prefixSettings}.more.privacyPolicyPlaceholder`) as string}
|
||||||
|
/>
|
||||||
|
</>}
|
||||||
|
<div className='mt-10 flex justify-end'>
|
||||||
|
<Button className='mr-2 flex-shrink-0' onClick={onHide}>{t('common.operation.cancel')}</Button>
|
||||||
|
<Button type='primary' className='flex-shrink-0' onClick={onClickSave} loading={saveLoading}>{t('common.operation.save')}</Button>
|
||||||
|
</div>
|
||||||
|
</Modal >
|
||||||
|
</>
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export default React.memo(SettingsModal)
|
export default React.memo(SettingsModal)
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import './style.css'
|
import './style.css'
|
||||||
interface ILoadingProps {
|
type ILoadingProps = {
|
||||||
type?: 'area' | 'app'
|
type?: 'area' | 'app'
|
||||||
}
|
}
|
||||||
const Loading = (
|
const Loading = (
|
||||||
{ type = 'area' }: ILoadingProps = { type: 'area' }
|
{ type = 'area' }: ILoadingProps = { type: 'area' },
|
||||||
) => {
|
) => {
|
||||||
return (
|
return (
|
||||||
<div className={`flex w-full justify-center items-center ${type === 'app' ? 'h-full' : ''}`}>
|
<div className={`flex w-full justify-center items-center ${type === 'app' ? 'h-full' : ''}`}>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
.spin-animation path {
|
.spin-animation path {
|
||||||
animation: custom 2s linear infinite;
|
animation: custom 1s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes custom {
|
@keyframes custom {
|
||||||
|
@ -29,13 +29,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.spin-animation path:nth-child(2) {
|
.spin-animation path:nth-child(2) {
|
||||||
animation-delay: 0.5s;
|
animation-delay: 0.25s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.spin-animation path:nth-child(3) {
|
.spin-animation path:nth-child(3) {
|
||||||
animation-delay: 1s;
|
animation-delay: 0.5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.spin-animation path:nth-child(4) {
|
.spin-animation path:nth-child(4) {
|
||||||
animation-delay: 1.5s;
|
animation-delay: 1s;
|
||||||
}
|
}
|
||||||
|
|
|
@ -179,6 +179,9 @@ export type SiteConfig = {
|
||||||
copyright: string
|
copyright: string
|
||||||
/** Privacy Policy */
|
/** Privacy Policy */
|
||||||
privacy_policy: string
|
privacy_policy: string
|
||||||
|
|
||||||
|
icon: string
|
||||||
|
icon_background: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue
Block a user