move event source to top level
this way, activities are always logged while the app is open, and the activity log survives local navigations
This commit is contained in:
parent
4c6949ec46
commit
26f3b35cc7
5 changed files with 105 additions and 68 deletions
11
app/javascript/mastodon/actions/activity_log.js
Normal file
11
app/javascript/mastodon/actions/activity_log.js
Normal file
|
@ -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,
|
||||
});
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 (
|
||||
<Column bindToDocument={!multiColumn} ref={columnElement} label='Activity Log'>
|
||||
<ColumnHeader
|
||||
icon='comments'
|
||||
title='Activity Log'
|
||||
onClick={() => { columnElement.current.scrollTop() }}
|
||||
multiColumn={multiColumn}
|
||||
/>
|
||||
handleHeaderClick = () => {
|
||||
this.column.scrollTop();
|
||||
}
|
||||
|
||||
<DismissableBanner id='activity_log'>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id='dismissable_banner.activity_log_information'
|
||||
defaultMessage='Open Mastodon in another tab and interact with another instance (for example, follow an account on another instance). The resulting Activities will be shown here. You can find more information on my {blog}.'
|
||||
values={{
|
||||
blog: <a href='//seb.jambor.dev/' style={{ color: darkMode ? '#8c8dff' : '#3a3bff', textDecoration: 'none' }}>blog</a>,
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
<p style={{ paddingTop: '5px' }}>
|
||||
<FormattedMessage
|
||||
id='dismissable_banner.activity_log_clear'
|
||||
defaultMessage='Note: Activities will only be logged while this view is open. When you navigate elsewhere, the log will be cleared.'
|
||||
/>
|
||||
</p>
|
||||
</DismissableBanner>
|
||||
setRef = c => {
|
||||
this.column = c;
|
||||
}
|
||||
|
||||
<HotKeys handlers={handlers}>
|
||||
<div className={`${darkMode ? 'dark' : ''}`}>
|
||||
<ActivityPubVisualization logs={logs} />
|
||||
</div>
|
||||
</HotKeys>
|
||||
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 (
|
||||
<Column bindToDocument={!multiColumn} ref={this.setRef} label='Activity Log'>
|
||||
<ColumnHeader
|
||||
icon='comments'
|
||||
title='Activity Log'
|
||||
onClick={this.handleHeaderClick}
|
||||
multiColumn={multiColumn}
|
||||
/>
|
||||
|
||||
<DismissableBanner id='activity_log'>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id='dismissable_banner.activity_log_information'
|
||||
defaultMessage='When you interact with another instance (for example, follow an account on another instance), the resulting Activities will be shown here. You can find more information on my {blog}.'
|
||||
values={{
|
||||
blog: <a href='//seb.jambor.dev/' style={{ color: darkMode ? '#8c8dff' : '#3a3bff', textDecoration: 'none' }}>blog</a>,
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
<p style={{ paddingTop: '5px' }}>
|
||||
<FormattedMessage
|
||||
id='dismissable_banner.activity_log_clear'
|
||||
defaultMessage='Note: Activities will only be logged while Mastodon is open. When you navigate elsewhere or reload the page, the log will be cleared.'
|
||||
/>
|
||||
</p>
|
||||
</DismissableBanner>
|
||||
|
||||
<HotKeys handlers={handlers}>
|
||||
<div className={`${darkMode ? 'dark' : ''}`}>
|
||||
<ActivityPubVisualization logs={logs} />
|
||||
</div>
|
||||
</HotKeys>
|
||||
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
ActivityLog.propTypes = {
|
||||
multiColumn: PropTypes.bool,
|
||||
};
|
||||
|
|
17
app/javascript/mastodon/reducers/activity_log.js
Normal file
17
app/javascript/mastodon/reducers/activity_log.js
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue