SavePageControls.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import React, { useCallback } from 'react';
  2. import EventEmitter from 'events';
  3. import { isTopPage, isUsersProtectedPages } from '@growi/core/dist/utils/page-path-utils';
  4. import { useTranslation } from 'next-i18next';
  5. import {
  6. UncontrolledButtonDropdown, Button,
  7. DropdownToggle, DropdownMenu, DropdownItem,
  8. } from 'reactstrap';
  9. import type { IPageGrantData } from '~/interfaces/page';
  10. import { useIsAclEnabled } from '~/stores/context';
  11. import { useWaitingSaveProcessing } from '~/stores/editor';
  12. import { useSWRxCurrentPage, useIsEditablePage } from '~/stores/page';
  13. import { useSelectedGrant } from '~/stores/ui';
  14. import loggerFactory from '~/utils/logger';
  15. import GrantSelector from './SavePageControls/GrantSelector';
  16. declare global {
  17. // eslint-disable-next-line vars-on-top, no-var
  18. var globalEmitter: EventEmitter;
  19. }
  20. const logger = loggerFactory('growi:SavePageControls');
  21. export type SavePageControlsProps = {
  22. slackChannels: string
  23. }
  24. export const SavePageControls = (props: SavePageControlsProps): JSX.Element | null => {
  25. const { slackChannels } = props;
  26. const { t } = useTranslation();
  27. const { data: currentPage } = useSWRxCurrentPage();
  28. const { data: isEditablePage } = useIsEditablePage();
  29. const { data: isAclEnabled } = useIsAclEnabled();
  30. const { data: grantData, mutate: mutateGrant } = useSelectedGrant();
  31. const { data: _isWaitingSaveProcessing } = useWaitingSaveProcessing();
  32. const isWaitingSaveProcessing = _isWaitingSaveProcessing === true; // ignore undefined
  33. const updateGrantHandler = useCallback((grantData: IPageGrantData): void => {
  34. mutateGrant(grantData);
  35. }, [mutateGrant]);
  36. const save = useCallback(async(): Promise<void> => {
  37. // save
  38. globalEmitter.emit('saveAndReturnToView', { slackChannels });
  39. }, [slackChannels]);
  40. const saveAndOverwriteScopesOfDescendants = useCallback(() => {
  41. // save
  42. globalEmitter.emit('saveAndReturnToView', { overwriteScopesOfDescendants: true, slackChannels });
  43. }, [slackChannels]);
  44. if (isEditablePage == null || isAclEnabled == null || grantData == null) {
  45. return null;
  46. }
  47. if (!isEditablePage) {
  48. return null;
  49. }
  50. const { grant, grantedGroup } = grantData;
  51. const isGrantSelectorDisabledPage = isTopPage(currentPage?.path ?? '') || isUsersProtectedPages(currentPage?.path ?? '');
  52. const labelSubmitButton = (currentPage != null && !currentPage.isEmpty) ? t('Update') : t('Create');
  53. const labelOverwriteScopes = t('page_edit.overwrite_scopes', { operation: labelSubmitButton });
  54. return (
  55. <div className="d-flex align-items-center flex-nowrap">
  56. {isAclEnabled
  57. && (
  58. <div className="me-2">
  59. <GrantSelector
  60. grant={grant}
  61. disabled={isGrantSelectorDisabledPage}
  62. grantGroupId={grantedGroup?.id}
  63. grantGroupName={grantedGroup?.name}
  64. onUpdateGrant={updateGrantHandler}
  65. />
  66. </div>
  67. )
  68. }
  69. <UncontrolledButtonDropdown direction="up">
  70. <Button
  71. id="caret"
  72. data-testid="save-page-btn"
  73. color="primary"
  74. className="btn-submit"
  75. onClick={save}
  76. disabled={isWaitingSaveProcessing}
  77. >
  78. {isWaitingSaveProcessing && (
  79. <i className="fa fa-spinner fa-pulse me-1"></i>
  80. )}
  81. {labelSubmitButton}
  82. </Button>
  83. <DropdownToggle caret color="primary" disabled={isWaitingSaveProcessing} />
  84. <DropdownMenu end>
  85. <DropdownItem onClick={saveAndOverwriteScopesOfDescendants}>
  86. {labelOverwriteScopes}
  87. </DropdownItem>
  88. </DropdownMenu>
  89. </UncontrolledButtonDropdown>
  90. </div>
  91. );
  92. };