WorkspaceContent.jsx 10KB

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