share-project-modal.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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 useScopeValue from '../../../shared/context/util/scope-value-hook'
  11. const ShareProjectContext = createContext()
  12. ShareProjectContext.Provider.propTypes = {
  13. value: PropTypes.shape({
  14. isAdmin: PropTypes.bool.isRequired,
  15. updateProject: PropTypes.func.isRequired,
  16. monitorRequest: PropTypes.func.isRequired,
  17. eventTracking: PropTypes.shape({
  18. sendMB: PropTypes.func.isRequired
  19. }),
  20. inFlight: PropTypes.bool,
  21. setInFlight: PropTypes.func,
  22. error: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
  23. setError: PropTypes.func
  24. })
  25. }
  26. export function useShareProjectContext() {
  27. const context = useContext(ShareProjectContext)
  28. if (!context) {
  29. throw new Error(
  30. 'useShareProjectContext is only available inside ShareProjectProvider'
  31. )
  32. }
  33. return context
  34. }
  35. const projectShape = PropTypes.shape({
  36. _id: PropTypes.string.isRequired,
  37. members: PropTypes.arrayOf(
  38. PropTypes.shape({
  39. _id: PropTypes.string.isRequired
  40. })
  41. ),
  42. invites: PropTypes.arrayOf(
  43. PropTypes.shape({
  44. _id: PropTypes.string.isRequired
  45. })
  46. ),
  47. name: PropTypes.string,
  48. features: PropTypes.shape({
  49. collaborators: PropTypes.number
  50. }),
  51. publicAccesLevel: PropTypes.string,
  52. tokens: PropTypes.shape({
  53. readOnly: PropTypes.string,
  54. readAndWrite: PropTypes.string
  55. }),
  56. owner: PropTypes.shape({
  57. email: PropTypes.string
  58. })
  59. })
  60. const ProjectContext = createContext()
  61. ProjectContext.Provider.propTypes = {
  62. value: projectShape
  63. }
  64. export function useProjectContext() {
  65. const context = useContext(ProjectContext)
  66. if (!context) {
  67. throw new Error(
  68. 'useProjectContext is only available inside ShareProjectProvider'
  69. )
  70. }
  71. return context
  72. }
  73. export default function ShareProjectModal({
  74. handleHide,
  75. show,
  76. animation = true,
  77. isAdmin,
  78. eventTracking,
  79. ide
  80. }) {
  81. const [inFlight, setInFlight] = useState(false)
  82. const [error, setError] = useState()
  83. const [project, setProject] = useScopeValue('project', ide.$scope, true)
  84. // reset error when the modal is opened
  85. useEffect(() => {
  86. if (show) {
  87. setError(undefined)
  88. }
  89. }, [show])
  90. // close the modal if not in flight
  91. const cancel = useCallback(() => {
  92. if (!inFlight) {
  93. handleHide()
  94. }
  95. }, [handleHide, inFlight])
  96. // update `error` and `inFlight` while sending a request
  97. const monitorRequest = useCallback(request => {
  98. setError(undefined)
  99. setInFlight(true)
  100. const promise = request()
  101. promise.catch(error => {
  102. setError(
  103. error.data?.errorReason ||
  104. error.data?.error ||
  105. 'generic_something_went_wrong'
  106. )
  107. })
  108. promise.finally(() => {
  109. setInFlight(false)
  110. })
  111. return promise
  112. }, [])
  113. // merge the new data with the old project data
  114. const updateProject = useCallback(
  115. data => {
  116. setProject(project => Object.assign(project, data))
  117. },
  118. [setProject]
  119. )
  120. if (!project) {
  121. return null
  122. }
  123. return (
  124. <ShareProjectContext.Provider
  125. value={{
  126. isAdmin,
  127. updateProject,
  128. eventTracking,
  129. monitorRequest,
  130. inFlight,
  131. setInFlight,
  132. error,
  133. setError
  134. }}
  135. >
  136. <ProjectContext.Provider value={project}>
  137. <ShareProjectModalContent
  138. animation={animation}
  139. cancel={cancel}
  140. error={error}
  141. inFlight={inFlight}
  142. show={show}
  143. />
  144. </ProjectContext.Provider>
  145. </ShareProjectContext.Provider>
  146. )
  147. }
  148. ShareProjectModal.propTypes = {
  149. animation: PropTypes.bool,
  150. handleHide: PropTypes.func.isRequired,
  151. isAdmin: PropTypes.bool.isRequired,
  152. ide: PropTypes.shape({
  153. $scope: PropTypes.object.isRequired
  154. }).isRequired,
  155. show: PropTypes.bool.isRequired,
  156. eventTracking: PropTypes.shape({
  157. sendMB: PropTypes.func.isRequired
  158. })
  159. }