share-project-modal.js 3.2 KB

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