Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

menu-dropdown.tsx 4.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { useCallback, useRef, useState } from 'react'
  4. import { useTranslation } from 'react-i18next'
  5. import type { Placement } from '@floating-ui/react'
  6. import {
  7. RiEqualizer2Line,
  8. } from '@remixicon/react'
  9. import { usePathname, useRouter } from 'next/navigation'
  10. import Divider from '../../base/divider'
  11. import InfoModal from './info-modal'
  12. import ActionButton from '@/app/components/base/action-button'
  13. import {
  14. PortalToFollowElem,
  15. PortalToFollowElemContent,
  16. PortalToFollowElemTrigger,
  17. } from '@/app/components/base/portal-to-follow-elem'
  18. import ThemeSwitcher from '@/app/components/base/theme-switcher'
  19. import type { SiteInfo } from '@/models/share'
  20. import cn from '@/utils/classnames'
  21. import { AccessMode } from '@/models/access-control'
  22. import { useWebAppStore } from '@/context/web-app-context'
  23. type Props = {
  24. data?: SiteInfo
  25. placement?: Placement
  26. hideLogout?: boolean
  27. }
  28. const MenuDropdown: FC<Props> = ({
  29. data,
  30. placement,
  31. hideLogout,
  32. }) => {
  33. const webAppAccessMode = useWebAppStore(s => s.webAppAccessMode)
  34. const router = useRouter()
  35. const pathname = usePathname()
  36. const { t } = useTranslation()
  37. const [open, doSetOpen] = useState(false)
  38. const openRef = useRef(open)
  39. const setOpen = useCallback((v: boolean) => {
  40. doSetOpen(v)
  41. openRef.current = v
  42. }, [doSetOpen])
  43. const handleTrigger = useCallback(() => {
  44. setOpen(!openRef.current)
  45. }, [setOpen])
  46. const handleLogout = useCallback(() => {
  47. localStorage.removeItem('token')
  48. localStorage.removeItem('webapp_access_token')
  49. router.replace(`/webapp-signin?redirect_url=${pathname}`)
  50. }, [router, pathname])
  51. const [show, setShow] = useState(false)
  52. return (
  53. <>
  54. <PortalToFollowElem
  55. open={open}
  56. onOpenChange={setOpen}
  57. placement={placement || 'bottom-end'}
  58. offset={{
  59. mainAxis: 4,
  60. crossAxis: -4,
  61. }}
  62. >
  63. <PortalToFollowElemTrigger onClick={handleTrigger}>
  64. <div>
  65. <ActionButton size='l' className={cn(open && 'bg-state-base-hover')}>
  66. <RiEqualizer2Line className='h-[18px] w-[18px]' />
  67. </ActionButton>
  68. </div>
  69. </PortalToFollowElemTrigger>
  70. <PortalToFollowElemContent className='z-50'>
  71. <div className='w-[224px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg backdrop-blur-sm'>
  72. <div className='p-1'>
  73. <div className={cn('system-md-regular flex cursor-pointer items-center rounded-lg py-1.5 pl-3 pr-2 text-text-secondary')}>
  74. <div className='grow'>{t('common.theme.theme')}</div>
  75. <ThemeSwitcher />
  76. </div>
  77. </div>
  78. <Divider type='horizontal' className='my-0' />
  79. <div className='p-1'>
  80. {data?.privacy_policy && (
  81. <a href={data.privacy_policy} target='_blank' className='system-md-regular flex cursor-pointer items-center rounded-lg px-3 py-1.5 text-text-secondary hover:bg-state-base-hover'>
  82. <span className='grow'>{t('share.chat.privacyPolicyMiddle')}</span>
  83. </a>
  84. )}
  85. <div
  86. onClick={() => {
  87. handleTrigger()
  88. setShow(true)
  89. }}
  90. className='system-md-regular cursor-pointer rounded-lg px-3 py-1.5 text-text-secondary hover:bg-state-base-hover'
  91. >{t('common.userProfile.about')}</div>
  92. </div>
  93. {!(hideLogout || webAppAccessMode === AccessMode.EXTERNAL_MEMBERS || webAppAccessMode === AccessMode.PUBLIC) && (
  94. <div className='p-1'>
  95. <div
  96. onClick={handleLogout}
  97. className='system-md-regular cursor-pointer rounded-lg px-3 py-1.5 text-text-secondary hover:bg-state-base-hover'
  98. >
  99. {t('common.userProfile.logout')}
  100. </div>
  101. </div>
  102. )}
  103. </div>
  104. </PortalToFollowElemContent>
  105. </PortalToFollowElem>
  106. {show && (
  107. <InfoModal
  108. isShow={show}
  109. onClose={() => {
  110. setShow(false)
  111. }}
  112. data={data}
  113. />
  114. )}
  115. </>
  116. )
  117. }
  118. export default React.memo(MenuDropdown)