IF YOU WOULD LIKE TO GET AN ACCOUNT, please write an email to s dot adaszewski at gmail dot com. User accounts are meant only to report issues and/or generate pull requests. This is a purpose-specific Git hosting for ADARED projects. Thank you for your understanding!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

256 lines
9.3KB

  1. //
  2. // Copyright (C) Stanislaw Adaszewski, 2020
  3. // Contact: s.adaszewski@gmail.com
  4. // Website: https://adared.ch/wba
  5. // License: GNU Affero General Public License, Version 3
  6. //
  7. import { h, Component, createRef } from 'preact';
  8. import { route } from 'preact-router';
  9. import WBNavbarCommon from 'wb-navbar-common';
  10. import WBProjectListing from 'wb-project-listing';
  11. import WBInlineSearch from 'wb-inline-search';
  12. import WBArvadosCrumbs from 'wb-arvados-crumbs';
  13. import WBTabs from 'wb-tabs';
  14. import WBProcessListing from 'wb-process-listing';
  15. import WBCollectionListing from 'wb-collection-listing';
  16. import WBWorkflowListing from 'wb-workflow-listing';
  17. import WBRenameDialog from 'wb-rename-dialog';
  18. import WBDeleteDialog from 'wb-delete-dialog';
  19. import WBNewProjectDialog from 'wb-new-project-dialog';
  20. import WBEditDescriptionDialog from 'wb-edit-description-dialog';
  21. import wbMoveObject from 'wb-move-object';
  22. import wbCopyCollection from 'wb-copy-collection';
  23. import arvadosTypeName from 'arvados-type-name';
  24. class WBBrowseProjectTabs extends Component {
  25. render({ ownerUuid, selected, newProjectDialogRef, projectListingRef,
  26. moveHere, copyHere }) {
  27. return (
  28. <WBTabs tabs={ [
  29. { 'name': 'Projects', 'isActive': true },
  30. ownerUuid ? { 'name': ( <span><i class="fas fa-plus-square text-success"></i> New Project</span> ),
  31. 'onClick': () => newProjectDialogRef.current.show(ownerUuid,
  32. () => projectListingRef.current.fetchItems() ) } : null,
  33. ( ownerUuid && Object.keys(selected).length > 0 ) ?
  34. { 'name': ( <span><i class="fas fa-compress-arrows-alt text-warning"></i> Move Here</span> ),
  35. 'onClick': moveHere } : null,
  36. ( ownerUuid && (uuids => uuids.length > 0 && uuids.length ===
  37. uuids.map(arvadosTypeName).filter(a => (a === 'collection')).length )(Object.keys(selected)) ) ?
  38. { 'name': ( <span><i class="fas fa-file-import text-warning"></i> Copy Here</span> ),
  39. 'onClick': copyHere } : null
  40. ] } />
  41. );
  42. }
  43. }
  44. class WBBrowse extends Component {
  45. constructor(...args) {
  46. super(...args);
  47. this.renameDialogRef = createRef();
  48. this.deleteDialogRef = createRef();
  49. this.newProjectDialogRef = createRef();
  50. this.projectListingRef = createRef();
  51. this.projectTabsRef = createRef();
  52. this.editDescriptionDialogRef = createRef();
  53. this.state.selected = {};
  54. }
  55. getUrl(params) {
  56. const mode = ('mode' in params ? params.mode : this.props.mode);
  57. if (mode === 'shared-with-me')
  58. return '/shared-with-me/' +
  59. ('activePage' in params ? params.activePage : (this.props.activePage || '')) + '/' +
  60. ('textSearch' in params ? params.textSearch : (this.props.textSearch || ''));
  61. let res = '/browse/' +
  62. ('ownerUuid' in params ? params.ownerUuid : (this.props.ownerUuid || '')) + '/' +
  63. ('activePage' in params ? params.activePage : (this.props.activePage || '')) + '/' +
  64. ('objTypeTab' in params ? params.objTypeTab : (this.props.objTypeTab || '')) + '/' +
  65. ('collectionPage' in params ? params.collectionPage : (this.props.collectionPage || '')) + '/' +
  66. ('processPage' in params ? params.processPage : (this.props.processPage || '')) + '/' +
  67. ('workflowPage' in params ? params.workflowPage : (this.props.workflowPage || '')) + '/' +
  68. encodeURIComponent('textSearch' in params ? params.textSearch : (this.props.textSearch || ''));
  69. return res;
  70. }
  71. route(params) {
  72. route(this.getUrl(params));
  73. }
  74. renameDialog(item, callback) {
  75. // throw Error('Not implemented');
  76. this.renameDialogRef.current.show(item, callback);
  77. }
  78. renderRenameLink(item, callback) {
  79. return (
  80. <a href="#" title="Rename" onclick={ e => { e.preventDefault(); this.renameDialog(item, callback); } }>
  81. <i class="fas fa-edit text-secondary"></i>
  82. </a>
  83. );
  84. }
  85. renderEditDescription(item, callback) {
  86. return (
  87. <a href="#" title="Edit description" onclick={ e => { e.preventDefault();
  88. this.editDescriptionDialogRef.current.show(item, callback); } }>
  89. <i class="fas fa-edit text-secondary"></i>
  90. </a>
  91. );
  92. }
  93. renderDeleteButton(item, callback) {
  94. return (
  95. <button class="btn btn-outline-danger m-1" title="Delete"
  96. onclick={ () => this.deleteDialogRef.current.show(item, callback) }>
  97. <i class="fas fa-trash"></i>
  98. </button>
  99. );
  100. }
  101. renderSelectionCell(item) {
  102. const { selected } = this.state;
  103. const { uuid } = item;
  104. return (
  105. <div>
  106. <input type="checkbox" checked={ (uuid in selected) }
  107. onChange={ e => {
  108. if (e.target.checked)
  109. selected[uuid] = true;
  110. else
  111. delete selected[uuid];
  112. this.projectTabsRef.current.setState({});
  113. } } /> { '\u00A0' }
  114. </div>
  115. );
  116. }
  117. renderSharingButton(item) {
  118. return (
  119. <a class="btn btn-outline-success m-1" title="Share"
  120. href={ '/sharing/' + item.uuid }>
  121. <i class="fas fa-share-alt"></i>
  122. </a>
  123. );
  124. }
  125. moveOrCopyOp(op) {
  126. const { ownerUuid, app } = this.props;
  127. const { selected } = this.state;
  128. const { arvHost, arvToken } = app.state;
  129. let prom = new Promise(accept => accept());
  130. const uuids = Object.keys(selected);
  131. for (let i = 0; i < uuids.length; i++) {
  132. prom = prom.then(() => op(arvHost, arvToken, uuids[i], ownerUuid));
  133. prom = prom.then(() => ( delete selected[uuids[i]] ));
  134. prom = prom.catch(() => {});
  135. }
  136. prom = prom.then(() => this.setState({}));
  137. }
  138. moveHere() {
  139. this.moveOrCopyOp(wbMoveObject);
  140. }
  141. copyHere() {
  142. this.moveOrCopyOp(wbCopyCollection);
  143. }
  144. render({ mode, ownerUuid, activePage, app,
  145. objTypeTab, collectionPage, processPage, workflowPage,
  146. textSearch }, { selected }) {
  147. const commonProps = {
  148. renderRenameLink: (it, cb) => this.renderRenameLink(it, cb),
  149. renderEditDescription: (it, cb) => this.renderEditDescription(it, cb),
  150. renderDeleteButton: (it, cb) => this.renderDeleteButton(it, cb),
  151. renderSelectionCell: it => this.renderSelectionCell(it),
  152. renderSharingButton: it => this.renderSharingButton(it),
  153. textSearch,
  154. app,
  155. appState: app.state,
  156. arvHost: app.state.arvHost,
  157. arvToken: app.state.arvToken,
  158. ownerUuid
  159. };
  160. const { currentUser } = app.state;
  161. const noDefaultTab = (!ownerUuid || ownerUuid === currentUser.uuid);
  162. return (
  163. <div>
  164. <WBRenameDialog app={ app } ref={ this.renameDialogRef } />
  165. <WBDeleteDialog app={ app } ref={ this.deleteDialogRef } />
  166. <WBNewProjectDialog app={ app } ref={ this.newProjectDialogRef } />
  167. <WBEditDescriptionDialog app={ app } ref={ this.editDescriptionDialogRef } />
  168. <WBNavbarCommon app={ app }
  169. activeItem={ mode === 'shared-with-me' ? 'shared-with-me' :
  170. (!ownerUuid) ? 'all-projects' :
  171. (ownerUuid === app.state.currentUser.uuid) ? 'home' : null }
  172. textSearch={ textSearch }
  173. textSearchNavigate={ textSearch => route(this.getUrl({ textSearch,
  174. activePage: 0, collectionPage: 0, processPage: 0, workflowPage: 0 })) } />
  175. <WBArvadosCrumbs mode={ mode } uuid={ ownerUuid } app={ app } />
  176. <WBBrowseProjectTabs ref={ this.projectTabsRef } ownerUuid={ ownerUuid }
  177. selected={ selected } newProjectDialogRef={ this.newProjectDialogRef }
  178. projectListingRef={ this.projectListingRef } moveHere={ () => this.moveHere() }
  179. copyHere={ () => this.copyHere() } />
  180. <WBProjectListing ref={ this.projectListingRef }
  181. mode={ mode }
  182. itemsPerPage="5"
  183. activePage={ Number(activePage || 0) }
  184. getPageUrl={ i => this.getUrl({ 'activePage': i }) }
  185. { ...commonProps } />
  186. { (mode !== 'browse') ? null : (
  187. <WBTabs tabs={ [
  188. { 'id': 'collection', 'name': 'Collections', 'isActive': ((!objTypeTab && !noDefaultTab) || objTypeTab === 'collection') },
  189. { 'id': 'process', 'name': 'Processes', 'isActive': (objTypeTab === 'process') },
  190. { 'id': 'workflow', 'name': 'Workflows', 'isActive': (objTypeTab === 'workflow') } ] }
  191. onTabChanged={ tab => this.route({ 'objTypeTab': tab['id'] }) } />
  192. ) }
  193. {
  194. (mode !== 'browse') ? null :
  195. ((!objTypeTab && !noDefaultTab) || objTypeTab === 'collection') ? (
  196. <WBCollectionListing
  197. itemsPerPage="20"
  198. activePage={ Number(collectionPage || 0) }
  199. getPageUrl={ i => this.getUrl({ 'collectionPage': i }) }
  200. { ...commonProps } />
  201. ) : (objTypeTab === 'process') ? (
  202. <WBProcessListing
  203. itemsPerPage="20"
  204. activePage={ Number(processPage || 0) }
  205. onPageChanged={ i => this.route({ 'processPage': i }) }
  206. { ...commonProps } />
  207. ) : (objTypeTab === 'workflow') ? (
  208. <WBWorkflowListing
  209. itemsPerPage="20"
  210. page={ Number(workflowPage || 0) }
  211. getPageUrl={ i => this.getUrl({ 'workflowPage': i }) }
  212. { ...commonProps } />
  213. ) : null
  214. }
  215. </div>
  216. );
  217. }
  218. }
  219. export default WBBrowse;