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.

270 lines
9.7KB

  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 WBBrowseDialogProjectList from 'wb-browse-dialog-project-list';
  9. import WBBrowseDialogCollectionList from 'wb-browse-dialog-collection-list';
  10. import WBBrowseDialogCollectionContent from 'wb-browse-dialog-collection-content';
  11. import WBBrowseDialogUserList from 'wb-browse-dialog-user-list';
  12. import linkState from 'linkstate';
  13. import { Router } from 'preact-router';
  14. import { createHashHistory } from 'history';
  15. //
  16. // internal URLs look like this
  17. //
  18. // /browse-dialog/browse/( owner-uuid )/( project-page )/( text-search )
  19. // /browse-dialog/users//( users-page )/( text-search )
  20. // /browse-dialog/shared-with-me//( project-page )/( collection-page )/( text-search )
  21. // /browse-dialog/content/( collection-uuid )//( content-page )/( text-search )/( collection-path )
  22. //
  23. // general pattern therefore:
  24. // /browse-dialog/( mode )/( uuid )/( top-page )/( bottom-page )/( text-search )
  25. //
  26. // props:
  27. // selectMany: Boolean
  28. // selectWhat: [ 'file', 'directory', 'owner' ]
  29. //
  30. // state:
  31. // selected: Array of UUID
  32. // textSearch: string
  33. // textSearchInput: string
  34. //
  35. class WBBrowseDialog extends Component {
  36. constructor(...args) {
  37. super(...args);
  38. this.state.history = [];
  39. this.state.selected = {};
  40. this.state.selectedOrder = [];
  41. const { currentUser } = this.props.app.state;
  42. this.state.currentUrl = '/browse-dialog/browse/' + currentUser.uuid;
  43. this.state.uuid = currentUser.uuid;
  44. this.state.mode = 'browse';
  45. this.state.topPage = 0;
  46. this.state.bottomPage = 0;
  47. this.state.collectionPath = '';
  48. this.state.textSearch = '';
  49. this.state.id = ('id' in this.props) ? this.props.id : uuid.v4();
  50. this.state.accept = () => {};
  51. this.modalRef = createRef();
  52. }
  53. navigateBack() {
  54. if (this.state.history.length === 0)
  55. return;
  56. const url = this.state.history.pop();
  57. this.navigate(url, false);
  58. }
  59. navigate(url, useHistory=true, stateUpdate={}) {
  60. if (typeof(url) === 'object') {
  61. url = ['', 'browse-dialog',
  62. 'mode' in url ? url.mode : this.state.mode,
  63. 'uuid' in url ? url.uuid : this.state.uuid,
  64. 'topPage' in url ? url.topPage : this.state.topPage,
  65. 'bottomPage' in url ? url.bottomPage : this.state.bottomPage,
  66. 'textSearch' in url ? url.textSearch : this.state.textSearch,
  67. encodeURIComponent('collectionPath' in url ? url.collectionPath : this.state.collectionPath)
  68. ].join('/');
  69. }
  70. url = url.substr(url.indexOf('/browse-dialog/'));
  71. if (useHistory)
  72. this.state.history.push(this.state.currentUrl);
  73. let [ _1, _2, mode, uuid, topPage, bottomPage, textSearch, collectionPath ] = url.split('/');
  74. topPage = parseInt(topPage, 10) || 0;
  75. bottomPage = parseInt(bottomPage, 10) || 0;
  76. collectionPath = decodeURIComponent(collectionPath || '');
  77. this.setState(Object.assign({
  78. 'currentUrl': url,
  79. mode, uuid, topPage, bottomPage, textSearch, collectionPath
  80. }, stateUpdate));
  81. }
  82. select(uuid) {
  83. let { selected, selectedOrder } = this.state;
  84. if (uuid in selected) {
  85. const n = selectedOrder.indexOf(uuid);
  86. selectedOrder = selected.splice(n, n + 1);
  87. }
  88. selected[uuid] = true;
  89. selectedOrder.push(uuid);
  90. /* this.setState({
  91. selected, selectedOrder
  92. }); */
  93. }
  94. deselect(uuid) {
  95. let { selected, selectedOrder } = this.state;
  96. if (!(uuid in selected))
  97. return;
  98. const n = selectedOrder.indexOf(uuid);
  99. selectedOrder = selected.splice(n, n + 1);
  100. delete selected[uuid];
  101. /* this.setState({
  102. selected, selectedOrder
  103. }); */
  104. }
  105. resetSelection() {
  106. this.setState({
  107. 'selected': {},
  108. 'selectedOrder': []
  109. });
  110. }
  111. makeSelectionCell(uuid) {
  112. const { selected, accept, selectMany, id } = this.state;
  113. return selectMany ? (
  114. <div>
  115. <input type="checkbox" checked={ (uuid in selected) }
  116. onChange={ e => {
  117. if (e.target.checked)
  118. this.select(uuid);
  119. else
  120. this.deselect(uuid);
  121. } } /> { '\u00A0' }
  122. </div>
  123. ) : (
  124. <button class="btn btn-outline-primary" title="Use"
  125. onclick={ () => {
  126. $('#' + id).modal('hide');
  127. accept(uuid);
  128. } }>
  129. <i class="fas fa-hand-pointer"></i>
  130. </button>
  131. );
  132. }
  133. show(selectWhat, selectMany, accept=(() => {})) {
  134. const { app } = this.props;
  135. const { currentUser } = app.state;
  136. this.navigate('/browse-dialog/browse/' + currentUser.uuid, false,
  137. { selectWhat, selectMany, accept, history: [],
  138. selected: {}, selectedOrder: [] });
  139. $('#' + this.state.id).modal();
  140. }
  141. componentWillUnmount() {
  142. $(this.modalRef.current).modal('hide');
  143. }
  144. render({ app },
  145. { history, currentUrl, mode, uuid,
  146. topPage, bottomPage, textSearch,
  147. collectionPath, id, accept, selectedOrder,
  148. selectMany, selectWhat }) {
  149. return (
  150. <div class="modal" id={ id } tabindex="-1" role="dialog" ref={ this.modalRef }>
  151. <div class="modal-dialog modal-lg" role="document">
  152. <div class="modal-content">
  153. <div class="modal-header">
  154. { false ? <h5 class="modal-title">Browse</h5> : null }
  155. <div>{ currentUrl }</div>
  156. <button type="button" class="close" data-dismiss="modal" aria-label="Close">
  157. <span aria-hidden="true">&times;</span>
  158. </button>
  159. </div>
  160. <div class="modal-body">
  161. <div class="mb-3">
  162. <a href="#" class={ 'btn btn-outline-secondary mr-2' +
  163. (history.length === 0 ? ' disabled': '') }
  164. onclick={ e => { e.preventDefault();
  165. this.navigateBack(); } }>Back</a>
  166. <a href="#" class="btn btn-outline-primary mr-2"
  167. onclick={ e => { e.preventDefault();
  168. this.navigate('/browse-dialog/browse/' + app.state.currentUser.uuid); } }>Home</a>
  169. <a href="#" class="btn btn-outline-primary mr-2"
  170. onclick={ e => { e.preventDefault();
  171. this.navigate('/browse-dialog/browse'); } }>All Projects</a>
  172. <a href="#" class="btn btn-outline-primary mr-2"
  173. onclick={ e => { e.preventDefault();
  174. this.navigate('/browse-dialog/users'); } }>All Users</a>
  175. <a href="#" class="btn btn-outline-primary mr-2"
  176. onclick={ e => { e.preventDefault();
  177. this.navigate('/browse-dialog/shared-with-me'); } }>Shared with Me</a>
  178. </div>
  179. <div class="input-group mb-3">
  180. <input type="text" class="form-control" placeholder="Search"
  181. aria-label="Search" value={ textSearch }
  182. onChange={ e => this.navigate({
  183. 'textSearch': e.target.value,
  184. 'topPage': 0,
  185. 'bottomPage': 0}) } />
  186. <div class="input-group-append">
  187. <button class="btn btn-outline-primary" type="button">Search</button>
  188. </div>
  189. </div>
  190. { (mode === 'browse' || mode === 'shared-with-me') ? (
  191. <div>
  192. <h5>Projects</h5>
  193. <WBBrowseDialogProjectList app={ app }
  194. navigate={ url => this.navigate(url) }
  195. mode={ mode } ownerUuid={ uuid }
  196. page={ topPage } textSearch={ textSearch }
  197. selectWhat={ selectWhat }
  198. makeSelectionCell={ uuid => this.makeSelectionCell(uuid) } />
  199. </div>
  200. ) : null }
  201. { (mode === 'users') ? (
  202. <WBBrowseDialogUserList app={ app }
  203. navigate={ url => this.navigate(url) }
  204. page={ topPage } textSearch={ textSearch }/>
  205. ) : null }
  206. { (mode === 'content') ? (
  207. <div>
  208. <h5>Content</h5>
  209. <WBBrowseDialogCollectionContent app={ app }
  210. collectionUuid={ uuid } collectionPath={ collectionPath }
  211. page={ bottomPage } selectWhat={ selectWhat }
  212. makeSelectionCell={ uuid => this.makeSelectionCell(uuid) }
  213. navigate={ url => this.navigate(url) }
  214. textSearch={ textSearch } />
  215. </div>
  216. ) : (selectWhat !== 'owner' && mode === 'browse') ? (
  217. <div>
  218. <h5>Collections</h5>
  219. <WBBrowseDialogCollectionList app={ app }
  220. page={ bottomPage } textSearch={ textSearch }
  221. navigate={ url => this.navigate(url) }
  222. ownerUuid={ uuid } selectWhat={ selectWhat }
  223. makeSelectionCell={ uuid => this.makeSelectionCell(uuid) } />
  224. </div>
  225. ) : null }
  226. </div>
  227. <div class="modal-footer">
  228. { selectMany ? (
  229. <button type="button" class="btn btn-primary"
  230. onclick={ e => { e.preventDefault(); accept(selectedOrder); $('#' + id).modal('hide'); } }>Accept</button>
  231. ) : null }
  232. <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
  233. </div>
  234. </div>
  235. </div>
  236. </div>
  237. );
  238. }
  239. }
  240. WBBrowseDialog.defaultProps = {
  241. 'accept': () => {}
  242. };
  243. export default WBBrowseDialog;