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.

125 lines
3.5 KiB

  1. import React from 'react';
  2. import { Provider } from 'react-redux';
  3. import PropTypes from 'prop-types';
  4. import configureStore from '../store/configureStore';
  5. import {
  6. updateTimeline,
  7. deleteFromTimelines,
  8. refreshHomeTimeline,
  9. connectTimeline,
  10. disconnectTimeline,
  11. } from '../actions/timelines';
  12. import { showOnboardingOnce } from '../actions/onboarding';
  13. import { updateNotifications, refreshNotifications } from '../actions/notifications';
  14. import BrowserRouter from 'react-router-dom/BrowserRouter';
  15. import Route from 'react-router-dom/Route';
  16. import ScrollContext from 'react-router-scroll/lib/ScrollBehaviorContext';
  17. import UI from '../features/ui';
  18. import { hydrateStore } from '../actions/store';
  19. import createStream from '../stream';
  20. import { IntlProvider, addLocaleData } from 'react-intl';
  21. import { getLocale } from '../locales';
  22. const { localeData, messages } = getLocale();
  23. addLocaleData(localeData);
  24. const store = configureStore();
  25. const initialState = JSON.parse(document.getElementById('initial-state').textContent);
  26. store.dispatch(hydrateStore(initialState));
  27. class Mastodon extends React.PureComponent {
  28. componentDidMount() {
  29. const { locale } = this.props;
  30. const streamingAPIBaseURL = store.getState().getIn(['meta', 'streaming_api_base_url']);
  31. const accessToken = store.getState().getIn(['meta', 'access_token']);
  32. const setupPolling = () => {
  33. this.polling = setInterval(() => {
  34. store.dispatch(refreshHomeTimeline());
  35. store.dispatch(refreshNotifications());
  36. }, 20000);
  37. };
  38. const clearPolling = () => {
  39. clearInterval(this.polling);
  40. this.polling = undefined;
  41. };
  42. this.subscription = createStream(streamingAPIBaseURL, accessToken, 'user', {
  43. connected () {
  44. clearPolling();
  45. store.dispatch(connectTimeline('home'));
  46. },
  47. disconnected () {
  48. setupPolling();
  49. store.dispatch(disconnectTimeline('home'));
  50. },
  51. received (data) {
  52. switch(data.event) {
  53. case 'update':
  54. store.dispatch(updateTimeline('home', JSON.parse(data.payload)));
  55. break;
  56. case 'delete':
  57. store.dispatch(deleteFromTimelines(data.payload));
  58. break;
  59. case 'notification':
  60. store.dispatch(updateNotifications(JSON.parse(data.payload), messages, locale));
  61. break;
  62. }
  63. },
  64. reconnected () {
  65. clearPolling();
  66. store.dispatch(connectTimeline('home'));
  67. store.dispatch(refreshHomeTimeline());
  68. store.dispatch(refreshNotifications());
  69. },
  70. });
  71. // Desktop notifications
  72. if (typeof window.Notification !== 'undefined' && Notification.permission === 'default') {
  73. Notification.requestPermission();
  74. }
  75. store.dispatch(showOnboardingOnce());
  76. }
  77. componentWillUnmount () {
  78. if (typeof this.subscription !== 'undefined') {
  79. this.subscription.close();
  80. this.subscription = null;
  81. }
  82. if (typeof this.polling !== 'undefined') {
  83. clearInterval(this.polling);
  84. this.polling = null;
  85. }
  86. }
  87. render () {
  88. const { locale } = this.props;
  89. return (
  90. <IntlProvider locale={locale} messages={messages}>
  91. <Provider store={store}>
  92. <BrowserRouter basename='/web'>
  93. <ScrollContext>
  94. <Route path='/' component={UI} />
  95. </ScrollContext>
  96. </BrowserRouter>
  97. </Provider>
  98. </IntlProvider>
  99. );
  100. }
  101. }
  102. Mastodon.propTypes = {
  103. locale: PropTypes.string.isRequired,
  104. };
  105. export default Mastodon;