WorkspaceContent.jsx 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. import React from 'react'
  2. import { connect } from 'react-redux'
  3. import { withRouter, Route } from 'react-router-dom'
  4. import appFactory from '../appFactory.js'
  5. import { PAGE, ROLE, findIdRoleUserWorkspace } from '../helper.js'
  6. import Folder from '../component/Workspace/Folder.jsx'
  7. import ContentItem from '../component/Workspace/ContentItem.jsx'
  8. import ContentItemHeader from '../component/Workspace/ContentItemHeader.jsx'
  9. import DropdownCreateButton from '../component/common/Input/DropdownCreateButton.jsx'
  10. import OpenContentApp from '../component/Workspace/OpenContentApp.jsx'
  11. import OpenCreateContentApp from '../component/Workspace/OpenCreateContentApp.jsx'
  12. import {
  13. PageWrapper,
  14. PageTitle,
  15. PageContent
  16. } from 'tracim_frontend_lib'
  17. import {
  18. getWorkspaceContentList,
  19. getWorkspaceMemberList,
  20. getFolderContent,
  21. putWorkspaceContentArchived,
  22. putWorkspaceContentDeleted
  23. } from '../action-creator.async.js'
  24. import {
  25. newFlashMessage,
  26. setWorkspaceContentList,
  27. setWorkspaceContentArchived,
  28. setWorkspaceContentDeleted,
  29. setWorkspaceMemberList
  30. } from '../action-creator.sync.js'
  31. const qs = require('query-string')
  32. class WorkspaceContent extends React.Component {
  33. constructor (props) {
  34. super(props)
  35. this.state = {
  36. workspaceIdInUrl: props.match.params.idws ? parseInt(props.match.params.idws) : null, // this is used to avoid handling the parseInt every time
  37. appOpenedType: false,
  38. contentLoaded: false
  39. }
  40. document.addEventListener('appCustomEvent', this.customEventReducer)
  41. }
  42. customEventReducer = async ({ detail: { type, data } }) => {
  43. switch (type) {
  44. case 'refreshContentList':
  45. console.log('%c<WorkspaceContent> Custom event', 'color: #28a745', type, data)
  46. this.loadContentList(this.state.workspaceIdInUrl)
  47. break
  48. case 'openContentUrl':
  49. console.log('%c<WorkspaceContent> Custom event', 'color: #28a745', type, data)
  50. this.props.history.push(PAGE.WORKSPACE.CONTENT(data.idWorkspace, data.contentType, data.idContent))
  51. break
  52. case 'appClosed':
  53. case 'hide_popupCreateContent':
  54. console.log('%c<WorkspaceContent> Custom event', 'color: #28a745', type, data, this.state.workspaceIdInUrl)
  55. this.props.history.push(PAGE.WORKSPACE.CONTENT_LIST(this.state.workspaceIdInUrl))
  56. this.setState({appOpenedType: false})
  57. break
  58. }
  59. }
  60. componentDidMount () {
  61. const { workspaceList, match } = this.props
  62. console.log('%c<WorkspaceContent> componentDidMount', 'color: #c17838')
  63. let wsToLoad = null
  64. if (match.params.idws === undefined) {
  65. if (workspaceList.length > 0) wsToLoad = workspaceList[0].id
  66. else return
  67. } else wsToLoad = match.params.idws
  68. this.loadContentList(wsToLoad)
  69. }
  70. async componentDidUpdate (prevProps, prevState) {
  71. console.log('%c<WorkspaceContent> componentDidUpdate', 'color: #c17838')
  72. if (this.state.workspaceIdInUrl === null) return
  73. const idWorkspace = parseInt(this.props.match.params.idws)
  74. if (isNaN(idWorkspace)) return
  75. const prevFilter = qs.parse(prevProps.location.search).type
  76. const currentFilter = qs.parse(this.props.location.search).type
  77. if (prevState.workspaceIdInUrl !== idWorkspace || prevFilter !== currentFilter) {
  78. this.setState({workspaceIdInUrl: idWorkspace})
  79. this.loadContentList(idWorkspace)
  80. }
  81. // if (user.user_id !== -1 && prevProps.user.id !== user.id) dispatch(getWorkspaceList(user.user_id, idWorkspace))
  82. }
  83. componentWillUnmount () {
  84. this.props.dispatchCustomEvent('unmount_app')
  85. document.removeEventListener('appCustomEvent', this.customEventReducer)
  86. }
  87. loadContentList = async idWorkspace => {
  88. const { user, dispatch } = this.props
  89. const wsContent = await dispatch(getWorkspaceContentList(user, idWorkspace, 0))
  90. const wsMember = await dispatch(getWorkspaceMemberList(user, idWorkspace))
  91. if (wsContent.status === 200) dispatch(setWorkspaceContentList(wsContent.json))
  92. else dispatch(newFlashMessage('Error while loading workspace', 'danger'))
  93. if (wsMember.status === 200) dispatch(setWorkspaceMemberList(wsMember.json))
  94. else dispatch(newFlashMessage('Error while loading members list', 'warning'))
  95. this.setState({contentLoaded: true})
  96. }
  97. handleClickContentItem = content => {
  98. console.log('%c<WorkspaceContent> content clicked', 'color: #c17838', content)
  99. this.props.history.push(PAGE.WORKSPACE.CONTENT(content.idWorkspace, content.type, content.id))
  100. }
  101. handleClickEditContentItem = (e, content) => {
  102. e.stopPropagation()
  103. console.log('%c<WorkspaceContent> edit nyi', 'color: #c17838', content)
  104. }
  105. handleClickMoveContentItem = (e, content) => {
  106. e.stopPropagation()
  107. console.log('%c<WorkspaceContent> move nyi', 'color: #c17838', content)
  108. }
  109. handleClickDownloadContentItem = (e, content) => {
  110. e.stopPropagation()
  111. console.log('%c<WorkspaceContent> download nyi', 'color: #c17838', content)
  112. }
  113. handleClickArchiveContentItem = async (e, content) => {
  114. const { props, state } = this
  115. e.stopPropagation()
  116. const fetchPutContentArchived = await props.dispatch(putWorkspaceContentArchived(props.user, content.idWorkspace, content.id))
  117. switch (fetchPutContentArchived.status) {
  118. case 204:
  119. props.dispatch(setWorkspaceContentArchived(content.idWorkspace, content.id))
  120. this.loadContentList(state.workspaceIdInUrl)
  121. break
  122. default: props.dispatch(newFlashMessage(props.t('Error while archiving document')))
  123. }
  124. }
  125. handleClickDeleteContentItem = async (e, content) => {
  126. const { props, state } = this
  127. e.stopPropagation()
  128. const fetchPutContentDeleted = await props.dispatch(putWorkspaceContentDeleted(props.user, content.idWorkspace, content.id))
  129. switch (fetchPutContentDeleted.status) {
  130. case 204:
  131. props.dispatch(setWorkspaceContentDeleted(content.idWorkspace, content.id))
  132. this.loadContentList(state.workspaceIdInUrl)
  133. break
  134. default: props.dispatch(newFlashMessage(props.t('Error while deleting document')))
  135. }
  136. }
  137. handleClickFolder = folderId => {
  138. this.props.dispatch(getFolderContent(this.props.workspace.id, folderId))
  139. }
  140. handleClickCreateContent = (e, idFolder, contentType) => {
  141. e.stopPropagation()
  142. this.props.history.push(`${PAGE.WORKSPACE.NEW(this.state.workspaceIdInUrl, contentType)}?parent_id=${idFolder}`)
  143. }
  144. handleUpdateAppOpenedType = openedAppType => this.setState({appOpenedType: openedAppType})
  145. render () {
  146. const { user, currentWorkspace, workspaceContentList, contentType } = this.props
  147. const { state } = this
  148. const filterWorkspaceContent = (contentList, filter) => {
  149. return filter.length === 0
  150. ? contentList
  151. : contentList.filter(c => c.type === 'folder' || filter.includes(c.type)) // keep unfiltered files and folders
  152. // @FIXME we need to filter subfolder too, but right now, we dont handle subfolder
  153. // .map(c => c.type !== 'folder' ? c : {...c, content: filterWorkspaceContent(c.content, filter)}) // recursively filter folder content
  154. }
  155. // .filter(c => c.type !== 'folder' || c.content.length > 0) // remove empty folder => 2018/05/21 - since we load only one lvl of content, don't remove empty
  156. const urlFilter = qs.parse(this.props.location.search).type
  157. const filteredWorkspaceContentList = workspaceContentList.length > 0
  158. ? filterWorkspaceContent(workspaceContentList, urlFilter ? [urlFilter] : [])
  159. : []
  160. const idRoleUserWorkspace = findIdRoleUserWorkspace(user.user_id, currentWorkspace.memberList, ROLE)
  161. return (
  162. <div className='WorkspaceContent' style={{width: '100%'}}>
  163. {state.contentLoaded &&
  164. <OpenContentApp
  165. // automatically open the app for the idContent in url
  166. idWorkspace={this.state.workspaceIdInUrl}
  167. appOpenedType={this.state.appOpenedType}
  168. updateAppOpenedType={this.handleUpdateAppOpenedType}
  169. />
  170. }
  171. {state.contentLoaded &&
  172. <Route path={PAGE.WORKSPACE.NEW(':idws', ':type')} component={() =>
  173. <OpenCreateContentApp
  174. // automatically open the popup create content of the app in url
  175. idWorkspace={this.state.workspaceIdInUrl}
  176. appOpenedType={this.state.appOpenedType}
  177. />
  178. } />
  179. }
  180. <PageWrapper customeClass='workspace'>
  181. <PageTitle
  182. parentClass='workspace__header'
  183. customClass='justify-content-between'
  184. title='Liste des Contenus'
  185. subtitle={workspaceContentList.label ? workspaceContentList.label : ''}
  186. >
  187. {idRoleUserWorkspace >= 2 &&
  188. <DropdownCreateButton
  189. parentClass='workspace__header__btnaddcontent'
  190. idFolder={null} // null because it is workspace root content
  191. onClickCreateContent={this.handleClickCreateContent}
  192. availableApp={contentType.filter(ct => ct.slug !== 'comment')} // @FIXME: Côme - 2018/08/21 - should use props.appList
  193. />
  194. }
  195. </PageTitle>
  196. <PageContent parentClass='workspace__content'>
  197. <div className='workspace__content__fileandfolder folder__content active'>
  198. <ContentItemHeader />
  199. { filteredWorkspaceContentList.map((c, i) => c.type === 'folder'
  200. ? (
  201. <Folder
  202. availableApp={contentType.filter(ct => ct.slug !== 'comment')} // @FIXME: Côme - 2018/08/21 - should use props.appList
  203. folderData={c}
  204. onClickItem={this.handleClickContentItem}
  205. idRoleUserWorkspace={idRoleUserWorkspace}
  206. onClickExtendedAction={{
  207. edit: this.handleClickEditContentItem,
  208. move: this.handleClickMoveContentItem,
  209. download: this.handleClickDownloadContentItem,
  210. archive: this.handleClickArchiveContentItem,
  211. delete: this.handleClickDeleteContentItem
  212. }}
  213. onClickFolder={this.handleClickFolder}
  214. onClickCreateContent={this.handleClickCreateContent}
  215. isLast={i === filteredWorkspaceContentList.length - 1}
  216. key={c.id}
  217. />
  218. )
  219. : (
  220. <ContentItem
  221. label={c.label}
  222. type={c.type}
  223. faIcon={contentType.length ? contentType.find(a => a.slug === c.type).faIcon : ''}
  224. statusSlug={c.statusSlug}
  225. contentType={contentType.length ? contentType.find(ct => ct.slug === c.type) : null}
  226. onClickItem={() => this.handleClickContentItem(c)}
  227. idRoleUserWorkspace={idRoleUserWorkspace}
  228. onClickExtendedAction={{
  229. edit: e => this.handleClickEditContentItem(e, c),
  230. move: e => this.handleClickMoveContentItem(e, c),
  231. download: e => this.handleClickDownloadContentItem(e, c),
  232. archive: e => this.handleClickArchiveContentItem(e, c),
  233. delete: e => this.handleClickDeleteContentItem(e, c)
  234. }}
  235. onClickCreateContent={this.handleClickCreateContent}
  236. isLast={i === filteredWorkspaceContentList.length - 1}
  237. key={c.id}
  238. />
  239. )
  240. )}
  241. </div>
  242. {idRoleUserWorkspace >= 2 &&
  243. <DropdownCreateButton
  244. customClass='workspace__content__button'
  245. idFolder={null}
  246. onClickCreateContent={this.handleClickCreateContent}
  247. availableApp={contentType.filter(ct => ct.slug !== 'comment')} // @FIXME: Côme - 2018/08/21 - should use props.appList
  248. />
  249. }
  250. </PageContent>
  251. </PageWrapper>
  252. </div>
  253. )
  254. }
  255. }
  256. const mapStateToProps = ({ user, currentWorkspace, workspaceContentList, workspaceList, contentType }) => ({
  257. user, currentWorkspace, workspaceContentList, workspaceList, contentType
  258. })
  259. export default withRouter(connect(mapStateToProps)(appFactory(WorkspaceContent)))