link-sharing.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. import React, { useCallback, useState } from 'react'
  2. import PropTypes from 'prop-types'
  3. import { Button, Col, OverlayTrigger, Row, Tooltip } from 'react-bootstrap'
  4. import { Trans } from 'react-i18next'
  5. import Icon from '../../../shared/components/icon'
  6. import {
  7. useProjectContext,
  8. useShareProjectContext
  9. } from './share-project-modal'
  10. import { setProjectAccessLevel } from '../utils/api'
  11. import CopyLink from '../../../shared/components/copy-link'
  12. export default function LinkSharing() {
  13. const [inflight, setInflight] = useState(false)
  14. const { monitorRequest, updateProject } = useShareProjectContext()
  15. const project = useProjectContext()
  16. // set the access level of a project
  17. const setAccessLevel = useCallback(
  18. publicAccesLevel => {
  19. setInflight(true)
  20. monitorRequest(() => setProjectAccessLevel(project, publicAccesLevel))
  21. .then(() => {
  22. // TODO: ideally this would use the response from the server
  23. updateProject({ publicAccesLevel })
  24. // TODO: eventTracking.sendMB('project-make-token-based') when publicAccesLevel is 'tokenBased'
  25. })
  26. .finally(() => {
  27. setInflight(false)
  28. })
  29. },
  30. [monitorRequest, project, updateProject]
  31. )
  32. switch (project.publicAccesLevel) {
  33. // Private (with token-access available)
  34. case 'private':
  35. return (
  36. <PrivateSharing setAccessLevel={setAccessLevel} inflight={inflight} />
  37. )
  38. // Token-based access
  39. case 'tokenBased':
  40. return (
  41. <TokenBasedSharing
  42. setAccessLevel={setAccessLevel}
  43. inflight={inflight}
  44. />
  45. )
  46. // Legacy public-access
  47. case 'readAndWrite':
  48. case 'readOnly':
  49. return (
  50. <LegacySharing
  51. setAccessLevel={setAccessLevel}
  52. accessLevel={project.publicAccesLevel}
  53. inflight={inflight}
  54. />
  55. )
  56. default:
  57. return null
  58. }
  59. }
  60. function PrivateSharing({ setAccessLevel, inflight }) {
  61. return (
  62. <Row className="public-access-level">
  63. <Col xs={12} className="text-center">
  64. <Trans i18nKey="link_sharing_is_off" />
  65. <span>&nbsp;&nbsp;</span>
  66. <Button
  67. type="button"
  68. bsStyle="link"
  69. className="btn-inline-link"
  70. onClick={() => setAccessLevel('tokenBased')}
  71. disabled={inflight}
  72. >
  73. <Trans i18nKey="turn_on_link_sharing" />
  74. </Button>
  75. <span>&nbsp;&nbsp;</span>
  76. <LinkSharingInfo />
  77. </Col>
  78. </Row>
  79. )
  80. }
  81. PrivateSharing.propTypes = {
  82. setAccessLevel: PropTypes.func.isRequired,
  83. inflight: PropTypes.bool
  84. }
  85. function TokenBasedSharing({ setAccessLevel, inflight }) {
  86. const project = useProjectContext()
  87. return (
  88. <Row className="public-access-level">
  89. <Col xs={12} className="text-center">
  90. <strong>
  91. <Trans i18nKey="link_sharing_is_on" />
  92. </strong>
  93. <span>&nbsp;&nbsp;</span>
  94. <Button
  95. bsStyle="link"
  96. className="btn-inline-link"
  97. onClick={() => setAccessLevel('private')}
  98. disabled={inflight}
  99. >
  100. <Trans i18nKey="turn_off_link_sharing" />
  101. </Button>
  102. <span>&nbsp;&nbsp;</span>
  103. <LinkSharingInfo />
  104. </Col>
  105. <Col xs={12} className="access-token-display-area">
  106. <div className="access-token-wrapper">
  107. <strong>
  108. <Trans i18nKey="anyone_with_link_can_edit" />
  109. </strong>
  110. <AccessToken
  111. token={project?.tokens?.readAndWrite}
  112. path="/"
  113. tooltipId="tooltip-copy-link-rw"
  114. />
  115. </div>
  116. <div className="access-token-wrapper">
  117. <strong>
  118. <Trans i18nKey="anyone_with_link_can_view" />
  119. </strong>
  120. <AccessToken
  121. token={project?.tokens?.readOnly}
  122. path="/read/"
  123. tooltipId="tooltip-copy-link-ro"
  124. />
  125. </div>
  126. </Col>
  127. </Row>
  128. )
  129. }
  130. TokenBasedSharing.propTypes = {
  131. setAccessLevel: PropTypes.func.isRequired,
  132. inflight: PropTypes.bool
  133. }
  134. function LegacySharing({ accessLevel, setAccessLevel, inflight }) {
  135. return (
  136. <Row className="public-access-level">
  137. <Col xs={12} className="text-center">
  138. <strong>
  139. {accessLevel === 'readAndWrite' && (
  140. <Trans i18nKey="this_project_is_public" />
  141. )}
  142. {accessLevel === 'readOnly' && (
  143. <Trans i18nKey="this_project_is_public_read_only" />
  144. )}
  145. </strong>
  146. <span>&nbsp;&nbsp;</span>
  147. <Button
  148. type="button"
  149. bsStyle="link"
  150. className="btn-inline-link"
  151. onClick={() => setAccessLevel('private')}
  152. disabled={inflight}
  153. >
  154. <Trans i18nKey="make_private" />
  155. </Button>
  156. <span>&nbsp;&nbsp;</span>
  157. <LinkSharingInfo />
  158. </Col>
  159. </Row>
  160. )
  161. }
  162. LegacySharing.propTypes = {
  163. accessLevel: PropTypes.string.isRequired,
  164. setAccessLevel: PropTypes.func.isRequired,
  165. inflight: PropTypes.bool
  166. }
  167. export function ReadOnlyTokenLink() {
  168. const project = useProjectContext()
  169. return (
  170. <Row className="public-access-level">
  171. <Col xs={12} className="access-token-display-area">
  172. <div className="access-token-wrapper">
  173. <strong>
  174. <Trans i18nKey="anyone_with_link_can_view" />
  175. </strong>
  176. <AccessToken
  177. token={project?.tokens?.readOnly}
  178. path="/read/"
  179. tooltipId="tooltip-copy-link-ro"
  180. />
  181. </div>
  182. </Col>
  183. </Row>
  184. )
  185. }
  186. function AccessToken({ token, path, tooltipId }) {
  187. if (!token) {
  188. return (
  189. <pre className="access-token">
  190. <span>
  191. <Trans i18nKey="loading" />…
  192. </span>
  193. </pre>
  194. )
  195. }
  196. const link = `${window.location.origin}${path}${token}`
  197. return (
  198. <pre className="access-token">
  199. <span>{link}</span>
  200. <CopyLink link={link} tooltipId={tooltipId} />
  201. </pre>
  202. )
  203. }
  204. AccessToken.propTypes = {
  205. token: PropTypes.string,
  206. tooltipId: PropTypes.string.isRequired,
  207. path: PropTypes.string.isRequired
  208. }
  209. function LinkSharingInfo() {
  210. return (
  211. <OverlayTrigger
  212. placement="top"
  213. overlay={
  214. <Tooltip id="tooltip-link-sharing-info">
  215. <Trans i18nKey="learn_more_about_link_sharing" />
  216. </Tooltip>
  217. }
  218. >
  219. <a
  220. href="/learn/how-to/What_is_Link_Sharing%3F"
  221. target="_blank"
  222. rel="noopener"
  223. >
  224. <Icon type="question-circle" />
  225. </a>
  226. </OverlayTrigger>
  227. )
  228. }