PageTimeline.jsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import { withTranslation } from 'react-i18next';
  4. import * as entities from 'entities';
  5. import AppContainer from '../services/AppContainer';
  6. import { createSubscribedElement } from './UnstatedUtils';
  7. import RevisionLoader from './Page/RevisionLoader';
  8. class PageTimeline extends React.Component {
  9. constructor(props) {
  10. super(props);
  11. const { appContainer } = this.props;
  12. this.state = {
  13. isEnabled: appContainer.getConfig().isEnabledTimeline,
  14. isInitialized: false,
  15. // TODO: remove after when timeline is implemented with React and inject data with props
  16. pages: this.props.pages,
  17. };
  18. }
  19. componentWillMount() {
  20. if (!this.state.isEnabled) {
  21. return;
  22. }
  23. const { appContainer } = this.props;
  24. // initialize GrowiRenderer
  25. this.growiRenderer = appContainer.getRenderer('timeline');
  26. this.initBsTab();
  27. }
  28. /**
  29. * initialize Bootstrap Tab event for 'shown.bs.tab'
  30. * TODO: remove this method after implement with React
  31. */
  32. initBsTab() {
  33. $('a[data-toggle="tab"][href="#view-timeline"]').on('shown.bs.tab', () => {
  34. if (this.state.isInitialized) {
  35. return;
  36. }
  37. const pageIdsElm = document.getElementById('page-timeline-data');
  38. if (pageIdsElm == null || pageIdsElm.text.length === 0) {
  39. return;
  40. }
  41. const pages = this.extractDataFromDom();
  42. this.setState({
  43. isInitialized: true,
  44. pages,
  45. });
  46. });
  47. }
  48. /**
  49. * extract page data from DOM
  50. * TODO: remove this method after implement with React
  51. */
  52. extractDataFromDom() {
  53. const pageIdsElm = document.getElementById('page-timeline-data');
  54. if (pageIdsElm == null || pageIdsElm.text.length === 0) {
  55. return null;
  56. }
  57. let pages = JSON.parse(pageIdsElm.text);
  58. // decode path
  59. pages = pages.map((page) => {
  60. page.path = decodeURIComponent(entities.decodeHTML(page.path));
  61. return page;
  62. });
  63. return pages;
  64. }
  65. render() {
  66. if (!this.state.isEnabled) {
  67. return <React.Fragment></React.Fragment>;
  68. }
  69. const { pages } = this.state;
  70. if (pages == null) {
  71. return <React.Fragment></React.Fragment>;
  72. }
  73. return pages.map((page) => {
  74. return (
  75. <div className="timeline-body" key={`key-${page.id}`}>
  76. <div className="card card-timeline">
  77. <div className="card-header"><a href={page.path}>{page.path}</a></div>
  78. <div className="card-body">
  79. <RevisionLoader
  80. lazy
  81. growiRenderer={this.growiRenderer}
  82. pageId={page.id}
  83. revisionId={page.revision}
  84. />
  85. </div>
  86. </div>
  87. </div>
  88. );
  89. });
  90. }
  91. }
  92. PageTimeline.propTypes = {
  93. t: PropTypes.func.isRequired, // i18next
  94. appContainer: PropTypes.instanceOf(AppContainer).isRequired,
  95. pages: PropTypes.arrayOf(PropTypes.object),
  96. };
  97. /**
  98. * Wrapper component for using unstated
  99. */
  100. const PageTimelineWrapper = (props) => {
  101. return createSubscribedElement(PageTimeline, props, [AppContainer]);
  102. };
  103. export default withTranslation()(PageTimelineWrapper);