From 26f3b35cc7d774d492b017e3f3c6f1e7a3582fe4 Mon Sep 17 00:00:00 2001 From: Sebastian Jambor Date: Sun, 15 Jan 2023 19:58:48 +0100 Subject: [PATCH] move event source to top level this way, activities are always logged while the app is open, and the activity log survives local navigations --- .../mastodon/actions/activity_log.js | 11 ++ .../mastodon/containers/mastodon.js | 10 ++ .../mastodon/features/activity_log/index.js | 133 +++++++++--------- .../mastodon/reducers/activity_log.js | 17 +++ app/javascript/mastodon/reducers/index.js | 2 + 5 files changed, 105 insertions(+), 68 deletions(-) create mode 100644 app/javascript/mastodon/actions/activity_log.js create mode 100644 app/javascript/mastodon/reducers/activity_log.js diff --git a/app/javascript/mastodon/actions/activity_log.js b/app/javascript/mastodon/actions/activity_log.js new file mode 100644 index 000000000..680965f60 --- /dev/null +++ b/app/javascript/mastodon/actions/activity_log.js @@ -0,0 +1,11 @@ +export const ACTIVITY_LOG_RESET ='ACTIVITY_LOG_RESET'; +export const ACTIVITY_LOG_ADD = 'ACTIVITY_LOG_ADD'; + +export const resetActivityLog = () => ({ + type: ACTIVITY_LOG_RESET, +}); + +export const addActivityLog = value => ({ + type: ACTIVITY_LOG_ADD, + value, +}); diff --git a/app/javascript/mastodon/containers/mastodon.js b/app/javascript/mastodon/containers/mastodon.js index 002b71e93..d3ec9aa81 100644 --- a/app/javascript/mastodon/containers/mastodon.js +++ b/app/javascript/mastodon/containers/mastodon.js @@ -13,6 +13,7 @@ import { connectUserStream } from 'mastodon/actions/streaming'; import ErrorBoundary from 'mastodon/components/error_boundary'; import initialState, { title as siteTitle } from 'mastodon/initial_state'; import { getLocale } from 'mastodon/locales'; +import { addActivityLog } from 'mastodon/actions/activity_log'; const { localeData, messages } = getLocale(); addLocaleData(localeData); @@ -61,6 +62,14 @@ export default class Mastodon extends React.PureComponent { componentDidMount() { if (this.identity.signedIn) { this.disconnect = store.dispatch(connectUserStream()); + + this.eventSource = new EventSource('/api/v1/activity_log'); + this.eventSource.onmessage = (event) => { + const parsed = JSON.parse(event.data); + if (parsed.type !== 'keep-alive') { + store.dispatch(addActivityLog(parsed)); + } + }; } } @@ -68,6 +77,7 @@ export default class Mastodon extends React.PureComponent { if (this.disconnect) { this.disconnect(); this.disconnect = null; + this.eventSource.close(); } } diff --git a/app/javascript/mastodon/features/activity_log/index.js b/app/javascript/mastodon/features/activity_log/index.js index 6ae431e2c..c37075740 100644 --- a/app/javascript/mastodon/features/activity_log/index.js +++ b/app/javascript/mastodon/features/activity_log/index.js @@ -1,4 +1,6 @@ -import React, { useEffect, useReducer, useRef } from 'react'; +import React from 'react'; +import { connect } from 'react-redux'; +import ImmutablePureComponent from 'react-immutable-pure-component'; import { FormattedMessage } from 'react-intl'; import PropTypes from 'prop-types'; import Column from 'mastodon/components/column'; @@ -8,79 +10,74 @@ import DismissableBanner from 'mastodon/components/dismissable_banner'; import ActivityPubVisualization from 'activitypub-visualization'; -export default function ActivityLog({ multiColumn }) { - const [logs, dispatch] = useReducer((state, [type, data]) => { - switch (type) { - case 'add-log-event': - return [...state, data]; - case 'reset-logs': - return []; - default: - return state; - } - }, []); +const mapStateToProps = (state) => { + return { + logs: state.getIn(['activity_log', 'logs']), + }; +}; - const columnElement = useRef(null); +export default @connect(mapStateToProps) +class ActivityLog extends ImmutablePureComponent { - useEffect(() => { - const eventSource = new EventSource('/api/v1/activity_log'); - eventSource.onmessage = (event) => { - const parsed = JSON.parse(event.data); - if (parsed.type !== 'keep-alive') { - dispatch(['add-log-event', parsed]); - } - }; - - return function() { - eventSource.close(); - }; - }, []); - - const darkMode = !(document.body && document.body.classList.contains('theme-mastodon-light')); - - // hijack the toggleHidden shortcut to copy the logs to clipbaord - const handlers = { - toggleHidden: () => navigator.clipboard.writeText(JSON.stringify(logs, null, 2)), + static propTypes = { + multiColumn: PropTypes.bool, }; - return ( - - { columnElement.current.scrollTop() }} - multiColumn={multiColumn} - /> + handleHeaderClick = () => { + this.column.scrollTop(); + } - -

- blog, - }} - /> -

-

- -

-
+ setRef = c => { + this.column = c; + } - -
- -
-
+ render() { + + const { logs, multiColumn } = this.props; + + const darkMode = !(document.body && document.body.classList.contains('theme-mastodon-light')); + + // hijack the toggleHidden shortcut to copy the logs to clipbaord + const handlers = { + toggleHidden: () => navigator.clipboard.writeText(JSON.stringify(logs, null, 2)), + }; + + return ( + + + + +

+ blog, + }} + /> +

+

+ +

+
+ + +
+ +
+
+ +
+ ); + } -
- ); } - -ActivityLog.propTypes = { - multiColumn: PropTypes.bool, -}; diff --git a/app/javascript/mastodon/reducers/activity_log.js b/app/javascript/mastodon/reducers/activity_log.js new file mode 100644 index 000000000..716a6ecd1 --- /dev/null +++ b/app/javascript/mastodon/reducers/activity_log.js @@ -0,0 +1,17 @@ +import { Map as ImmutableMap } from 'immutable'; +import { ACTIVITY_LOG_ADD, ACTIVITY_LOG_RESET } from '../actions/activity_log'; + +const initialState = ImmutableMap({ + logs: [], +}); + +export default function activity_log(state = initialState, action) { + switch (action.type) { + case ACTIVITY_LOG_ADD: + return state.set('logs', [...state.get('logs'), action.value]); + case ACTIVITY_LOG_RESET: + return state.set('logs', []); + default: + return state; + } +} diff --git a/app/javascript/mastodon/reducers/index.js b/app/javascript/mastodon/reducers/index.js index 69771ad1b..ea82aceae 100644 --- a/app/javascript/mastodon/reducers/index.js +++ b/app/javascript/mastodon/reducers/index.js @@ -9,6 +9,7 @@ import user_lists from './user_lists'; import domain_lists from './domain_lists'; import accounts from './accounts'; import accounts_counters from './accounts_counters'; +import activity_log from './activity_log'; import statuses from './statuses'; import relationships from './relationships'; import settings from './settings'; @@ -44,6 +45,7 @@ import followed_tags from './followed_tags'; const reducers = { announcements, + activity_log, dropdown_menu, timelines, meta,