fix: free quota tip (#831)

This commit is contained in:
zxhlyh
2023-08-14 16:36:04 +08:00
committed by GitHub
parent c5d148bf94
commit 0feb0bf7c0
10 changed files with 79 additions and 51 deletions

View File

@@ -4,13 +4,12 @@ import { useEffect, useRef } from 'react'
import useSWRInfinite from 'swr/infinite' import useSWRInfinite from 'swr/infinite'
import { debounce } from 'lodash-es' import { debounce } from 'lodash-es'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useSearchParams } from 'next/navigation'
import AppCard from './AppCard' import AppCard from './AppCard'
import NewAppCard from './NewAppCard' import NewAppCard from './NewAppCard'
import type { AppListResponse } from '@/models/app' import type { AppListResponse } from '@/models/app'
import { fetchAppList } from '@/service/apps' import { fetchAppList } from '@/service/apps'
import { useSelector } from '@/context/app-context' import { useSelector } from '@/context/app-context'
import { NEED_REFRESH_APP_LIST_KEY, SPARK_FREE_QUOTA_PENDING } from '@/config' import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
const getKey = (pageIndex: number, previousPageData: AppListResponse) => { const getKey = (pageIndex: number, previousPageData: AppListResponse) => {
if (!pageIndex || previousPageData.has_more) if (!pageIndex || previousPageData.has_more)
@@ -24,7 +23,6 @@ const Apps = () => {
const loadingStateRef = useRef(false) const loadingStateRef = useRef(false)
const pageContainerRef = useSelector(state => state.pageContainerRef) const pageContainerRef = useSelector(state => state.pageContainerRef)
const anchorRef = useRef<HTMLAnchorElement>(null) const anchorRef = useRef<HTMLAnchorElement>(null)
const searchParams = useSearchParams()
useEffect(() => { useEffect(() => {
document.title = `${t('app.title')} - Dify` document.title = `${t('app.title')} - Dify`
@@ -32,13 +30,6 @@ const Apps = () => {
localStorage.removeItem(NEED_REFRESH_APP_LIST_KEY) localStorage.removeItem(NEED_REFRESH_APP_LIST_KEY)
mutate() mutate()
} }
if (
localStorage.getItem(SPARK_FREE_QUOTA_PENDING) !== '1'
&& searchParams.get('type') === 'provider_apply_callback'
&& searchParams.get('provider') === 'spark'
&& searchParams.get('result') === 'success'
)
localStorage.setItem(SPARK_FREE_QUOTA_PENDING, '1')
}, []) }, [])
useEffect(() => { useEffect(() => {

View File

@@ -0,0 +1,5 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="link-external-01">
<path id="Icon" d="M10.5 4.5L10.5 1.5M10.5 1.5H7.5M10.5 1.5L6.5 5.5M5 2.5H3.9C3.05992 2.5 2.63988 2.5 2.31901 2.66349C2.03677 2.8073 1.8073 3.03677 1.66349 3.31901C1.5 3.63988 1.5 4.05992 1.5 4.9V8.1C1.5 8.94008 1.5 9.36012 1.66349 9.68099C1.8073 9.96323 2.03677 10.1927 2.31901 10.3365C2.63988 10.5 3.05992 10.5 3.9 10.5H7.1C7.94008 10.5 8.36012 10.5 8.68099 10.3365C8.96323 10.1927 9.1927 9.96323 9.33651 9.68099C9.5 9.36012 9.5 8.94008 9.5 8.1V7" stroke="#155EEF" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 650 B

View File

@@ -0,0 +1,38 @@
{
"icon": {
"type": "element",
"isRootNode": true,
"name": "svg",
"attributes": {
"width": "12",
"height": "12",
"viewBox": "0 0 12 12",
"fill": "none",
"xmlns": "http://www.w3.org/2000/svg"
},
"children": [
{
"type": "element",
"name": "g",
"attributes": {
"id": "link-external-01"
},
"children": [
{
"type": "element",
"name": "path",
"attributes": {
"id": "Icon",
"d": "M10.5 4.5L10.5 1.5M10.5 1.5H7.5M10.5 1.5L6.5 5.5M5 2.5H3.9C3.05992 2.5 2.63988 2.5 2.31901 2.66349C2.03677 2.8073 1.8073 3.03677 1.66349 3.31901C1.5 3.63988 1.5 4.05992 1.5 4.9V8.1C1.5 8.94008 1.5 9.36012 1.66349 9.68099C1.8073 9.96323 2.03677 10.1927 2.31901 10.3365C2.63988 10.5 3.05992 10.5 3.9 10.5H7.1C7.94008 10.5 8.36012 10.5 8.68099 10.3365C8.96323 10.1927 9.1927 9.96323 9.33651 9.68099C9.5 9.36012 9.5 8.94008 9.5 8.1V7",
"stroke": "currentColor",
"stroke-linecap": "round",
"stroke-linejoin": "round"
},
"children": []
}
]
}
]
},
"name": "LinkExternal01"
}

View File

@@ -0,0 +1,14 @@
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import * as React from 'react'
import data from './LinkExternal01.json'
import IconBase from '@/app/components/base/icons/IconBase'
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
props,
ref,
) => <IconBase {...props} ref={ref} data={data as IconData} />)
export default Icon

View File

@@ -5,6 +5,7 @@ export { default as Edit03 } from './Edit03'
export { default as Hash02 } from './Hash02' export { default as Hash02 } from './Hash02'
export { default as HelpCircle } from './HelpCircle' export { default as HelpCircle } from './HelpCircle'
export { default as InfoCircle } from './InfoCircle' export { default as InfoCircle } from './InfoCircle'
export { default as LinkExternal01 } from './LinkExternal01'
export { default as LinkExternal02 } from './LinkExternal02' export { default as LinkExternal02 } from './LinkExternal02'
export { default as Loading02 } from './Loading02' export { default as Loading02 } from './Loading02'
export { default as LogOut01 } from './LogOut01' export { default as LogOut01 } from './LogOut01'

View File

@@ -1,14 +1,14 @@
import { useEffect, useState } from 'react' import { useState } from 'react'
import type { FC } from 'react' import type { FC } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector' import { useContext } from 'use-context-selector'
import type { ProviderConfigItem, ProviderWithQuota, TypeWithI18N } from '../declarations' import type { ProviderConfigItem, TypeWithI18N } from '../declarations'
import { ProviderEnum as ProviderEnumValue } from '../declarations' import { ProviderEnum as ProviderEnumValue } from '../declarations'
import s from './index.module.css' import s from './index.module.css'
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 { submitFreeQuota } from '@/service/common' import { submitFreeQuota } from '@/service/common'
import { SPARK_FREE_QUOTA_PENDING } from '@/config' import { LinkExternal01 } from '@/app/components/base/icons/src/vender/line/general'
const TIP_MAP: { [k: string]: TypeWithI18N } = { const TIP_MAP: { [k: string]: TypeWithI18N } = {
[ProviderEnumValue.minimax]: { [ProviderEnumValue.minimax]: {
@@ -20,34 +20,17 @@ const TIP_MAP: { [k: string]: TypeWithI18N } = {
'zh-Hans': '免费获取 300 万个 token', 'zh-Hans': '免费获取 300 万个 token',
}, },
} }
const FREE_QUOTA_TIP = {
'en': 'Your 3 million tokens will be credited in 5 minutes.',
'zh-Hans': '您的 300 万 token 将在 5 分钟内到账。',
}
type FreeQuotaProps = { type FreeQuotaProps = {
modelItem: ProviderConfigItem modelItem: ProviderConfigItem
onUpdate: () => void onUpdate: () => void
freeProvider?: ProviderWithQuota
} }
const FreeQuota: FC<FreeQuotaProps> = ({ const FreeQuota: FC<FreeQuotaProps> = ({
modelItem, modelItem,
onUpdate, onUpdate,
freeProvider,
}) => { }) => {
const { locale } = useContext(I18n) const { locale } = useContext(I18n)
const { t } = useTranslation() const { t } = useTranslation()
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [freeQuotaPending, setFreeQuotaPending] = useState(false)
useEffect(() => {
if (
modelItem.key === ProviderEnumValue.spark
&& localStorage.getItem(SPARK_FREE_QUOTA_PENDING) === '1'
&& freeProvider
&& !freeProvider.is_valid
)
setFreeQuotaPending(true)
}, [freeProvider, modelItem.key])
const handleClick = async () => { const handleClick = async () => {
try { try {
@@ -64,29 +47,21 @@ const FreeQuota: FC<FreeQuotaProps> = ({
} }
} }
if (freeQuotaPending) {
return (
<div className='flex items-center'>
<div className={`${s.vender} ml-1 mr-2 text-xs font-medium text-transparent`}>{FREE_QUOTA_TIP[locale]}</div>
<Button
className='!px-3 !h-7 !rounded-md !text-xs !font-medium !bg-white !text-gray-700'
onClick={onUpdate}
>
{t('common.operation.reload')}
</Button>
<div className='mx-2 w-[1px] h-4 bg-black/5' />
</div>
)
}
return ( return (
<div className='flex items-center'> <div className='flex items-center'>
📣 📣
<div className={`${s.vender} ml-1 mr-2 text-xs font-medium text-transparent`}>{TIP_MAP[modelItem.key][locale]}</div> <div className={`${s.vender} ml-1 text-xs font-medium text-transparent`}>{TIP_MAP[modelItem.key][locale]}</div>
<div className='mx-1 text-xs font-medium text-gray-400'>·</div>
<a
href='https://docs.dify.ai/v/zh-hans/getting-started/faq/llms-use-faq#8.-ru-he-mian-fei-shen-ling-xun-fei-xing-huo-minimax-mo-xing-de-ti-yanedu'
target='_blank'
className='flex items-center text-xs font-medium text-[#155EEF]'>
{t('common.modelProvider.freeQuota.howToEarn')}
<LinkExternal01 className='ml-0.5 w-3 h-3' />
</a>
<Button <Button
type='primary' type='primary'
className='!px-3 !h-7 !rounded-md !text-xs !font-medium' className='ml-3 !px-3 !h-7 !rounded-md !text-xs !font-medium'
onClick={handleClick} onClick={handleClick}
disabled={loading} disabled={loading}
> >

View File

@@ -34,10 +34,9 @@ const Setting: FC<SettingProps> = ({
return ( return (
<div className='flex items-center'> <div className='flex items-center'>
{ {
(modelItem.key === ProviderEnum.minimax || modelItem.key === ProviderEnum.spark) && systemFree && !systemFree?.is_valid && !IS_CE_EDITION && ( (modelItem.key === ProviderEnum.minimax || modelItem.key === ProviderEnum.spark) && systemFree && !systemFree?.is_valid && !IS_CE_EDITION && locale === 'zh-Hans' && (
<FreeQuota <FreeQuota
modelItem={modelItem} modelItem={modelItem}
freeProvider={systemFree}
onUpdate={onUpdate} onUpdate={onUpdate}
/> />
) )

View File

@@ -120,4 +120,3 @@ export const VAR_ITEM_TEMPLATE = {
export const appDefaultIconBackground = '#D5F5F6' export const appDefaultIconBackground = '#D5F5F6'
export const NEED_REFRESH_APP_LIST_KEY = 'needRefreshAppList' export const NEED_REFRESH_APP_LIST_KEY = 'needRefreshAppList'
export const SPARK_FREE_QUOTA_PENDING = 'sparkFreeQuotaPending'

View File

@@ -250,6 +250,9 @@ const translation = {
front: 'Your API KEY will be encrypted and stored using', front: 'Your API KEY will be encrypted and stored using',
back: ' technology.', back: ' technology.',
}, },
freeQuota: {
howToEarn: 'How to earn',
},
}, },
dataSource: { dataSource: {
add: 'Add a data source', add: 'Add a data source',

View File

@@ -250,6 +250,9 @@ const translation = {
front: '您的密钥将使用', front: '您的密钥将使用',
back: '技术进行加密和存储。', back: '技术进行加密和存储。',
}, },
freeQuota: {
howToEarn: '如何获取',
},
}, },
dataSource: { dataSource: {
add: '添加数据源', add: '添加数据源',