share-project-modal.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  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. project_id: project._id,
  53. })
  54. }
  55. }, [splitTestVariants, project._id, show])
  56. // reset error when the modal is opened
  57. useEffect(() => {
  58. if (show) {
  59. setError(undefined)
  60. }
  61. }, [show])
  62. // close the modal if not in flight
  63. const cancel = useCallback(() => {
  64. if (!inFlight) {
  65. handleHide()
  66. }
  67. }, [handleHide, inFlight])
  68. // update `error` and `inFlight` while sending a request
  69. const monitorRequest = useCallback(request => {
  70. setError(undefined)
  71. setInFlight(true)
  72. const promise = request()
  73. promise.catch(error => {
  74. setError(
  75. error.data?.errorReason ||
  76. error.data?.error ||
  77. 'generic_something_went_wrong'
  78. )
  79. })
  80. promise.finally(() => {
  81. setInFlight(false)
  82. })
  83. return promise
  84. }, [])
  85. // merge the new data with the old project data
  86. const updateProject = useCallback(
  87. data => Object.assign(project, data),
  88. [project]
  89. )
  90. if (!project) {
  91. return null
  92. }
  93. return (
  94. <ShareProjectContext.Provider
  95. value={{
  96. updateProject,
  97. monitorRequest,
  98. inFlight,
  99. setInFlight,
  100. error,
  101. setError,
  102. }}
  103. >
  104. <ShareProjectModalContent
  105. animation={animation}
  106. cancel={cancel}
  107. error={error}
  108. inFlight={inFlight}
  109. show={show}
  110. />
  111. </ShareProjectContext.Provider>
  112. )
  113. })
  114. ShareProjectModal.propTypes = {
  115. animation: PropTypes.bool,
  116. handleHide: PropTypes.func.isRequired,
  117. show: PropTypes.bool.isRequired,
  118. }
  119. export default ShareProjectModal