You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.tsx 11KB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. 'use client'
  2. import { useTranslation } from 'react-i18next'
  3. import { Fragment, useState } from 'react'
  4. import { useRouter } from 'next/navigation'
  5. import {
  6. RiAccountCircleLine,
  7. RiArrowRightUpLine,
  8. RiBookOpenLine,
  9. RiGithubLine,
  10. RiGraduationCapFill,
  11. RiInformation2Line,
  12. RiLogoutBoxRLine,
  13. RiMap2Line,
  14. RiSettings3Line,
  15. RiStarLine,
  16. RiTShirt2Line,
  17. } from '@remixicon/react'
  18. import Link from 'next/link'
  19. import { Menu, MenuButton, MenuItem, MenuItems, Transition } from '@headlessui/react'
  20. import Indicator from '../indicator'
  21. import AccountAbout from '../account-about'
  22. import GithubStar from '../github-star'
  23. import Support from './support'
  24. import Compliance from './compliance'
  25. import PremiumBadge from '@/app/components/base/premium-badge'
  26. import Avatar from '@/app/components/base/avatar'
  27. import ThemeSwitcher from '@/app/components/base/theme-switcher'
  28. import { logout } from '@/service/common'
  29. import { useAppContext } from '@/context/app-context'
  30. import { useProviderContext } from '@/context/provider-context'
  31. import { useModalContext } from '@/context/modal-context'
  32. import { IS_CLOUD_EDITION } from '@/config'
  33. import cn from '@/utils/classnames'
  34. import { useGlobalPublicStore } from '@/context/global-public-context'
  35. import { useDocLink } from '@/context/i18n'
  36. export default function AppSelector() {
  37. const itemClassName = `
  38. flex items-center w-full h-8 pl-3 pr-2 text-text-secondary system-md-regular
  39. rounded-lg hover:bg-state-base-hover cursor-pointer gap-1
  40. `
  41. const router = useRouter()
  42. const [aboutVisible, setAboutVisible] = useState(false)
  43. const { systemFeatures } = useGlobalPublicStore()
  44. const { t } = useTranslation()
  45. const docLink = useDocLink()
  46. const { userProfile, langGeniusVersionInfo, isCurrentWorkspaceOwner } = useAppContext()
  47. const { isEducationAccount } = useProviderContext()
  48. const { setShowAccountSettingModal } = useModalContext()
  49. const handleLogout = async () => {
  50. await logout({
  51. url: '/logout',
  52. params: {},
  53. })
  54. localStorage.removeItem('setup_status')
  55. localStorage.removeItem('console_token')
  56. localStorage.removeItem('refresh_token')
  57. // To avoid use other account's education notice info
  58. localStorage.removeItem('education-reverify-prev-expire-at')
  59. localStorage.removeItem('education-reverify-has-noticed')
  60. localStorage.removeItem('education-expired-has-noticed')
  61. router.push('/signin')
  62. }
  63. return (
  64. <div className="">
  65. <Menu as="div" className="relative inline-block text-left">
  66. {
  67. ({ open }) => (
  68. <>
  69. <MenuButton className={cn('inline-flex items-center rounded-[20px] p-0.5 hover:bg-background-default-dodge', open && 'bg-background-default-dodge')}>
  70. <Avatar avatar={userProfile.avatar_url} name={userProfile.name} size={36} />
  71. </MenuButton>
  72. <Transition
  73. as={Fragment}
  74. enter="transition ease-out duration-100"
  75. enterFrom="transform opacity-0 scale-95"
  76. enterTo="transform opacity-100 scale-100"
  77. leave="transition ease-in duration-75"
  78. leaveFrom="transform opacity-100 scale-100"
  79. leaveTo="transform opacity-0 scale-95"
  80. >
  81. <MenuItems
  82. className="
  83. absolute right-0 mt-1.5 w-60 max-w-80
  84. origin-top-right divide-y divide-divider-subtle rounded-xl bg-components-panel-bg-blur shadow-lg
  85. backdrop-blur-sm focus:outline-none
  86. "
  87. >
  88. <div className="px-1 py-1">
  89. <MenuItem disabled>
  90. <div className='flex flex-nowrap items-center py-2 pl-3 pr-2'>
  91. <div className='grow'>
  92. <div className='system-md-medium break-all text-text-primary'>
  93. {userProfile.name}
  94. {isEducationAccount && (
  95. <PremiumBadge size='s' color='blue' className='ml-1 !px-2'>
  96. <RiGraduationCapFill className='mr-1 h-3 w-3' />
  97. <span className='system-2xs-medium'>EDU</span>
  98. </PremiumBadge>
  99. )}
  100. </div>
  101. <div className='system-xs-regular break-all text-text-tertiary'>{userProfile.email}</div>
  102. </div>
  103. <Avatar avatar={userProfile.avatar_url} name={userProfile.name} size={36} />
  104. </div>
  105. </MenuItem>
  106. <MenuItem>
  107. <Link
  108. className={cn(itemClassName, 'group',
  109. 'data-[active]:bg-state-base-hover',
  110. )}
  111. href='/account'
  112. target='_self' rel='noopener noreferrer'>
  113. <RiAccountCircleLine className='size-4 shrink-0 text-text-tertiary' />
  114. <div className='system-md-regular grow px-1 text-text-secondary'>{t('common.account.account')}</div>
  115. <RiArrowRightUpLine className='size-[14px] shrink-0 text-text-tertiary' />
  116. </Link>
  117. </MenuItem>
  118. <MenuItem>
  119. <div className={cn(itemClassName,
  120. 'data-[active]:bg-state-base-hover',
  121. )} onClick={() => setShowAccountSettingModal({ payload: 'members' })}>
  122. <RiSettings3Line className='size-4 shrink-0 text-text-tertiary' />
  123. <div className='system-md-regular grow px-1 text-text-secondary'>{t('common.userProfile.settings')}</div>
  124. </div>
  125. </MenuItem>
  126. </div>
  127. {!systemFeatures.branding.enabled && <>
  128. <div className='p-1'>
  129. <MenuItem>
  130. <Link
  131. className={cn(itemClassName, 'group justify-between',
  132. 'data-[active]:bg-state-base-hover',
  133. )}
  134. href={docLink('/introduction')}
  135. target='_blank' rel='noopener noreferrer'>
  136. <RiBookOpenLine className='size-4 shrink-0 text-text-tertiary' />
  137. <div className='system-md-regular grow px-1 text-text-secondary'>{t('common.userProfile.helpCenter')}</div>
  138. <RiArrowRightUpLine className='size-[14px] shrink-0 text-text-tertiary' />
  139. </Link>
  140. </MenuItem>
  141. <Support />
  142. {IS_CLOUD_EDITION && isCurrentWorkspaceOwner && <Compliance />}
  143. </div>
  144. <div className='p-1'>
  145. <MenuItem>
  146. <Link
  147. className={cn(itemClassName, 'group justify-between',
  148. 'data-[active]:bg-state-base-hover',
  149. )}
  150. href='https://roadmap.dify.ai'
  151. target='_blank' rel='noopener noreferrer'>
  152. <RiMap2Line className='size-4 shrink-0 text-text-tertiary' />
  153. <div className='system-md-regular grow px-1 text-text-secondary'>{t('common.userProfile.roadmap')}</div>
  154. <RiArrowRightUpLine className='size-[14px] shrink-0 text-text-tertiary' />
  155. </Link>
  156. </MenuItem>
  157. <MenuItem>
  158. <Link
  159. className={cn(itemClassName, 'group justify-between',
  160. 'data-[active]:bg-state-base-hover',
  161. )}
  162. href='https://github.com/langgenius/dify'
  163. target='_blank' rel='noopener noreferrer'>
  164. <RiGithubLine className='size-4 shrink-0 text-text-tertiary' />
  165. <div className='system-md-regular grow px-1 text-text-secondary'>{t('common.userProfile.github')}</div>
  166. <div className='flex items-center gap-0.5 rounded-[5px] border border-divider-deep bg-components-badge-bg-dimm px-[5px] py-[3px]'>
  167. <RiStarLine className='size-3 shrink-0 text-text-tertiary' />
  168. <GithubStar className='system-2xs-medium-uppercase text-text-tertiary' />
  169. </div>
  170. </Link>
  171. </MenuItem>
  172. {
  173. document?.body?.getAttribute('data-public-site-about') !== 'hide' && (
  174. <MenuItem>
  175. <div className={cn(itemClassName, 'justify-between',
  176. 'data-[active]:bg-state-base-hover',
  177. )} onClick={() => setAboutVisible(true)}>
  178. <RiInformation2Line className='size-4 shrink-0 text-text-tertiary' />
  179. <div className='system-md-regular grow px-1 text-text-secondary'>{t('common.userProfile.about')}</div>
  180. <div className='flex shrink-0 items-center'>
  181. <div className='system-xs-regular mr-2 text-text-tertiary'>{langGeniusVersionInfo.current_version}</div>
  182. <Indicator color={langGeniusVersionInfo.current_version === langGeniusVersionInfo.latest_version ? 'green' : 'orange'} />
  183. </div>
  184. </div>
  185. </MenuItem>
  186. )
  187. }
  188. </div>
  189. </>}
  190. <MenuItem disabled>
  191. <div className='p-1'>
  192. <div className={cn(itemClassName, 'hover:bg-transparent')}>
  193. <RiTShirt2Line className='size-4 shrink-0 text-text-tertiary' />
  194. <div className='system-md-regular grow px-1 text-text-secondary'>{t('common.theme.theme')}</div>
  195. <ThemeSwitcher />
  196. </div>
  197. </div>
  198. </MenuItem>
  199. <MenuItem>
  200. <div className='p-1' onClick={() => handleLogout()}>
  201. <div
  202. className={cn(itemClassName, 'group justify-between',
  203. 'data-[active]:bg-state-base-hover',
  204. )}
  205. >
  206. <RiLogoutBoxRLine className='size-4 shrink-0 text-text-tertiary' />
  207. <div className='system-md-regular grow px-1 text-text-secondary'>{t('common.userProfile.logout')}</div>
  208. </div>
  209. </div>
  210. </MenuItem>
  211. </MenuItems>
  212. </Transition>
  213. </>
  214. )
  215. }
  216. </Menu>
  217. {
  218. aboutVisible && <AccountAbout onCancel={() => setAboutVisible(false)} langGeniusVersionInfo={langGeniusVersionInfo} />
  219. }
  220. </div >
  221. )
  222. }