|
@@ -10,6 +10,8 @@ import loggerFactory from '~/utils/logger';
|
|
|
|
|
|
|
|
import LinkedPagePath from '~/models/linked-page-path';
|
|
import LinkedPagePath from '~/models/linked-page-path';
|
|
|
|
|
|
|
|
|
|
+import FootstampIcon from '../FootstampIcon';
|
|
|
|
|
+
|
|
|
import { withUnstatedContainers } from '../UnstatedUtils';
|
|
import { withUnstatedContainers } from '../UnstatedUtils';
|
|
|
import AppContainer from '~/client/services/AppContainer';
|
|
import AppContainer from '~/client/services/AppContainer';
|
|
|
import { toastError } from '~/client/util/apiNotification';
|
|
import { toastError } from '~/client/util/apiNotification';
|
|
@@ -17,6 +19,106 @@ import { toastError } from '~/client/util/apiNotification';
|
|
|
import FormattedDistanceDate from '../FormattedDistanceDate';
|
|
import FormattedDistanceDate from '../FormattedDistanceDate';
|
|
|
|
|
|
|
|
const logger = loggerFactory('growi:History');
|
|
const logger = loggerFactory('growi:History');
|
|
|
|
|
+
|
|
|
|
|
+function PageItemLower({ page }) {
|
|
|
|
|
+ return (
|
|
|
|
|
+ <div className="d-flex justify-content-between grw-recent-changes-item-lower pt-1">
|
|
|
|
|
+ <div className="d-flex">
|
|
|
|
|
+ <div className="footstamp-icon mr-1 d-inline-block"><FootstampIcon /></div>
|
|
|
|
|
+ <div className="mr-2 grw-list-counts d-inline-block">{page.seenUsers.length}</div>
|
|
|
|
|
+ <div className="icon-bubble mr-1 d-inline-block"></div>
|
|
|
|
|
+ <div className="mr-2 grw-list-counts d-inline-block">{page.commentCount}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="grw-formatted-distance-date small mt-auto">
|
|
|
|
|
+ <FormattedDistanceDate id={page._id} date={page.updatedAt} />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ );
|
|
|
|
|
+}
|
|
|
|
|
+PageItemLower.propTypes = {
|
|
|
|
|
+ page: PropTypes.any,
|
|
|
|
|
+};
|
|
|
|
|
+function LargePageItem({ page }) {
|
|
|
|
|
+ const dPagePath = new DevidedPagePath(page.path, false, true);
|
|
|
|
|
+ const linkedPagePathFormer = new LinkedPagePath(dPagePath.former);
|
|
|
|
|
+ const linkedPagePathLatter = new LinkedPagePath(dPagePath.latter);
|
|
|
|
|
+ const FormerLink = () => (
|
|
|
|
|
+ <div className="grw-page-path-text-muted-container small">
|
|
|
|
|
+ <PagePathHierarchicalLink linkedPagePath={linkedPagePathFormer} />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ let locked;
|
|
|
|
|
+ if (page.grant !== 1) {
|
|
|
|
|
+ locked = <span><i className="icon-lock ml-2" /></span>;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const tags = page.tags;
|
|
|
|
|
+ const tagElements = tags.map((tag) => {
|
|
|
|
|
+ return (
|
|
|
|
|
+ <a key={tag.name} href={`/_search?q=tag:${tag.name}`} className="grw-tag-label badge badge-secondary mr-2 small">
|
|
|
|
|
+ {tag.name}
|
|
|
|
|
+ </a>
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ return (
|
|
|
|
|
+ <li className="list-group-item py-3 px-0">
|
|
|
|
|
+ <div className="d-flex w-100">
|
|
|
|
|
+ <UserPicture user={page.lastUpdateUser} size="md" noTooltip />
|
|
|
|
|
+ <div className="flex-grow-1 ml-2">
|
|
|
|
|
+ { !dPagePath.isRoot && <FormerLink /> }
|
|
|
|
|
+ <h5 className="my-2">
|
|
|
|
|
+ <PagePathHierarchicalLink linkedPagePath={linkedPagePathLatter} basePath={dPagePath.isRoot ? undefined : dPagePath.former} />
|
|
|
|
|
+ {locked}
|
|
|
|
|
+ </h5>
|
|
|
|
|
+ <div className="grw-tag-labels mt-1 mb-2">
|
|
|
|
|
+ { tagElements }
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <PageItemLower page={page} />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </li>
|
|
|
|
|
+ );
|
|
|
|
|
+}
|
|
|
|
|
+LargePageItem.propTypes = {
|
|
|
|
|
+ page: PropTypes.any,
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+function SmallPageItem({ page }) {
|
|
|
|
|
+ const dPagePath = new DevidedPagePath(page.path, false, true);
|
|
|
|
|
+ const linkedPagePathFormer = new LinkedPagePath(dPagePath.former);
|
|
|
|
|
+ const linkedPagePathLatter = new LinkedPagePath(dPagePath.latter);
|
|
|
|
|
+ const FormerLink = () => (
|
|
|
|
|
+ <div className="grw-page-path-text-muted-container small">
|
|
|
|
|
+ <PagePathHierarchicalLink linkedPagePath={linkedPagePathFormer} />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ let locked;
|
|
|
|
|
+ if (page.grant !== 1) {
|
|
|
|
|
+ locked = <span><i className="icon-lock ml-2" /></span>;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return (
|
|
|
|
|
+ <li className="list-group-item py-2 px-0">
|
|
|
|
|
+ <div className="d-flex w-100">
|
|
|
|
|
+ <UserPicture user={page.lastUpdateUser} size="md" noTooltip />
|
|
|
|
|
+ <div className="flex-grow-1 ml-2">
|
|
|
|
|
+ { !dPagePath.isRoot && <FormerLink /> }
|
|
|
|
|
+ <h5 className="my-0">
|
|
|
|
|
+ <PagePathHierarchicalLink linkedPagePath={linkedPagePathLatter} basePath={dPagePath.isRoot ? undefined : dPagePath.former} />
|
|
|
|
|
+ {locked}
|
|
|
|
|
+ </h5>
|
|
|
|
|
+ <PageItemLower page={page} />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </li>
|
|
|
|
|
+ );
|
|
|
|
|
+}
|
|
|
|
|
+SmallPageItem.propTypes = {
|
|
|
|
|
+ page: PropTypes.any,
|
|
|
|
|
+};
|
|
|
class RecentChanges extends React.Component {
|
|
class RecentChanges extends React.Component {
|
|
|
|
|
|
|
|
static propTypes = {
|
|
static propTypes = {
|
|
@@ -26,10 +128,16 @@ class RecentChanges extends React.Component {
|
|
|
|
|
|
|
|
constructor(props) {
|
|
constructor(props) {
|
|
|
super(props);
|
|
super(props);
|
|
|
-
|
|
|
|
|
|
|
+ this.state = {
|
|
|
|
|
+ isRecentChangesSidebarSmall: false,
|
|
|
|
|
+ };
|
|
|
this.reloadData = this.reloadData.bind(this);
|
|
this.reloadData = this.reloadData.bind(this);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ componentWillMount() {
|
|
|
|
|
+ this.retrieveSizePreferenceFromLocalStorage();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
async componentDidMount() {
|
|
async componentDidMount() {
|
|
|
this.reloadData();
|
|
this.reloadData();
|
|
|
}
|
|
}
|
|
@@ -46,36 +154,22 @@ class RecentChanges extends React.Component {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- PageItem = ({ page }) => {
|
|
|
|
|
- const dPagePath = new DevidedPagePath(page.path, false, true);
|
|
|
|
|
- const linkedPagePathFormer = new LinkedPagePath(dPagePath.former);
|
|
|
|
|
- const linkedPagePathLatter = new LinkedPagePath(dPagePath.latter);
|
|
|
|
|
- const FormerLink = () => (
|
|
|
|
|
- <div className="grw-page-path-text-muted-container small">
|
|
|
|
|
- <PagePathHierarchicalLink linkedPagePath={linkedPagePathFormer} />
|
|
|
|
|
- </div>
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ retrieveSizePreferenceFromLocalStorage() {
|
|
|
|
|
+ if (window.localStorage.isRecentChangesSidebarSmall === 'true') {
|
|
|
|
|
+ this.setState({
|
|
|
|
|
+ isRecentChangesSidebarSmall: true,
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- return (
|
|
|
|
|
- <li className="list-group-item p-2">
|
|
|
|
|
- <div className="d-flex w-100">
|
|
|
|
|
- <UserPicture user={page.lastUpdateUser} size="md" noTooltip />
|
|
|
|
|
- <div className="flex-grow-1 ml-2">
|
|
|
|
|
- { !dPagePath.isRoot && <FormerLink /> }
|
|
|
|
|
- <h5 className="mb-1">
|
|
|
|
|
- <PagePathHierarchicalLink linkedPagePath={linkedPagePathLatter} basePath={dPagePath.isRoot ? undefined : dPagePath.former} />
|
|
|
|
|
- </h5>
|
|
|
|
|
- <div className="text-right small">
|
|
|
|
|
- <FormattedDistanceDate id={page.id} date={page.updatedAt} />
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </li>
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ changeSizeHandler = (e) => {
|
|
|
|
|
+ this.setState({
|
|
|
|
|
+ isRecentChangesSidebarSmall: e.target.checked,
|
|
|
|
|
+ });
|
|
|
|
|
+ window.localStorage.setItem('isRecentChangesSidebarSmall', e.target.checked);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
render() {
|
|
|
- const { PageItem } = this;
|
|
|
|
|
const { t } = this.props;
|
|
const { t } = this.props;
|
|
|
const { recentlyUpdatedPages } = this.props.appContainer.state;
|
|
const { recentlyUpdatedPages } = this.props.appContainer.state;
|
|
|
|
|
|
|
@@ -84,13 +178,26 @@ class RecentChanges extends React.Component {
|
|
|
<div className="grw-sidebar-content-header p-3 d-flex">
|
|
<div className="grw-sidebar-content-header p-3 d-flex">
|
|
|
<h3 className="mb-0">{t('Recent Changes')}</h3>
|
|
<h3 className="mb-0">{t('Recent Changes')}</h3>
|
|
|
{/* <h3 className="mb-0">{t('Recent Created')}</h3> */} {/* TODO: impl switching */}
|
|
{/* <h3 className="mb-0">{t('Recent Created')}</h3> */} {/* TODO: impl switching */}
|
|
|
- <button type="button" className="btn btn-sm btn-outline-secondary ml-auto" onClick={this.reloadData}>
|
|
|
|
|
|
|
+ <button type="button" className="btn btn-sm ml-auto grw-btn-reload-rc" onClick={this.reloadData}>
|
|
|
<i className="icon icon-reload"></i>
|
|
<i className="icon icon-reload"></i>
|
|
|
</button>
|
|
</button>
|
|
|
|
|
+ <div className="grw-recent-changes-resize-button custom-control custom-switch ml-2">
|
|
|
|
|
+ <input
|
|
|
|
|
+ id="recentChangesResize"
|
|
|
|
|
+ className="custom-control-input"
|
|
|
|
|
+ type="checkbox"
|
|
|
|
|
+ checked={this.state.isRecentChangesSidebarSmall}
|
|
|
|
|
+ onChange={e => this.setState({ isRecentChangesSidebarSmall: e.target.checked })}
|
|
|
|
|
+ />
|
|
|
|
|
+ <label className="custom-control-label" htmlFor="recentChangesResize">
|
|
|
|
|
+ </label>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
- <div className="grw-sidebar-content-body p-3">
|
|
|
|
|
|
|
+ <div className="grw-sidebar-content-body grw-recent-changes p-3">
|
|
|
<ul className="list-group list-group-flush">
|
|
<ul className="list-group list-group-flush">
|
|
|
- { recentlyUpdatedPages.map(page => <PageItem key={page.id} page={page} />) }
|
|
|
|
|
|
|
+ {recentlyUpdatedPages.map(page => (this.state.isRecentChangesSidebarSmall
|
|
|
|
|
+ ? <SmallPageItem key={page._id} page={page} />
|
|
|
|
|
+ : <LargePageItem key={page._id} page={page} />))}
|
|
|
</ul>
|
|
</ul>
|
|
|
</div>
|
|
</div>
|
|
|
</>
|
|
</>
|
|
@@ -104,4 +211,5 @@ class RecentChanges extends React.Component {
|
|
|
*/
|
|
*/
|
|
|
const RecentChangesWrapper = withUnstatedContainers(RecentChanges, [AppContainer]);
|
|
const RecentChangesWrapper = withUnstatedContainers(RecentChanges, [AppContainer]);
|
|
|
|
|
|
|
|
|
|
+
|
|
|
export default withTranslation()(RecentChangesWrapper);
|
|
export default withTranslation()(RecentChangesWrapper);
|