Selaa lähdekoodia

Feature/add emoji to webapp (#345)

tags/0.3.3
crazywoola 2 vuotta sitten
vanhempi
commit
433f8cb57e
No account linked to committer's email address

+ 1
- 2
web/app/(commonLayout)/apps/NewAppDialog.tsx Näytä tiedosto

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 () => {
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)
}} }}

+ 2
- 4
web/app/components/app/overview/customize/index.tsx Näytä tiedosto

'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'
{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}>
<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>

+ 86
- 62
web/app/components/app/overview/settings/index.tsx Näytä tiedosto

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
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()
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)
} }


return ( return (
<Modal
title={t(`${prefixSettings}.title`)}
isShow={isShow}
onClose={onHide}
className={`${s.settingsModal}`}
>
<div className={`mt-6 font-medium ${s.settingTitle} text-gray-900`}>{t(`${prefixSettings}.webName`)}</div>
<div className='flex mt-2'>
<AppIcon className='!mr-3 self-center' />
<input className={`flex-grow rounded-lg h-10 box-border px-3 ${s.projectName} bg-gray-100`}
value={inputInfo.title}
onChange={onChange('title')} />
</div>
<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>
<SimpleSelect
items={Object.keys(LANGUAGE_MAP).map(lang => ({ name: LANGUAGE_MAP[lang as Language], value: lang }))}
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>
<>
{showEmojiPicker && <EmojiPicker
onSelect={(icon, icon_background) => {
console.log(icon, icon_background)
setEmoji({ icon, icon_background })
setShowEmojiPicker(false)
}}
onClose={() => {
setEmoji({ icon: '🤖', icon_background: '#FFEAD5' })
setShowEmojiPicker(false)
}}
/>}
<Modal
title={t(`${prefixSettings}.title`)}
isShow={isShow}
onClose={onHide}
className={`${s.settingsModal}`}
>
<div className={`mt-6 font-medium ${s.settingTitle} text-gray-900`}>{t(`${prefixSettings}.webName`)}</div>
<div className='flex mt-2'>
<AppIcon size='large'
onClick={() => { setShowEmojiPicker(true) }}
className='cursor-pointer !mr-3 self-center'
icon={emoji.icon}
background={emoji.icon_background}
/>
<input className={`flex-grow rounded-lg h-10 box-border px-3 ${s.projectName} bg-gray-100`}
value={inputInfo.title}
onChange={onChange('title')} />
</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-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-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-6 mb-2 font-medium ${s.settingTitle} text-gray-900 `}>{t(`${prefixSettings}.language`)}</div>
<SimpleSelect
items={Object.keys(LANGUAGE_MAP).map(lang => ({ name: LANGUAGE_MAP[lang as Language], value: lang }))}
defaultValue={language}
onSelect={item => setLanguage(item.value as Language)}
/> />
</>}
<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 >
{!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)

+ 2
- 2
web/app/components/base/loading/index.tsx Näytä tiedosto

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' : ''}`}>

+ 5
- 5
web/app/components/base/loading/style.css Näytä tiedosto

.spin-animation path { .spin-animation path {
animation: custom 2s linear infinite;
animation: custom 1s linear infinite;
} }


@keyframes custom { @keyframes custom {
} }


.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;
}

+ 3
- 0
web/types/app.ts Näytä tiedosto

copyright: string copyright: string
/** Privacy Policy */ /** Privacy Policy */
privacy_policy: string privacy_policy: string

icon: string
icon_background: string
} }


/** /**

Loading…
Peruuta
Tallenna