V5PageMigration.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import React, {
  2. FC, useCallback, useEffect, useState,
  3. } from 'react';
  4. import { useTranslation } from 'next-i18next';
  5. import { toastError, toastSuccess } from '~/client/util/toastr';
  6. import {
  7. SocketEventName, PMStartedData, PMMigratingData, PMErrorCountData, PMEndedData,
  8. } from '~/interfaces/websocket';
  9. import { useGlobalAdminSocket } from '~/stores/websocket';
  10. import AdminAppContainer from '../../../client/services/AdminAppContainer';
  11. import { withUnstatedContainers } from '../../UnstatedUtils';
  12. import LabeledProgressBar from '../Common/LabeledProgressBar';
  13. import { ConfirmModal } from './ConfirmModal';
  14. type Props = {
  15. adminAppContainer: typeof AdminAppContainer & { v5PageMigrationHandler: () => Promise<{ isV5Compatible: boolean }> },
  16. }
  17. const V5PageMigration: FC<Props> = (props: Props) => {
  18. // Modal
  19. const [isV5PageMigrationModalShown, setIsV5PageMigrationModalShown] = useState(false);
  20. // Progress bar
  21. const [isInProgress, setProgressing] = useState<boolean | undefined>(undefined); // use false as ended
  22. const [total, setTotal] = useState<number>(0);
  23. const [skip, setSkip] = useState<number>(0);
  24. const [current, setCurrent] = useState<number>(0);
  25. const [isSucceeded, setSucceeded] = useState<boolean | undefined>(undefined);
  26. const { data: adminSocket } = useGlobalAdminSocket();
  27. const { t } = useTranslation();
  28. const { adminAppContainer } = props;
  29. /*
  30. * Local components
  31. */
  32. const renderResultMessage = useCallback((isSucceeded: boolean) => {
  33. return (
  34. <>
  35. {
  36. isSucceeded
  37. ? <p className="text-success p-1">{t('admin:v5_page_migration.migration_succeeded')}</p>
  38. : <p className="text-danger p-1">{t('admin:v5_page_migration.migration_failed')}</p>
  39. }
  40. </>
  41. );
  42. }, [t]);
  43. const renderProgressBar = () => {
  44. if (isInProgress == null) {
  45. return <></>;
  46. }
  47. return (
  48. <>
  49. {
  50. isSucceeded != null && renderResultMessage(isSucceeded)
  51. }
  52. <LabeledProgressBar
  53. header={t('admin:v5_page_migration.header_upgrading_progress')}
  54. currentCount={current}
  55. errorsCount={skip}
  56. totalCount={total}
  57. isInProgress={isInProgress}
  58. />
  59. </>
  60. );
  61. };
  62. /*
  63. * Functions
  64. */
  65. const onConfirm = async() => {
  66. setIsV5PageMigrationModalShown(false);
  67. try {
  68. const { isV5Compatible } = await adminAppContainer.v5PageMigrationHandler();
  69. if (isV5Compatible) {
  70. return toastSuccess(t('admin:v5_page_migration.already_upgraded'));
  71. }
  72. toastSuccess(t('admin:v5_page_migration.successfully_started'));
  73. }
  74. catch (err) {
  75. toastError(err);
  76. }
  77. };
  78. /*
  79. * Use Effect
  80. */
  81. // Setup Admin Socket
  82. useEffect(() => {
  83. adminSocket?.once(SocketEventName.PMStarted, (data: PMStartedData) => {
  84. setProgressing(true);
  85. setTotal(data.total);
  86. });
  87. adminSocket?.on(SocketEventName.PMMigrating, (data: PMMigratingData) => {
  88. setProgressing(true);
  89. setCurrent(data.count);
  90. });
  91. adminSocket?.on(SocketEventName.PMErrorCount, (data: PMErrorCountData) => {
  92. setProgressing(true);
  93. setSkip(data.skip);
  94. });
  95. adminSocket?.once(SocketEventName.PMEnded, (data: PMEndedData) => {
  96. setProgressing(false);
  97. setSucceeded(data.isSucceeded);
  98. });
  99. return () => {
  100. adminSocket?.off(SocketEventName.PMStarted);
  101. adminSocket?.off(SocketEventName.PMMigrating);
  102. adminSocket?.off(SocketEventName.PMErrorCount);
  103. adminSocket?.off(SocketEventName.PMEnded);
  104. };
  105. }, [adminSocket]);
  106. return (
  107. <>
  108. <ConfirmModal
  109. isModalOpen={isV5PageMigrationModalShown}
  110. warningMessage={t('admin:v5_page_migration.modal_migration_warning')}
  111. supplymentaryMessage={t('admin:v5_page_migration.migration_note')}
  112. confirmButtonTitle={t('admin:v5_page_migration.start_upgrading')}
  113. onConfirm={onConfirm}
  114. onCancel={() => setIsV5PageMigrationModalShown(false)}
  115. />
  116. <p className="card custom-card">
  117. {t('admin:v5_page_migration.migration_desc')}
  118. <br />
  119. <br />
  120. <span className="text-danger">
  121. <span className="material-symbols-outlined">error</span>
  122. {t('admin:v5_page_migration.migration_note')}
  123. </span>
  124. </p>
  125. {renderProgressBar()}
  126. <div className="row my-3">
  127. <div className="mx-auto">
  128. <button type="button" className="btn btn-warning" onClick={() => setIsV5PageMigrationModalShown(true)} disabled={isInProgress != null}>
  129. {t('admin:v5_page_migration.upgrade_to_v5')}
  130. </button>
  131. </div>
  132. </div>
  133. </>
  134. );
  135. };
  136. export default withUnstatedContainers(V5PageMigration, [AdminAppContainer]);