Browse Source

Feature/add emoji to webapp (#345)

crazywoola 1 year ago
parent
commit
433f8cb57e

+ 1 - 2
web/app/(commonLayout)/apps/NewAppDialog.tsx

@@ -52,7 +52,7 @@ const NewAppDialog = ({ show, onSuccess, onClose }: NewAppDialogProps) => {
       mutateTemplates()
       setIsWithTemplate(false)
     }
-  }, [show])
+  }, [mutateTemplates, show])
 
   const isCreatingRef = useRef(false)
   const onCreate: MouseEventHandler = useCallback(async () => {
@@ -97,7 +97,6 @@ const NewAppDialog = ({ show, onSuccess, onClose }: NewAppDialogProps) => {
   return <>
     {showEmojiPicker && <EmojiPicker
       onSelect={(icon, icon_background) => {
-        console.log(icon, icon_background)
         setEmoji({ icon, icon_background })
         setShowEmojiPicker(false)
       }}

+ 2 - 4
web/app/components/app/overview/customize/index.tsx

@@ -1,10 +1,10 @@
 'use client'
 import type { FC } from 'react'
 import React from 'react'
-import { AppMode } from '@/types/app'
 import { ArrowTopRightOnSquareIcon } from '@heroicons/react/24/outline'
 import { useTranslation } from 'react-i18next'
 import { useContext } from 'use-context-selector'
+import type { AppMode } from '@/types/app'
 import I18n from '@/context/i18n'
 import Button from '@/app/components/base/button'
 import Modal from '@/app/components/base/modal'
@@ -23,8 +23,6 @@ const StepNum: FC<{ children: React.ReactNode }> = ({ children }) =>
     {children}
   </div>
 
-
-
 const GithubIcon = ({ className }: { className: string }) => {
   return (
     <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>
           <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 API_KEY = {`'<Web API Key From Dify>'`}
+            export const API_KEY = {'\'<Web API Key From Dify>\''}
           </pre>
         </div>
       </div>

+ 86 - 62
web/app/components/app/overview/settings/index.tsx

@@ -7,11 +7,11 @@ import { Trans, useTranslation } from 'react-i18next'
 import s from './style.module.css'
 import Modal from '@/app/components/base/modal'
 import Button from '@/app/components/base/button'
-import Switch from '@/app/components/base/switch'
 import AppIcon from '@/app/components/base/app-icon'
 import { SimpleSelect } from '@/app/components/base/select'
 import type { AppDetailResponse } from '@/models/app'
 import type { Language } from '@/types/app'
+import EmojiPicker from '@/app/components/base/emoji-picker'
 
 export type ISettingsModalProps = {
   appInfo: AppDetailResponse
@@ -42,11 +42,14 @@ const SettingsModal: FC<ISettingsModalProps> = ({
   onSave,
 }) => {
   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 [language, setLanguage] = useState(default_language)
   const [saveLoading, setSaveLoading] = useState(false)
   const { t } = useTranslation()
+  // Emoji Picker
+  const [showEmojiPicker, setShowEmojiPicker] = useState(false)
+  const [emoji, setEmoji] = useState({ icon, icon_background })
 
   const onHide = () => {
     onClose()
@@ -64,6 +67,8 @@ const SettingsModal: FC<ISettingsModalProps> = ({
       prompt_public: false,
       copyright: inputInfo.copyright,
       privacy_policy: inputInfo.privacyPolicy,
+      icon: emoji.icon,
+      icon_background: emoji.icon_background,
     }
     await onSave(params)
     setSaveLoading(false)
@@ -77,69 +82,88 @@ const SettingsModal: FC<ISettingsModalProps> = ({
   }
 
   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>
-        <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)

+ 2 - 2
web/app/components/base/loading/index.tsx

@@ -1,11 +1,11 @@
 import React from 'react'
 
 import './style.css'
-interface ILoadingProps {
+type ILoadingProps = {
   type?: 'area' | 'app'
 }
 const Loading = (
-  { type = 'area' }: ILoadingProps = { type: 'area' }
+  { type = 'area' }: ILoadingProps = { type: 'area' },
 ) => {
   return (
     <div className={`flex w-full justify-center items-center ${type === 'app' ? 'h-full' : ''}`}>

+ 5 - 5
web/app/components/base/loading/style.css

@@ -1,5 +1,5 @@
 .spin-animation path {
-    animation: custom 2s linear infinite;
+    animation: custom 1s linear infinite;
 }
 
 @keyframes custom {
@@ -29,13 +29,13 @@
 }
 
 .spin-animation path:nth-child(2) {
-    animation-delay: 0.5s;
+    animation-delay: 0.25s;
 }
 
 .spin-animation path:nth-child(3) {
-    animation-delay: 1s;
+    animation-delay: 0.5s;
 }
 
 .spin-animation path:nth-child(4) {
-    animation-delay: 1.5s;
-}
+    animation-delay: 1s;
+}

+ 3 - 0
web/types/app.ts

@@ -179,6 +179,9 @@ export type SiteConfig = {
   copyright: string
   /** Privacy Policy */
   privacy_policy: string
+
+  icon: string
+  icon_background: string
 }
 
 /**