share-project-modal.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  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. const ShareProjectModal = React.memo(function ShareProjectModal({
  71. handleHide,
  72. show,
  73. animation = true,
  74. isAdmin,
  75. }) {
  76. const [inFlight, setInFlight] = useState(false)
  77. const [error, setError] = useState()
  78. const [project, setProject] = useScopeValue('project', true)
  79. // reset error when the modal is opened
  80. useEffect(() => {
  81. if (show) {
  82. setError(undefined)
  83. }
  84. }, [show])
  85. // close the modal if not in flight
  86. const cancel = useCallback(() => {
  87. if (!inFlight) {
  88. handleHide()
  89. }
  90. }, [handleHide, inFlight])
  91. // update `error` and `inFlight` while sending a request
  92. const monitorRequest = useCallback(request => {
  93. setError(undefined)
  94. setInFlight(true)
  95. const promise = request()
  96. promise.catch(error => {
  97. setError(
  98. error.data?.errorReason ||
  99. error.data?.error ||
  100. 'generic_something_went_wrong'
  101. )
  102. })
  103. promise.finally(() => {
  104. setInFlight(false)
  105. })
  106. return promise
  107. }, [])
  108. // merge the new data with the old project data
  109. const updateProject = useCallback(
  110. data => {
  111. setProject(project => Object.assign(project, data))
  112. },
  113. [setProject]
  114. )
  115. if (!project) {
  116. return null
  117. }
  118. return (
  119. <ShareProjectContext.Provider
  120. value={{
  121. isAdmin,
  122. updateProject,
  123. monitorRequest,
  124. inFlight,
  125. setInFlight,
  126. error,
  127. setError,
  128. }}
  129. >
  130. <ProjectContext.Provider value={project}>
  131. <ShareProjectModalContent
  132. animation={animation}
  133. cancel={cancel}
  134. error={error}
  135. inFlight={inFlight}
  136. show={show}
  137. />
  138. </ProjectContext.Provider>
  139. </ShareProjectContext.Provider>
  140. )
  141. })
  142. ShareProjectModal.propTypes = {
  143. animation: PropTypes.bool,
  144. handleHide: PropTypes.func.isRequired,
  145. isAdmin: PropTypes.bool.isRequired,
  146. show: PropTypes.bool.isRequired,
  147. }
  148. export default ShareProjectModal