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.

185 lines
7.3 KiB

  1. import React from 'react';
  2. import Column from '../ui/components/column';
  3. import ColumnLink from '../ui/components/column_link';
  4. import ColumnSubheading from '../ui/components/column_subheading';
  5. import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
  6. import { connect } from 'react-redux';
  7. import PropTypes from 'prop-types';
  8. import ImmutablePropTypes from 'react-immutable-proptypes';
  9. import ImmutablePureComponent from 'react-immutable-pure-component';
  10. import { me, profile_directory, showTrends } from '../../initial_state';
  11. import { fetchFollowRequests } from 'mastodon/actions/accounts';
  12. import { List as ImmutableList } from 'immutable';
  13. import NavigationContainer from '../compose/containers/navigation_container';
  14. import Icon from 'mastodon/components/icon';
  15. import LinkFooter from 'mastodon/features/ui/components/link_footer';
  16. import TrendsContainer from './containers/trends_container';
  17. const messages = defineMessages({
  18. home_timeline: { id: 'tabs_bar.home', defaultMessage: 'Home' },
  19. notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' },
  20. public_timeline: { id: 'navigation_bar.public_timeline', defaultMessage: 'Federated timeline' },
  21. settings_subheading: { id: 'column_subheading.settings', defaultMessage: 'Settings' },
  22. community_timeline: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
  23. direct: { id: 'navigation_bar.direct', defaultMessage: 'Direct messages' },
  24. bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
  25. preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
  26. follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
  27. favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
  28. blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
  29. domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Hidden domains' },
  30. mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
  31. pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned toots' },
  32. lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
  33. discover: { id: 'navigation_bar.discover', defaultMessage: 'Discover' },
  34. personal: { id: 'navigation_bar.personal', defaultMessage: 'Personal' },
  35. security: { id: 'navigation_bar.security', defaultMessage: 'Security' },
  36. menu: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
  37. profile_directory: { id: 'getting_started.directory', defaultMessage: 'Profile directory' },
  38. });
  39. const mapStateToProps = state => ({
  40. myAccount: state.getIn(['accounts', me]),
  41. columns: state.getIn(['settings', 'columns']),
  42. unreadFollowRequests: state.getIn(['user_lists', 'follow_requests', 'items'], ImmutableList()).size,
  43. });
  44. const mapDispatchToProps = dispatch => ({
  45. fetchFollowRequests: () => dispatch(fetchFollowRequests()),
  46. });
  47. const badgeDisplay = (number, limit) => {
  48. if (number === 0) {
  49. return undefined;
  50. } else if (limit && number >= limit) {
  51. return `${limit}+`;
  52. } else {
  53. return number;
  54. }
  55. };
  56. const NAVIGATION_PANEL_BREAKPOINT = 600 + (285 * 2) + (10 * 2);
  57. export default @connect(mapStateToProps, mapDispatchToProps)
  58. @injectIntl
  59. class GettingStarted extends ImmutablePureComponent {
  60. static contextTypes = {
  61. router: PropTypes.object.isRequired,
  62. };
  63. static propTypes = {
  64. intl: PropTypes.object.isRequired,
  65. myAccount: ImmutablePropTypes.map.isRequired,
  66. columns: ImmutablePropTypes.list,
  67. multiColumn: PropTypes.bool,
  68. fetchFollowRequests: PropTypes.func.isRequired,
  69. unreadFollowRequests: PropTypes.number,
  70. unreadNotifications: PropTypes.number,
  71. };
  72. componentDidMount () {
  73. const { fetchFollowRequests, multiColumn } = this.props;
  74. if (!multiColumn && window.innerWidth >= NAVIGATION_PANEL_BREAKPOINT) {
  75. this.context.router.history.replace('/timelines/home');
  76. return;
  77. }
  78. fetchFollowRequests();
  79. }
  80. render () {
  81. const { intl, myAccount, columns, multiColumn, unreadFollowRequests } = this.props;
  82. const navItems = [];
  83. let height = (multiColumn) ? 0 : 60;
  84. if (multiColumn) {
  85. navItems.push(
  86. <ColumnSubheading key='header-discover' text={intl.formatMessage(messages.discover)} />,
  87. <ColumnLink key='community_timeline' icon='users' text={intl.formatMessage(messages.community_timeline)} to='/timelines/public/local' />,
  88. <ColumnLink key='public_timeline' icon='globe' text={intl.formatMessage(messages.public_timeline)} to='/timelines/public' />,
  89. );
  90. height += 34 + 48*2;
  91. if (profile_directory) {
  92. navItems.push(
  93. <ColumnLink key='directory' icon='address-book' text={intl.formatMessage(messages.profile_directory)} to='/directory' />,
  94. );
  95. height += 48;
  96. }
  97. navItems.push(
  98. <ColumnSubheading key='header-personal' text={intl.formatMessage(messages.personal)} />,
  99. );
  100. height += 34;
  101. } else if (profile_directory) {
  102. navItems.push(
  103. <ColumnLink key='directory' icon='address-book' text={intl.formatMessage(messages.profile_directory)} to='/directory' />,
  104. );
  105. height += 48;
  106. }
  107. if (multiColumn && !columns.find(item => item.get('id') === 'HOME')) {
  108. navItems.push(
  109. <ColumnLink key='home' icon='home' text={intl.formatMessage(messages.home_timeline)} to='/timelines/home' />,
  110. );
  111. height += 48;
  112. }
  113. navItems.push(
  114. <ColumnLink key='direct' icon='envelope' text={intl.formatMessage(messages.direct)} to='/timelines/direct' />,
  115. <ColumnLink key='bookmark' icon='bookmark' text={intl.formatMessage(messages.bookmarks)} to='/bookmarks' />,
  116. <ColumnLink key='favourites' icon='heart' text={intl.formatMessage(messages.favourites)} to='/favourites' />,
  117. <ColumnLink key='lists' icon='list-ul' text={intl.formatMessage(messages.lists)} to='/lists' />,
  118. );
  119. height += 48*4;
  120. if (myAccount.get('locked') || unreadFollowRequests > 0) {
  121. navItems.push(<ColumnLink key='follow_requests' icon='user-plus' text={intl.formatMessage(messages.follow_requests)} badge={badgeDisplay(unreadFollowRequests, 40)} to='/follow_requests' />);
  122. height += 48;
  123. }
  124. if (!multiColumn) {
  125. navItems.push(
  126. <ColumnSubheading key='header-settings' text={intl.formatMessage(messages.settings_subheading)} />,
  127. <ColumnLink key='preferences' icon='gears' text={intl.formatMessage(messages.preferences)} href='/settings/preferences' />,
  128. );
  129. height += 34 + 48;
  130. }
  131. return (
  132. <Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.menu)}>
  133. {multiColumn && <div className='column-header__wrapper'>
  134. <h1 className='column-header'>
  135. <button>
  136. <Icon id='bars' className='column-header__icon' fixedWidth />
  137. <FormattedMessage id='getting_started.heading' defaultMessage='Getting started' />
  138. </button>
  139. </h1>
  140. </div>}
  141. <div className='getting-started'>
  142. <div className='getting-started__wrapper' style={{ height }}>
  143. {!multiColumn && <NavigationContainer />}
  144. {navItems}
  145. </div>
  146. {!multiColumn && <div className='flex-spacer' />}
  147. <LinkFooter withHotkeys={multiColumn} />
  148. </div>
  149. {multiColumn && showTrends && <TrendsContainer />}
  150. </Column>
  151. );
  152. }
  153. }