PersonalDropdown.jsx 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. import React, { useState, useCallback } from 'react';
  2. import { UserPicture } from '@growi/ui';
  3. import { useTranslation } from 'react-i18next';
  4. import { UncontrolledTooltip } from 'reactstrap';
  5. import { useUserUISettings } from '~/client/services/user-ui-settings';
  6. import { toastError } from '~/client/util/apiNotification';
  7. import { apiv3Post } from '~/client/util/apiv3-client';
  8. import {
  9. isUserPreferenceExists,
  10. isDarkMode as isDarkModeByUtil,
  11. applyColorScheme,
  12. removeUserPreference,
  13. updateUserPreference,
  14. updateUserPreferenceWithOsSettings,
  15. } from '~/client/util/color-scheme';
  16. import { useCurrentUser } from '~/stores/context';
  17. import { usePreferDrawerModeByUser, usePreferDrawerModeOnEditByUser } from '~/stores/ui';
  18. import MoonIcon from '../Icons/MoonIcon';
  19. import SidebarDockIcon from '../Icons/SidebarDockIcon';
  20. import SidebarDrawerIcon from '../Icons/SidebarDrawerIcon';
  21. import SunIcon from '../Icons/SunIcon';
  22. const PersonalDropdown = () => {
  23. const { t } = useTranslation();
  24. const { data: currentUser } = useCurrentUser();
  25. const user = currentUser || {};
  26. const [useOsSettings, setOsSettings] = useState(!isUserPreferenceExists());
  27. const [isDarkMode, setIsDarkMode] = useState(isDarkModeByUtil());
  28. const { data: isPreferDrawerMode, mutate: mutatePreferDrawerMode } = usePreferDrawerModeByUser();
  29. const { data: isPreferDrawerModeOnEdit, mutate: mutatePreferDrawerModeOnEdit } = usePreferDrawerModeOnEditByUser();
  30. const { scheduleToPut } = useUserUISettings();
  31. const logoutHandler = async() => {
  32. try {
  33. await apiv3Post('/logout');
  34. window.location.reload();
  35. }
  36. catch (err) {
  37. toastError(err);
  38. }
  39. };
  40. const preferDrawerModeSwitchModifiedHandler = useCallback((bool) => {
  41. mutatePreferDrawerMode(bool);
  42. scheduleToPut({ preferDrawerModeByUser: bool });
  43. }, [mutatePreferDrawerMode, scheduleToPut]);
  44. const preferDrawerModeOnEditSwitchModifiedHandler = useCallback((bool) => {
  45. mutatePreferDrawerModeOnEdit(bool);
  46. scheduleToPut({ preferDrawerModeOnEditByUser: bool });
  47. }, [mutatePreferDrawerModeOnEdit, scheduleToPut]);
  48. const followOsCheckboxModifiedHandler = (bool) => {
  49. if (bool) {
  50. removeUserPreference();
  51. }
  52. else {
  53. updateUserPreferenceWithOsSettings();
  54. }
  55. applyColorScheme();
  56. // update states
  57. setOsSettings(bool);
  58. setIsDarkMode(isDarkModeByUtil());
  59. };
  60. const userPreferenceSwitchModifiedHandler = (bool) => {
  61. updateUserPreference(bool);
  62. applyColorScheme();
  63. // update state
  64. setIsDarkMode(isDarkModeByUtil());
  65. };
  66. /* eslint-disable react/prop-types */
  67. const IconWithTooltip = ({
  68. id, label, children, additionalClasses,
  69. }) => (
  70. <>
  71. <div id={id} className={`px-2 grw-icon-container ${additionalClasses != null ? additionalClasses : ''}`}>{children}</div>
  72. <UncontrolledTooltip placement="bottom" fade={false} target={id}>{label}</UncontrolledTooltip>
  73. </>
  74. );
  75. /* eslint-enable react/prop-types */
  76. return (
  77. <>
  78. {/* Button */}
  79. {/* remove .dropdown-toggle for hide caret */}
  80. {/* See https://stackoverflow.com/a/44577512/13183572 */}
  81. <a className="px-md-3 nav-link waves-effect waves-light" data-toggle="dropdown">
  82. <UserPicture user={user} noLink noTooltip /><span className="ml-1 d-none d-lg-inline-block">&nbsp;{user.name}</span>
  83. </a>
  84. {/* Menu */}
  85. <div className="dropdown-menu dropdown-menu-right">
  86. <div className="px-4 pt-3 pb-2 text-center">
  87. <UserPicture user={user} size="lg" noLink noTooltip />
  88. <h5 className="mt-2">
  89. {user.name}
  90. </h5>
  91. <div className="my-2">
  92. <i className="icon-user icon-fw"></i>{user.username}<br />
  93. <i className="icon-envelope icon-fw"></i><span className="grw-email-sm">{user.email}</span>
  94. </div>
  95. <div className="btn-group btn-block mt-2" role="group">
  96. <a className="btn btn-sm btn-outline-secondary col" href={`/user/${user.username}`}>
  97. <i className="icon-fw icon-home"></i>{ t('personal_dropdown.home') }
  98. </a>
  99. <a className="btn btn-sm btn-outline-secondary col" href="/me">
  100. <i className="icon-fw icon-wrench"></i>{ t('personal_dropdown.settings') }
  101. </a>
  102. </div>
  103. </div>
  104. <div className="dropdown-divider"></div>
  105. {/* Sidebar Mode */}
  106. <h6 className="dropdown-header">{t('personal_dropdown.sidebar_mode')}</h6>
  107. <form className="px-4">
  108. <div className="form-row justify-content-center">
  109. <div className="form-group col-auto mb-0 d-flex align-items-center">
  110. <IconWithTooltip id="iwt-sidebar-drawer" label="Drawer">
  111. <SidebarDrawerIcon />
  112. </IconWithTooltip>
  113. <div className="custom-control custom-switch custom-checkbox-secondary ml-2">
  114. <input
  115. id="swSidebarMode"
  116. className="custom-control-input"
  117. type="checkbox"
  118. checked={!isPreferDrawerMode}
  119. onChange={e => preferDrawerModeSwitchModifiedHandler(!e.target.checked)}
  120. />
  121. <label className="custom-control-label" htmlFor="swSidebarMode"></label>
  122. </div>
  123. <IconWithTooltip id="iwt-sidebar-dock" label="Dock">
  124. <SidebarDockIcon />
  125. </IconWithTooltip>
  126. </div>
  127. </div>
  128. </form>
  129. {/* Sidebar Mode on Editor */}
  130. <h6 className="dropdown-header">{t('personal_dropdown.sidebar_mode_editor')}</h6>
  131. <form className="px-4">
  132. <div className="form-row justify-content-center">
  133. <div className="form-group col-auto mb-0 d-flex align-items-center">
  134. <IconWithTooltip id="iwt-sidebar-editor-drawer" label="Drawer">
  135. <SidebarDrawerIcon />
  136. </IconWithTooltip>
  137. <div className="custom-control custom-switch custom-checkbox-secondary ml-2">
  138. <input
  139. id="swSidebarModeOnEditor"
  140. className="custom-control-input"
  141. type="checkbox"
  142. checked={!isPreferDrawerModeOnEdit}
  143. onChange={e => preferDrawerModeOnEditSwitchModifiedHandler(!e.target.checked)}
  144. />
  145. <label className="custom-control-label" htmlFor="swSidebarModeOnEditor"></label>
  146. </div>
  147. <IconWithTooltip id="iwt-sidebar-editor-dock" label="Dock">
  148. <SidebarDockIcon />
  149. </IconWithTooltip>
  150. </div>
  151. </div>
  152. </form>
  153. <div className="dropdown-divider"></div>
  154. {/* Color Mode */}
  155. <h6 className="dropdown-header">{t('personal_dropdown.color_mode')}</h6>
  156. <form className="px-4">
  157. <div className="form-row">
  158. <div className="form-group col-auto">
  159. <div className="custom-control custom-checkbox">
  160. <input
  161. id="cbFollowOs"
  162. className="custom-control-input"
  163. type="checkbox"
  164. checked={useOsSettings}
  165. onChange={e => followOsCheckboxModifiedHandler(e.target.checked)}
  166. />
  167. <label className="custom-control-label text-nowrap" htmlFor="cbFollowOs">{t('personal_dropdown.use_os_settings')}</label>
  168. </div>
  169. </div>
  170. </div>
  171. <div className="form-row justify-content-center">
  172. <div className="form-group col-auto mb-0 d-flex align-items-center">
  173. <IconWithTooltip id="iwt-light" label="Light" additionalClasses={useOsSettings ? 'grw-icon-container-muted' : ''}>
  174. <SunIcon />
  175. </IconWithTooltip>
  176. <div className="custom-control custom-switch custom-checkbox-secondary ml-2">
  177. <input
  178. id="swUserPreference"
  179. className="custom-control-input"
  180. type="checkbox"
  181. checked={isDarkMode}
  182. disabled={useOsSettings}
  183. onChange={e => userPreferenceSwitchModifiedHandler(e.target.checked)}
  184. />
  185. <label className="custom-control-label" htmlFor="swUserPreference"></label>
  186. </div>
  187. <IconWithTooltip id="iwt-dark" label="Dark" additionalClasses={useOsSettings ? 'grw-icon-container-muted' : ''}>
  188. <MoonIcon />
  189. </IconWithTooltip>
  190. </div>
  191. </div>
  192. </form>
  193. <div className="dropdown-divider"></div>
  194. <button type="button" className="dropdown-item" onClick={logoutHandler}><i className="icon-fw icon-power"></i>{ t('Sign out') }</button>
  195. </div>
  196. </>
  197. );
  198. };
  199. export default PersonalDropdown;