share-project-modal.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import React, {
  2. createContext,
  3. useCallback,
  4. useContext,
  5. useEffect,
  6. useState,
  7. } from 'react'
  8. import PropTypes from 'prop-types'
  9. import ShareProjectModalContent from './share-project-modal-content'
  10. import {
  11. useProjectContext,
  12. projectShape,
  13. } from '../../../shared/context/project-context'
  14. import { useSplitTestContext } from '../../../shared/context/split-test-context'
  15. import { sendMB } from '../../../infrastructure/event-tracking'
  16. const ShareProjectContext = createContext()
  17. ShareProjectContext.Provider.propTypes = {
  18. value: PropTypes.shape({
  19. updateProject: PropTypes.func.isRequired,
  20. monitorRequest: PropTypes.func.isRequired,
  21. inFlight: PropTypes.bool,
  22. setInFlight: PropTypes.func,
  23. error: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
  24. setError: PropTypes.func,
  25. }),
  26. }
  27. export function useShareProjectContext() {
  28. const context = useContext(ShareProjectContext)
  29. if (!context) {
  30. throw new Error(
  31. 'useShareProjectContext is only available inside ShareProjectProvider'
  32. )
  33. }
  34. return context
  35. }
  36. const ShareProjectModal = React.memo(function ShareProjectModal({
  37. handleHide,
  38. show,
  39. animation = true,
  40. }) {
  41. const [inFlight, setInFlight] = useState(false)
  42. const [error, setError] = useState()
  43. const project = useProjectContext(projectShape)
  44. const { splitTestVariants } = useSplitTestContext({
  45. splitTestVariants: PropTypes.object,
  46. })
  47. // send tracking event when the modal is opened
  48. useEffect(() => {
  49. if (show) {
  50. sendMB('share-modal-opened', {
  51. splitTestVariant: splitTestVariants['null-test-share-modal'],
  52. })
  53. }
  54. }, [splitTestVariants, show])
  55. // reset error when the modal is opened
  56. useEffect(() => {
  57. if (show) {
  58. setError(undefined)
  59. }
  60. }, [show])
  61. // close the modal if not in flight
  62. const cancel = useCallback(() => {
  63. if (!inFlight) {
  64. handleHide()
  65. }
  66. }, [handleHide, inFlight])
  67. // update `error` and `inFlight` while sending a request
  68. const monitorRequest = useCallback(request => {
  69. setError(undefined)
  70. setInFlight(true)
  71. const promise = request()
  72. promise.catch(error => {
  73. setError(
  74. error.data?.errorReason ||
  75. error.data?.error ||
  76. 'generic_something_went_wrong'
  77. )
  78. })
  79. promise.finally(() => {
  80. setInFlight(false)
  81. })
  82. return promise
  83. }, [])
  84. // merge the new data with the old project data
  85. const updateProject = useCallback(
  86. data => Object.assign(project, data),
  87. [project]
  88. )
  89. if (!project) {
  90. return null
  91. }
  92. return (
  93. <ShareProjectContext.Provider
  94. value={{
  95. updateProject,
  96. monitorRequest,
  97. inFlight,
  98. setInFlight,
  99. error,
  100. setError,
  101. }}
  102. >
  103. <ShareProjectModalContent
  104. animation={animation}
  105. cancel={cancel}
  106. error={error}
  107. inFlight={inFlight}
  108. show={show}
  109. />
  110. </ShareProjectContext.Provider>
  111. )
  112. })
  113. ShareProjectModal.propTypes = {
  114. animation: PropTypes.bool,
  115. handleHide: PropTypes.func.isRequired,
  116. show: PropTypes.bool.isRequired,
  117. }
  118. export default ShareProjectModal