share-project-modal.js 3.8 KB

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