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.

124 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. export const store = configureStore();
  25. const initialState = JSON.parse(document.getElementById('initial-state').textContent);
  26. export const hydrateAction = hydrateStore(initialState);
  27. store.dispatch(hydrateAction);
  28. export default class Mastodon extends React.PureComponent {
  29. static propTypes = {
  30. locale: PropTypes.string.isRequired,
  31. };
  32. componentDidMount() {
  33. const { locale } = this.props;
  34. const streamingAPIBaseURL = store.getState().getIn(['meta', 'streaming_api_base_url']);
  35. const accessToken = store.getState().getIn(['meta', 'access_token']);
  36. const setupPolling = () => {
  37. this.polling = setInterval(() => {
  38. store.dispatch(refreshHomeTimeline());
  39. store.dispatch(refreshNotifications());
  40. }, 20000);
  41. };
  42. const clearPolling = () => {
  43. clearInterval(this.polling);
  44. this.polling = undefined;
  45. };
  46. this.subscription = createStream(streamingAPIBaseURL, accessToken, 'user', {
  47. connected () {
  48. clearPolling();
  49. store.dispatch(connectTimeline('home'));
  50. },
  51. disconnected () {
  52. setupPolling();
  53. store.dispatch(disconnectTimeline('home'));
  54. },
  55. received (data) {
  56. switch(data.event) {
  57. case 'update':
  58. store.dispatch(updateTimeline('home', JSON.parse(data.payload)));
  59. break;
  60. case 'delete':
  61. store.dispatch(deleteFromTimelines(data.payload));
  62. break;
  63. case 'notification':
  64. store.dispatch(updateNotifications(JSON.parse(data.payload), messages, locale));
  65. break;
  66. }
  67. },
  68. reconnected () {
  69. clearPolling();
  70. store.dispatch(connectTimeline('home'));
  71. store.dispatch(refreshHomeTimeline());
  72. store.dispatch(refreshNotifications());
  73. },
  74. });
  75. // Desktop notifications
  76. if (typeof window.Notification !== 'undefined' && Notification.permission === 'default') {
  77. Notification.requestPermission();
  78. }
  79. store.dispatch(showOnboardingOnce());
  80. }
  81. componentWillUnmount () {
  82. if (typeof this.subscription !== 'undefined') {
  83. this.subscription.close();
  84. this.subscription = null;
  85. }
  86. if (typeof this.polling !== 'undefined') {
  87. clearInterval(this.polling);
  88. this.polling = null;
  89. }
  90. }
  91. render () {
  92. const { locale } = this.props;
  93. return (
  94. <IntlProvider locale={locale} messages={messages}>
  95. <Provider store={store}>
  96. <BrowserRouter basename='/web'>
  97. <ScrollContext>
  98. <Route path='/' component={UI} />
  99. </ScrollContext>
  100. </BrowserRouter>
  101. </Provider>
  102. </IntlProvider>
  103. );
  104. }
  105. }