diff --git a/app/soapbox/actions/auth.js b/app/soapbox/actions/auth.js
index 50a26cd3c..09a225bf5 100644
--- a/app/soapbox/actions/auth.js
+++ b/app/soapbox/actions/auth.js
@@ -145,7 +145,17 @@ export function logIn(username, password) {
export function logOut() {
return (dispatch, getState) => {
+ const state = getState();
+
dispatch({ type: AUTH_LOGGED_OUT });
+
+ // Attempt to destroy OAuth token on logout
+ api(getState).post('/oauth/revoke', {
+ client_id: state.getIn(['auth', 'app', 'client_id']),
+ client_secret: state.getIn(['auth', 'app', 'client_secret']),
+ token: state.getIn(['auth', 'user', 'access_token']),
+ });
+
dispatch(showAlert('Successfully logged out.', ''));
};
}
diff --git a/app/soapbox/actions/compose.js b/app/soapbox/actions/compose.js
index ef6b9995d..f4a644e87 100644
--- a/app/soapbox/actions/compose.js
+++ b/app/soapbox/actions/compose.js
@@ -143,13 +143,13 @@ export function handleComposeSubmit(dispatch, getState, response, status) {
let dequeueArgs = {};
if (timelineId === 'community') dequeueArgs.onlyMedia = getSettings(getState()).getIn(['community', 'other', 'onlyMedia']);
dispatch(dequeueTimeline(timelineId, null, dequeueArgs));
- dispatch(updateTimeline(timelineId, { ...response.data }));
+ dispatch(updateTimeline(timelineId, response.data.id));
}
};
if (response.data.visibility !== 'direct') {
insertIfOnline('home');
- } else if (response.data.in_reply_to_id === null && response.data.visibility === 'public') {
+ } else if (response.data.visibility === 'public') {
insertIfOnline('community');
insertIfOnline('public');
}
@@ -440,17 +440,6 @@ export function updateTagHistory(tags) {
};
}
-export function hydrateCompose() {
- return (dispatch, getState) => {
- const me = getState().get('me');
- const history = tagHistory.get(me);
-
- if (history !== null) {
- dispatch(updateTagHistory(history));
- }
- };
-}
-
function insertIntoTagHistory(recognizedTags, text) {
return (dispatch, getState) => {
const state = getState();
diff --git a/app/soapbox/actions/import_data.js b/app/soapbox/actions/import_data.js
new file mode 100644
index 000000000..251d2972d
--- /dev/null
+++ b/app/soapbox/actions/import_data.js
@@ -0,0 +1,56 @@
+import api from '../api';
+import { showAlert } from 'soapbox/actions/alerts';
+
+export const IMPORT_FOLLOWS_REQUEST = 'IMPORT_FOLLOWS_REQUEST';
+export const IMPORT_FOLLOWS_SUCCESS = 'IMPORT_FOLLOWS_SUCCESS';
+export const IMPORT_FOLLOWS_FAIL = 'IMPORT_FOLLOWS_FAIL';
+
+export const IMPORT_BLOCKS_REQUEST = 'IMPORT_BLOCKS_REQUEST';
+export const IMPORT_BLOCKS_SUCCESS = 'IMPORT_BLOCKS_SUCCESS';
+export const IMPORT_BLOCKS_FAIL = 'IMPORT_BLOCKS_FAIL';
+
+export const IMPORT_MUTES_REQUEST = 'IMPORT_MUTES_REQUEST';
+export const IMPORT_MUTES_SUCCESS = 'IMPORT_MUTES_SUCCESS';
+export const IMPORT_MUTES_FAIL = 'IMPORT_MUTES_FAIL';
+
+export function importFollows(params) {
+ return (dispatch, getState) => {
+ dispatch({ type: IMPORT_FOLLOWS_REQUEST });
+ return api(getState)
+ .post('/api/pleroma/follow_import', params)
+ .then(response => {
+ dispatch(showAlert('', 'Followers imported successfully'));
+ dispatch({ type: IMPORT_FOLLOWS_SUCCESS, config: response.data });
+ }).catch(error => {
+ dispatch({ type: IMPORT_FOLLOWS_FAIL, error });
+ });
+ };
+}
+
+export function importBlocks(params) {
+ return (dispatch, getState) => {
+ dispatch({ type: IMPORT_BLOCKS_REQUEST });
+ return api(getState)
+ .post('/api/pleroma/blocks_import', params)
+ .then(response => {
+ dispatch(showAlert('', 'Blocks imported successfully'));
+ dispatch({ type: IMPORT_BLOCKS_SUCCESS, config: response.data });
+ }).catch(error => {
+ dispatch({ type: IMPORT_BLOCKS_FAIL, error });
+ });
+ };
+}
+
+export function importMutes(params) {
+ return (dispatch, getState) => {
+ dispatch({ type: IMPORT_MUTES_REQUEST });
+ return api(getState)
+ .post('/api/pleroma/mutes_import', params)
+ .then(response => {
+ dispatch(showAlert('', 'Mutes imported successfully'));
+ dispatch({ type: IMPORT_MUTES_SUCCESS, config: response.data });
+ }).catch(error => {
+ dispatch({ type: IMPORT_MUTES_FAIL, error });
+ });
+ };
+}
diff --git a/app/soapbox/actions/settings.js b/app/soapbox/actions/settings.js
index 097265814..205250c3b 100644
--- a/app/soapbox/actions/settings.js
+++ b/app/soapbox/actions/settings.js
@@ -2,6 +2,7 @@ import { debounce } from 'lodash';
import { showAlertForError } from './alerts';
import { patchMe } from 'soapbox/actions/me';
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
+import uuid from '../uuid';
export const SETTING_CHANGE = 'SETTING_CHANGE';
export const SETTING_SAVE = 'SETTING_SAVE';
@@ -114,6 +115,12 @@ const defaultSettings = ImmutableMap({
trends: ImmutableMap({
show: true,
}),
+
+ columns: ImmutableList([
+ ImmutableMap({ id: 'COMPOSE', uuid: uuid(), params: {} }),
+ ImmutableMap({ id: 'HOME', uuid: uuid(), params: {} }),
+ ImmutableMap({ id: 'NOTIFICATIONS', uuid: uuid(), params: {} }),
+ ]),
});
export function getSettings(state) {
diff --git a/app/soapbox/actions/store.js b/app/soapbox/actions/store.js
deleted file mode 100644
index 87e495b99..000000000
--- a/app/soapbox/actions/store.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import { Iterable, fromJS } from 'immutable';
-import { hydrateCompose } from './compose';
-
-export const STORE_HYDRATE = 'STORE_HYDRATE';
-export const STORE_HYDRATE_LAZY = 'STORE_HYDRATE_LAZY';
-
-const convertState = rawState =>
- fromJS(rawState, (k, v) =>
- Iterable.isIndexed(v) ? v.toList() : v.toMap());
-
-export function hydrateStore(rawState) {
- return dispatch => {
- const state = convertState(rawState);
-
- dispatch({
- type: STORE_HYDRATE,
- state,
- });
-
- dispatch(hydrateCompose());
- // dispatch(importFetchedAccounts(Object.values(rawState.accounts)));
- };
-};
diff --git a/app/soapbox/actions/timelines.js b/app/soapbox/actions/timelines.js
index 498c89dfb..67f6b19f8 100644
--- a/app/soapbox/actions/timelines.js
+++ b/app/soapbox/actions/timelines.js
@@ -25,31 +25,31 @@ export function processTimelineUpdate(timeline, status, accept) {
const columnSettings = getSettings(getState()).get(timeline, ImmutableMap());
const shouldSkipQueue = shouldFilter(fromJS(status), columnSettings);
+ dispatch(importFetchedStatus(status));
+
if (shouldSkipQueue) {
- return dispatch(updateTimeline(timeline, status, accept));
+ return dispatch(updateTimeline(timeline, status.id, accept));
} else {
- return dispatch(updateTimelineQueue(timeline, status, accept));
+ return dispatch(updateTimelineQueue(timeline, status.id, accept));
}
};
}
-export function updateTimeline(timeline, status, accept) {
+export function updateTimeline(timeline, statusId, accept) {
return dispatch => {
if (typeof accept === 'function' && !accept(status)) {
return;
}
- dispatch(importFetchedStatus(status));
-
dispatch({
type: TIMELINE_UPDATE,
timeline,
- status,
+ statusId,
});
};
};
-export function updateTimelineQueue(timeline, status, accept) {
+export function updateTimelineQueue(timeline, statusId, accept) {
return dispatch => {
if (typeof accept === 'function' && !accept(status)) {
return;
@@ -58,7 +58,7 @@ export function updateTimelineQueue(timeline, status, accept) {
dispatch({
type: TIMELINE_UPDATE_QUEUE,
timeline,
- status,
+ statusId,
});
};
};
@@ -73,8 +73,8 @@ export function dequeueTimeline(timeline, expandFunc, optionalExpandArgs) {
if (totalQueuedItemsCount === 0) {
return;
} else if (totalQueuedItemsCount > 0 && totalQueuedItemsCount <= MAX_QUEUED_ITEMS) {
- queuedItems.forEach(status => {
- dispatch(updateTimeline(timeline, status.toJS(), null));
+ queuedItems.forEach(statusId => {
+ dispatch(updateTimeline(timeline, statusId, null));
});
} else {
if (typeof expandFunc === 'function') {
diff --git a/app/soapbox/components/hashtag.js b/app/soapbox/components/hashtag.js
index 2a0017c9c..2872a236a 100644
--- a/app/soapbox/components/hashtag.js
+++ b/app/soapbox/components/hashtag.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { Sparklines, SparklinesCurve } from 'react-sparklines';
+// import { Sparklines, SparklinesCurve } from 'react-sparklines';
import { FormattedMessage } from 'react-intl';
import ImmutablePropTypes from 'react-immutable-proptypes';
import Permalink from './permalink';
@@ -17,11 +17,12 @@ const Hashtag = ({ hashtag }) => (
}
- {hashtag.get('history') &&
+ {/* Pleroma doesn't support tag history yet */}
+ {/* hashtag.get('history') &&
day.get('uses')).toArray()}>
-
}
+
*/}
);
diff --git a/app/soapbox/components/sidebar_menu.js b/app/soapbox/components/sidebar_menu.js
index 6cc615c65..ea9f753d4 100644
--- a/app/soapbox/components/sidebar_menu.js
+++ b/app/soapbox/components/sidebar_menu.js
@@ -29,6 +29,7 @@ const messages = defineMessages({
filters: { id: 'navigation_bar.filters', defaultMessage: 'Muted words' },
admin_settings: { id: 'navigation_bar.admin_settings', defaultMessage: 'Admin settings' },
soapbox_config: { id: 'navigation_bar.soapbox_config', defaultMessage: 'Soapbox config' },
+ import_data: { id: 'navigation_bar.import_data', defaultMessage: 'Import data' },
security: { id: 'navigation_bar.security', defaultMessage: 'Security' },
logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
lists: { id: 'column.lists', defaultMessage: 'Lists' },
@@ -180,6 +181,10 @@ class SidebarMenu extends ImmutablePureComponent {
{intl.formatMessage(messages.preferences)}
+
+
+ {intl.formatMessage(messages.import_data)}
+
{intl.formatMessage(messages.security)}
diff --git a/app/soapbox/containers/soapbox.js b/app/soapbox/containers/soapbox.js
index ed4549d27..138d12701 100644
--- a/app/soapbox/containers/soapbox.js
+++ b/app/soapbox/containers/soapbox.js
@@ -14,8 +14,6 @@ import { ScrollContext } from 'react-router-scroll-4';
import UI from '../features/ui';
// import Introduction from '../features/introduction';
import { fetchCustomEmojis } from '../actions/custom_emojis';
-import { hydrateStore } from '../actions/store';
-import initialState from '../initial_state';
import { preload } from '../actions/preload';
import { IntlProvider } from 'react-intl';
import ErrorBoundary from '../components/error_boundary';
@@ -32,9 +30,6 @@ const validLocale = locale => Object.keys(messages).includes(locale);
export const store = configureStore();
-const hydrateAction = hydrateStore(initialState);
-
-store.dispatch(hydrateAction);
store.dispatch(preload());
store.dispatch(fetchMe());
store.dispatch(fetchInstance());
diff --git a/app/soapbox/features/compose/components/action_bar.js b/app/soapbox/features/compose/components/action_bar.js
index 8bc6ef4c4..863d49f22 100644
--- a/app/soapbox/features/compose/components/action_bar.js
+++ b/app/soapbox/features/compose/components/action_bar.js
@@ -20,6 +20,7 @@ const messages = defineMessages({
filters: { id: 'navigation_bar.filters', defaultMessage: 'Muted words' },
admin_settings: { id: 'navigation_bar.admin_settings', defaultMessage: 'Admin settings' },
soapbox_config: { id: 'navigation_bar.soapbox_config', defaultMessage: 'Soapbox config' },
+ import_data: { id: 'navigation_bar.import_data', defaultMessage: 'Import data' },
security: { id: 'navigation_bar.security', defaultMessage: 'Security' },
logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
keyboard_shortcuts: { id: 'navigation_bar.keyboard_shortcuts', defaultMessage: 'Hotkeys' },
@@ -84,6 +85,7 @@ class ActionBar extends React.PureComponent {
menu.push({ text: intl.formatMessage(messages.soapbox_config), to: '/soapbox/config' });
}
menu.push({ text: intl.formatMessage(messages.preferences), to: '/settings/preferences' });
+ menu.push({ text: intl.formatMessage(messages.import_data), to: '/settings/import' });
menu.push({ text: intl.formatMessage(messages.security), to: '/auth/edit' });
menu.push({ text: intl.formatMessage(messages.logout), to: '/auth/sign_out', action: onClickLogOut });
diff --git a/app/soapbox/features/compose/containers/privacy_dropdown_container.js b/app/soapbox/features/compose/containers/privacy_dropdown_container.js
index c112bc553..a90d54fa6 100644
--- a/app/soapbox/features/compose/containers/privacy_dropdown_container.js
+++ b/app/soapbox/features/compose/containers/privacy_dropdown_container.js
@@ -17,7 +17,10 @@ const mapDispatchToProps = dispatch => ({
isUserTouching,
onModalOpen: props => dispatch(openModal('ACTIONS', props)),
- onModalClose: () => dispatch(closeModal()),
+ onModalClose: () => {
+ dispatch(closeModal());
+ dispatch(openModal('COMPOSE'));
+ },
});
diff --git a/app/soapbox/features/forms/__tests__/__snapshots__/forms-test.js.snap b/app/soapbox/features/forms/__tests__/__snapshots__/forms-test.js.snap
index 0f1e2df50..1bd05c5fd 100644
--- a/app/soapbox/features/forms/__tests__/__snapshots__/forms-test.js.snap
+++ b/app/soapbox/features/forms/__tests__/__snapshots__/forms-test.js.snap
@@ -55,23 +55,27 @@ exports[` renders correctly 1`] = `
`;
exports[` renders correctly 1`] = `
-
+
+
+
`;
exports[` renders correctly 1`] = `
diff --git a/app/soapbox/features/forms/index.js b/app/soapbox/features/forms/index.js
index 555af97f4..35e221e5f 100644
--- a/app/soapbox/features/forms/index.js
+++ b/app/soapbox/features/forms/index.js
@@ -270,7 +270,7 @@ export class SelectDropdown extends ImmutablePureComponent {
));
- const selectElem = ;
+ const selectElem = ;
return label ? (
{selectElem}
diff --git a/app/soapbox/features/import_data/components/csv_importer.js b/app/soapbox/features/import_data/components/csv_importer.js
new file mode 100644
index 000000000..7f837b067
--- /dev/null
+++ b/app/soapbox/features/import_data/components/csv_importer.js
@@ -0,0 +1,79 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { injectIntl } from 'react-intl';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+import PropTypes from 'prop-types';
+import {
+ SimpleInput,
+ SimpleForm,
+ FieldsGroup,
+} from 'soapbox/features/forms';
+
+export default @connect()
+@injectIntl
+class CSVImporter extends ImmutablePureComponent {
+
+ static propTypes = {
+ action: PropTypes.func.isRequired,
+ messages: PropTypes.object.isRequired,
+ dispatch: PropTypes.func.isRequired,
+ intl: PropTypes.object.isRequired,
+ };
+
+ state = {
+ file: null,
+ isLoading: false,
+ }
+
+ handleSubmit = (event) => {
+ const { dispatch, action } = this.props;
+
+ let params = new FormData();
+ params.append('list', this.state.file);
+
+ this.setState({ isLoading: true });
+ dispatch(action(params)).then(() => {
+ this.setState({ isLoading: false });
+ }).catch((error) => {
+ this.setState({ isLoading: false });
+ });
+
+ event.preventDefault();
+ }
+
+ handleFileChange = e => {
+ const [file] = e.target.files || [];
+ this.setState({ file });
+ }
+
+ render() {
+ const { intl, messages } = this.props;
+
+ return (
+
+
+
+
+
+
+ );
+ }
+
+}
diff --git a/app/soapbox/features/import_data/index.js b/app/soapbox/features/import_data/index.js
new file mode 100644
index 000000000..c1d788ff7
--- /dev/null
+++ b/app/soapbox/features/import_data/index.js
@@ -0,0 +1,56 @@
+import React from 'react';
+import { defineMessages, injectIntl } from 'react-intl';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+import PropTypes from 'prop-types';
+import Column from '../ui/components/column';
+import {
+ importFollows,
+ importBlocks,
+ // importMutes,
+} from 'soapbox/actions/import_data';
+import CSVImporter from './components/csv_importer';
+
+const messages = defineMessages({
+ heading: { id: 'column.import_data', defaultMessage: 'Import data' },
+ submit: { id: 'import_data.actions.import', defaultMessage: 'Import' },
+});
+
+const followMessages = defineMessages({
+ input_label: { id: 'import_data.follows_label', defaultMessage: 'Follows' },
+ input_hint: { id: 'import_data.hints.follows', defaultMessage: 'CSV file containing a list of followed accounts' },
+ submit: { id: 'import_data.actions.import_follows', defaultMessage: 'Import follows' },
+});
+
+const blockMessages = defineMessages({
+ input_label: { id: 'import_data.blocks_label', defaultMessage: 'Blocks' },
+ input_hint: { id: 'import_data.hints.blocks', defaultMessage: 'CSV file containing a list of blocked accounts' },
+ submit: { id: 'import_data.actions.import_blocks', defaultMessage: 'Import blocks' },
+});
+
+// Not yet supported by Pleroma stable, in develop branch
+// const muteMessages = defineMessages({
+// input_label: { id: 'import_data.mutes_label', defaultMessage: 'Mutes' },
+// input_hint: { id: 'import_data.hints.mutes', defaultMessage: 'CSV file containing a list of muted accounts' },
+// submit: { id: 'import_data.actions.import_mutes', defaultMessage: 'Import mutes' },
+// });
+
+export default @injectIntl
+class ImportData extends ImmutablePureComponent {
+
+ static propTypes = {
+ intl: PropTypes.object.isRequired,
+ };
+
+ render() {
+ const { intl } = this.props;
+
+ return (
+
+
+
+ {/* */}
+
+ );
+ }
+
+}
diff --git a/app/soapbox/features/soapbox_config/index.js b/app/soapbox/features/soapbox_config/index.js
index 6c6b679df..9f196a62b 100644
--- a/app/soapbox/features/soapbox_config/index.js
+++ b/app/soapbox/features/soapbox_config/index.js
@@ -321,7 +321,7 @@ class SoapboxConfig extends ImmutablePureComponent {
-
+ {/*
@@ -345,7 +345,7 @@ class SoapboxConfig extends ImmutablePureComponent {
-
+ */}
diff --git a/app/soapbox/features/standalone/compose/index.js b/app/soapbox/features/standalone/compose/index.js
deleted file mode 100644
index c9ac782e7..000000000
--- a/app/soapbox/features/standalone/compose/index.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import React from 'react';
-import ComposeFormContainer from '../../compose/containers/compose_form_container';
-import NotificationsContainer from '../../ui/containers/notifications_container';
-import LoadingBarContainer from '../../ui/containers/loading_bar_container';
-import ModalContainer from '../../ui/containers/modal_container';
-
-export default class Compose extends React.PureComponent {
-
- render() {
- return (
-
-
-
-
-
-
- );
- }
-
-}
diff --git a/app/soapbox/features/standalone/hashtag_timeline/index.js b/app/soapbox/features/standalone/hashtag_timeline/index.js
deleted file mode 100644
index 6bb162b6c..000000000
--- a/app/soapbox/features/standalone/hashtag_timeline/index.js
+++ /dev/null
@@ -1,84 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { expandHashtagTimeline } from 'soapbox/actions/timelines';
-import Masonry from 'react-masonry-infinite';
-import { List as ImmutableList } from 'immutable';
-import DetailedStatusContainer from 'soapbox/features/status/containers/detailed_status_container';
-import { debounce } from 'lodash';
-import LoadingIndicator from 'soapbox/components/loading_indicator';
-
-const mapStateToProps = (state, { hashtag }) => ({
- statusIds: state.getIn(['timelines', `hashtag:${hashtag}`, 'items'], ImmutableList()),
- isLoading: state.getIn(['timelines', `hashtag:${hashtag}`, 'isLoading'], false),
- hasMore: state.getIn(['timelines', `hashtag:${hashtag}`, 'hasMore'], false),
-});
-
-export default @connect(mapStateToProps)
-class HashtagTimeline extends React.PureComponent {
-
- static propTypes = {
- dispatch: PropTypes.func.isRequired,
- statusIds: ImmutablePropTypes.list.isRequired,
- isLoading: PropTypes.bool.isRequired,
- hasMore: PropTypes.bool.isRequired,
- hashtag: PropTypes.string.isRequired,
- };
-
- componentDidMount() {
- const { dispatch, hashtag } = this.props;
-
- dispatch(expandHashtagTimeline(hashtag));
- }
-
- handleLoadMore = () => {
- const maxId = this.props.statusIds.last();
-
- if (maxId) {
- this.props.dispatch(expandHashtagTimeline(this.props.hashtag, { maxId }));
- }
- }
-
- setRef = c => {
- this.masonry = c;
- }
-
- handleHeightChange = debounce(() => {
- if (!this.masonry) {
- return;
- }
-
- this.masonry.forcePack();
- }, 50)
-
- render() {
- const { statusIds, hasMore, isLoading } = this.props;
-
- const sizes = [
- { columns: 1, gutter: 0 },
- { mq: '415px', columns: 1, gutter: 10 },
- { mq: '640px', columns: 2, gutter: 10 },
- { mq: '960px', columns: 3, gutter: 10 },
- { mq: '1255px', columns: 3, gutter: 10 },
- ];
-
- const loader = (isLoading && statusIds.isEmpty()) ?
: undefined;
-
- return (
-
- {statusIds.map(statusId => (
-
-
-
- )).toArray()}
-
- );
- }
-
-}
diff --git a/app/soapbox/features/standalone/public_timeline/index.js b/app/soapbox/features/standalone/public_timeline/index.js
deleted file mode 100644
index a509c2e04..000000000
--- a/app/soapbox/features/standalone/public_timeline/index.js
+++ /dev/null
@@ -1,99 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { expandPublicTimeline, expandCommunityTimeline } from 'soapbox/actions/timelines';
-import Masonry from 'react-masonry-infinite';
-import { List as ImmutableList, Map as ImmutableMap } from 'immutable';
-import DetailedStatusContainer from 'soapbox/features/status/containers/detailed_status_container';
-import { debounce } from 'lodash';
-import LoadingIndicator from 'soapbox/components/loading_indicator';
-
-const mapStateToProps = (state, { local }) => {
- const timeline = state.getIn(['timelines', local ? 'community' : 'public'], ImmutableMap());
-
- return {
- statusIds: timeline.get('items', ImmutableList()),
- isLoading: timeline.get('isLoading', false),
- hasMore: timeline.get('hasMore', false),
- };
-};
-
-export default @connect(mapStateToProps)
-class PublicTimeline extends React.PureComponent {
-
- static propTypes = {
- dispatch: PropTypes.func.isRequired,
- statusIds: ImmutablePropTypes.list.isRequired,
- isLoading: PropTypes.bool.isRequired,
- hasMore: PropTypes.bool.isRequired,
- local: PropTypes.bool,
- };
-
- componentDidMount() {
- this._connect();
- }
-
- componentDidUpdate(prevProps) {
- if (prevProps.local !== this.props.local) {
- this._connect();
- }
- }
-
- _connect() {
- const { dispatch, local } = this.props;
-
- dispatch(local ? expandCommunityTimeline() : expandPublicTimeline());
- }
-
- handleLoadMore = () => {
- const { dispatch, statusIds, local } = this.props;
- const maxId = statusIds.last();
-
- if (maxId) {
- dispatch(local ? expandCommunityTimeline({ maxId }) : expandPublicTimeline({ maxId }));
- }
- }
-
- setRef = c => {
- this.masonry = c;
- }
-
- handleHeightChange = debounce(() => {
- if (!this.masonry) {
- return;
- }
-
- this.masonry.forcePack();
- }, 50)
-
- render() {
- const { statusIds, hasMore, isLoading } = this.props;
-
- const sizes = [
- { columns: 1, gutter: 0 },
- { mq: '415px', columns: 1, gutter: 10 },
- { mq: '640px', columns: 2, gutter: 10 },
- { mq: '960px', columns: 3, gutter: 10 },
- { mq: '1255px', columns: 3, gutter: 10 },
- ];
-
- const loader = (isLoading && statusIds.isEmpty()) ?
: undefined;
-
- return (
-
- {statusIds.map(statusId => (
-
-
-
- )).toArray()}
-
- );
- }
-
-}
diff --git a/app/soapbox/features/status/components/detailed_status.js b/app/soapbox/features/status/components/detailed_status.js
index 131de49ec..ee162810e 100644
--- a/app/soapbox/features/status/components/detailed_status.js
+++ b/app/soapbox/features/status/components/detailed_status.js
@@ -175,7 +175,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
-
+
diff --git a/app/soapbox/features/ui/index.js b/app/soapbox/features/ui/index.js
index 3f37ffe40..70b891ce8 100644
--- a/app/soapbox/features/ui/index.js
+++ b/app/soapbox/features/ui/index.js
@@ -78,6 +78,7 @@ import {
Preferences,
EditProfile,
SoapboxConfig,
+ ImportData,
PasswordReset,
SecurityForm,
MfaForm,
@@ -263,6 +264,7 @@ class SwitchingColumnsArea extends React.PureComponent {
+
diff --git a/app/soapbox/features/ui/util/async-components.js b/app/soapbox/features/ui/util/async-components.js
index 73a8a5540..83f26c270 100644
--- a/app/soapbox/features/ui/util/async-components.js
+++ b/app/soapbox/features/ui/util/async-components.js
@@ -186,6 +186,10 @@ export function SoapboxConfig() {
return import(/* webpackChunkName: "features/soapbox_config" */'../../soapbox_config');
}
+export function ImportData() {
+ return import(/* webpackChunkName: "features/import_data" */'../../import_data');
+}
+
export function PasswordReset() {
return import(/* webpackChunkName: "features/auth_login" */'../../auth_login/components/password_reset');
}
diff --git a/app/soapbox/features/ui/util/react_router_helpers.js b/app/soapbox/features/ui/util/react_router_helpers.js
index ceffd0daa..133750f91 100644
--- a/app/soapbox/features/ui/util/react_router_helpers.js
+++ b/app/soapbox/features/ui/util/react_router_helpers.js
@@ -18,7 +18,7 @@ class WrappedRoute extends React.Component {
static propTypes = {
component: PropTypes.func.isRequired,
- page: PropTypes.func,
+ page: PropTypes.object,
content: PropTypes.node,
componentParams: PropTypes.object,
layout: PropTypes.object,
diff --git a/app/soapbox/initial_state.js b/app/soapbox/initial_state.js
deleted file mode 100644
index dd47e4e8b..000000000
--- a/app/soapbox/initial_state.js
+++ /dev/null
@@ -1,6 +0,0 @@
-'use strict';
-
-const element = document.getElementById('initial-state');
-const initialState = element ? JSON.parse(element.textContent) : {};
-
-export default initialState;
diff --git a/app/soapbox/reducers/__tests__/compose-test.js b/app/soapbox/reducers/__tests__/compose-test.js
index 230036fe8..d8122bab2 100644
--- a/app/soapbox/reducers/__tests__/compose-test.js
+++ b/app/soapbox/reducers/__tests__/compose-test.js
@@ -3,18 +3,18 @@ import { Map as ImmutableMap } from 'immutable';
import { ME_FETCH_SUCCESS, ME_PATCH_SUCCESS } from 'soapbox/actions/me';
import { SETTING_CHANGE } from 'soapbox/actions/settings';
import * as actions from 'soapbox/actions/compose';
-//import { STORE_HYDRATE } from 'soapbox/actions/store';
//import { REDRAFT } from 'soapbox/actions/statuses';
import { TIMELINE_DELETE } from 'soapbox/actions/timelines';
describe('compose reducer', () => {
it('returns the initial state by default', () => {
- expect(reducer(undefined, {}).toJS()).toMatchObject({
+ const state = reducer(undefined, {});
+ expect(state.toJS()).toMatchObject({
mounted: 0,
sensitive: false,
spoiler: false,
spoiler_text: '',
- privacy: null,
+ privacy: 'public',
text: '',
focusDate: null,
caretPosition: null,
@@ -30,10 +30,10 @@ describe('compose reducer', () => {
suggestions: [],
default_privacy: 'public',
default_sensitive: false,
- idempotencyKey: null,
tagHistory: [],
content_type: 'text/markdown',
});
+ expect(state.get('idempotencyKey').length === 36);
});
it('uses \'public\' scope as default', () => {
@@ -132,23 +132,6 @@ describe('compose reducer', () => {
});
});
- // it('should handle STORE_HYDRATE', () => {
- // const state = ImmutableMap({ });
- // const action = {
- // type: STORE_HYDRATE,
- // state: ImmutableMap({
- // compose: true,
- // text: 'newtext',
- // }),
- // };
- // expect(reducer(state, action)).toEqual(ImmutableMap({
- // state: ImmutableMap({
- // compose: true,
- // text: 'newtext',
- // }),
- // }));
- // });
-
it('should handle COMPOSE_MOUNT', () => {
const state = ImmutableMap({ mounted: 1 });
const action = {
diff --git a/app/soapbox/reducers/compose.js b/app/soapbox/reducers/compose.js
index ad5ea9b64..c72ea6d87 100644
--- a/app/soapbox/reducers/compose.js
+++ b/app/soapbox/reducers/compose.js
@@ -38,21 +38,22 @@ import {
COMPOSE_POLL_SETTINGS_CHANGE,
} from '../actions/compose';
import { TIMELINE_DELETE } from '../actions/timelines';
-import { STORE_HYDRATE } from '../actions/store';
import { REDRAFT } from '../actions/statuses';
import { ME_FETCH_SUCCESS, ME_PATCH_SUCCESS } from '../actions/me';
import { SETTING_CHANGE, FE_NAME } from '../actions/settings';
import { Map as ImmutableMap, List as ImmutableList, OrderedSet as ImmutableOrderedSet, fromJS } from 'immutable';
+import { tagHistory } from 'soapbox/settings';
import uuid from '../uuid';
import { unescapeHTML } from '../utils/html';
const initialState = ImmutableMap({
+ id: null,
mounted: 0,
sensitive: false,
spoiler: false,
spoiler_text: '',
content_type: 'text/markdown',
- privacy: null,
+ privacy: 'public',
text: '',
focusDate: null,
caretPosition: null,
@@ -69,7 +70,7 @@ const initialState = ImmutableMap({
default_privacy: 'public',
default_sensitive: false,
resetFileKey: Math.floor((Math.random() * 0x10000)),
- idempotencyKey: null,
+ idempotencyKey: uuid(),
tagHistory: ImmutableList(),
});
@@ -178,16 +179,6 @@ const privacyPreference = (a, b) => {
return order[Math.max(order.indexOf(a), order.indexOf(b), 0)];
};
-const hydrate = (state, hydratedState = ImmutableMap()) => {
- state = clearAll(state.merge(hydratedState));
-
- if (hydratedState.has('text')) {
- state = state.set('text', hydratedState.get('text'));
- }
-
- return state;
-};
-
const domParser = new DOMParser();
const expandMentions = status => {
@@ -204,8 +195,6 @@ const expandMentions = status => {
export default function compose(state = initialState, action) {
let me, defaultPrivacy;
switch(action.type) {
- case STORE_HYDRATE:
- return hydrate(state, action.state.get('compose'));
case COMPOSE_MOUNT:
return state.set('mounted', state.get('mounted') + 1);
case COMPOSE_UNMOUNT:
@@ -374,9 +363,12 @@ export default function compose(state = initialState, action) {
return state.update('poll', poll => poll.set('expires_in', action.expiresIn).set('multiple', action.isMultiple));
case ME_FETCH_SUCCESS:
me = fromJS(action.me);
- defaultPrivacy = me.getIn(['pleroma', 'settings_store', FE_NAME, 'defaultPrivacy']);
- if (!defaultPrivacy) return state;
- return state.set('default_privacy', defaultPrivacy).set('privacy', defaultPrivacy);
+ defaultPrivacy = me.getIn(['pleroma', 'settings_store', FE_NAME, 'defaultPrivacy'], 'public');
+ return state.merge({
+ default_privacy: defaultPrivacy,
+ privacy: defaultPrivacy,
+ tagHistory: ImmutableList(tagHistory.get(action.me.id)),
+ });
case ME_PATCH_SUCCESS:
me = fromJS(action.me);
defaultPrivacy = me.getIn(['pleroma', 'settings_store', FE_NAME, 'defaultPrivacy']);
diff --git a/app/soapbox/reducers/contexts.js b/app/soapbox/reducers/contexts.js
index 8df462f81..844f9f78f 100644
--- a/app/soapbox/reducers/contexts.js
+++ b/app/soapbox/reducers/contexts.js
@@ -3,7 +3,8 @@ import {
ACCOUNT_MUTE_SUCCESS,
} from '../actions/accounts';
import { CONTEXT_FETCH_SUCCESS } from '../actions/statuses';
-import { TIMELINE_DELETE, TIMELINE_UPDATE } from '../actions/timelines';
+import { TIMELINE_DELETE } from '../actions/timelines';
+import { STATUS_IMPORT, STATUSES_IMPORT } from 'soapbox/actions/importer';
import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutable';
const initialState = ImmutableMap({
@@ -87,8 +88,12 @@ export default function replies(state = initialState, action) {
return normalizeContext(state, action.id, action.ancestors, action.descendants);
case TIMELINE_DELETE:
return deleteFromContexts(state, [action.id]);
- case TIMELINE_UPDATE:
+ case STATUS_IMPORT:
return updateContext(state, action.status);
+ case STATUSES_IMPORT:
+ return state.withMutations(mutable =>
+ action.statuses.forEach(status => updateContext(mutable, status)));
+
default:
return state;
}
diff --git a/app/soapbox/reducers/index.js b/app/soapbox/reducers/index.js
index 6f4abdafe..5ec2b581c 100644
--- a/app/soapbox/reducers/index.js
+++ b/app/soapbox/reducers/index.js
@@ -1,4 +1,6 @@
import { combineReducers } from 'redux-immutable';
+import { Map as ImmutableMap } from 'immutable';
+import { AUTH_LOGGED_OUT } from 'soapbox/actions/auth';
import dropdown_menu from './dropdown_menu';
import timelines from './timelines';
import meta from './meta';
@@ -48,7 +50,7 @@ import chat_messages from './chat_messages';
import chat_message_lists from './chat_message_lists';
import profile_hover_card from './profile_hover_card';
-const reducers = {
+const appReducer = combineReducers({
dropdown_menu,
timelines,
meta,
@@ -97,6 +99,27 @@ const reducers = {
chat_messages,
chat_message_lists,
profile_hover_card,
+});
+
+// Clear the state (mostly) when the user logs out
+const logOut = (state = ImmutableMap()) => {
+ const whitelist = ['instance', 'soapbox', 'custom_emojis'];
+
+ return ImmutableMap(
+ whitelist.reduce((acc, curr) => {
+ acc[curr] = state.get(curr);
+ return acc;
+ }, {})
+ );
};
-export default combineReducers(reducers);
+const rootReducer = (state, action) => {
+ switch(action.type) {
+ case AUTH_LOGGED_OUT:
+ return appReducer(logOut(state), action);
+ default:
+ return appReducer(state, action);
+ }
+};
+
+export default rootReducer;
diff --git a/app/soapbox/reducers/media_attachments.js b/app/soapbox/reducers/media_attachments.js
index 1f7fe8cff..7e67805dd 100644
--- a/app/soapbox/reducers/media_attachments.js
+++ b/app/soapbox/reducers/media_attachments.js
@@ -1,4 +1,3 @@
-import { STORE_HYDRATE } from '../actions/store';
import {
Map as ImmutableMap,
List as ImmutableList,
@@ -35,8 +34,6 @@ const initialState = ImmutableMap({
export default function meta(state = initialState, action) {
switch(action.type) {
- case STORE_HYDRATE:
- return state.merge(action.state.get('media_attachments'));
default:
return state;
}
diff --git a/app/soapbox/reducers/meta.js b/app/soapbox/reducers/meta.js
index 6bd397c2c..c3f5062df 100644
--- a/app/soapbox/reducers/meta.js
+++ b/app/soapbox/reducers/meta.js
@@ -1,6 +1,5 @@
'use strict';
-import { STORE_HYDRATE } from '../actions/store';
import { ME_FETCH_SUCCESS, ME_PATCH_SUCCESS } from 'soapbox/actions/me';
import { Map as ImmutableMap, fromJS } from 'immutable';
@@ -8,8 +7,6 @@ const initialState = ImmutableMap();
export default function meta(state = initialState, action) {
switch(action.type) {
- case STORE_HYDRATE:
- return state.merge(action.state.get('meta'));
case ME_FETCH_SUCCESS:
case ME_PATCH_SUCCESS:
const me = fromJS(action.me);
diff --git a/app/soapbox/reducers/notifications.js b/app/soapbox/reducers/notifications.js
index 7dbda2c64..3d0d14dfb 100644
--- a/app/soapbox/reducers/notifications.js
+++ b/app/soapbox/reducers/notifications.js
@@ -32,8 +32,9 @@ const initialState = ImmutableMap({
// For sorting the notifications
const comparator = (a, b) => {
- if (a.get('id') < b.get('id')) return 1;
- if (a.get('id') > b.get('id')) return -1;
+ const parse = m => parseInt(m.get('id'), 10);
+ if (parse(a) < parse(b)) return 1;
+ if (parse(a) > parse(b)) return -1;
return 0;
};
diff --git a/app/soapbox/reducers/push_notifications.js b/app/soapbox/reducers/push_notifications.js
index 317352b79..6b9001684 100644
--- a/app/soapbox/reducers/push_notifications.js
+++ b/app/soapbox/reducers/push_notifications.js
@@ -1,4 +1,3 @@
-import { STORE_HYDRATE } from '../actions/store';
import { SET_BROWSER_SUPPORT, SET_SUBSCRIPTION, CLEAR_SUBSCRIPTION, SET_ALERTS } from '../actions/push_notifications';
import Immutable from 'immutable';
@@ -17,21 +16,6 @@ const initialState = Immutable.Map({
export default function push_subscriptions(state = initialState, action) {
switch(action.type) {
- case STORE_HYDRATE: {
- const push_subscription = action.state.get('push_subscription');
-
- if (push_subscription) {
- return state
- .set('subscription', new Immutable.Map({
- id: push_subscription.get('id'),
- endpoint: push_subscription.get('endpoint'),
- }))
- .set('alerts', push_subscription.get('alerts') || initialState.get('alerts'))
- .set('isSubscribed', true);
- }
-
- return state;
- }
case SET_SUBSCRIPTION:
return state
.set('subscription', new Immutable.Map({
diff --git a/app/soapbox/reducers/settings.js b/app/soapbox/reducers/settings.js
index 1066716d7..be4f6b88d 100644
--- a/app/soapbox/reducers/settings.js
+++ b/app/soapbox/reducers/settings.js
@@ -1,11 +1,9 @@
import { SETTING_CHANGE, SETTING_SAVE, FE_NAME } from '../actions/settings';
import { NOTIFICATIONS_FILTER_SET } from '../actions/notifications';
-import { STORE_HYDRATE } from '../actions/store';
import { EMOJI_USE } from '../actions/emojis';
import { LIST_DELETE_SUCCESS, LIST_FETCH_FAIL } from '../actions/lists';
import { ME_FETCH_SUCCESS } from 'soapbox/actions/me';
import { Map as ImmutableMap, fromJS } from 'immutable';
-import uuid from '../uuid';
// Default settings are in action/settings.js
//
@@ -15,22 +13,12 @@ const initialState = ImmutableMap({
saved: true,
});
-const defaultColumns = fromJS([
- { id: 'COMPOSE', uuid: uuid(), params: {} },
- { id: 'HOME', uuid: uuid(), params: {} },
- { id: 'NOTIFICATIONS', uuid: uuid(), params: {} },
-]);
-
-const hydrate = (state, settings) => state.mergeDeep(settings).update('columns', (val = defaultColumns) => val);
-
const updateFrequentEmojis = (state, emoji) => state.update('frequentlyUsedEmojis', ImmutableMap(), map => map.update(emoji.id, 0, count => count + 1)).set('saved', false);
const filterDeadListColumns = (state, listId) => state.update('columns', columns => columns.filterNot(column => column.get('id') === 'LIST' && column.get('params').get('id') === listId));
export default function settings(state = initialState, action) {
switch(action.type) {
- case STORE_HYDRATE:
- return hydrate(state, action.state.get('settings'));
case ME_FETCH_SUCCESS:
const me = fromJS(action.me);
let fePrefs = me.getIn(['pleroma', 'settings_store', FE_NAME], ImmutableMap());
diff --git a/app/soapbox/reducers/statuses.js b/app/soapbox/reducers/statuses.js
index 8cccd4a09..b7f09ef22 100644
--- a/app/soapbox/reducers/statuses.js
+++ b/app/soapbox/reducers/statuses.js
@@ -2,6 +2,7 @@ import {
REBLOG_REQUEST,
REBLOG_FAIL,
FAVOURITE_REQUEST,
+ UNFAVOURITE_REQUEST,
FAVOURITE_FAIL,
} from '../actions/interactions';
import {
@@ -12,11 +13,12 @@ import {
} from '../actions/statuses';
import {
EMOJI_REACT_REQUEST,
+ UNEMOJI_REACT_REQUEST,
} from '../actions/emoji_reacts';
import { TIMELINE_DELETE } from '../actions/timelines';
import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer';
import { Map as ImmutableMap, fromJS } from 'immutable';
-import { simulateEmojiReact } from 'soapbox/utils/emoji_reacts';
+import { simulateEmojiReact, simulateUnEmojiReact } from 'soapbox/utils/emoji_reacts';
const importStatus = (state, status) => state.set(status.id, fromJS(status));
@@ -40,11 +42,27 @@ export default function statuses(state = initialState, action) {
case STATUSES_IMPORT:
return importStatuses(state, action.statuses);
case FAVOURITE_REQUEST:
- return state.setIn([action.status.get('id'), 'favourited'], true);
+ return state.update(action.status.get('id'), status =>
+ status
+ .set('favourited', true)
+ .update('favourites_count', count => count + 1));
+ case UNFAVOURITE_REQUEST:
+ return state.update(action.status.get('id'), status =>
+ status
+ .set('favourited', false)
+ .update('favourites_count', count => Math.max(0, count - 1)));
case EMOJI_REACT_REQUEST:
- const path = [action.status.get('id'), 'pleroma', 'emoji_reactions'];
- const emojiReacts = state.getIn(path);
- return state.setIn(path, simulateEmojiReact(emojiReacts, action.emoji));
+ return state
+ .updateIn(
+ [action.status.get('id'), 'pleroma', 'emoji_reactions'],
+ emojiReacts => simulateEmojiReact(emojiReacts, action.emoji)
+ );
+ case UNEMOJI_REACT_REQUEST:
+ return state
+ .updateIn(
+ [action.status.get('id'), 'pleroma', 'emoji_reactions'],
+ emojiReacts => simulateUnEmojiReact(emojiReacts, action.emoji)
+ );
case FAVOURITE_FAIL:
return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'favourited'], false);
case REBLOG_REQUEST:
diff --git a/app/soapbox/reducers/timelines.js b/app/soapbox/reducers/timelines.js
index b134742ba..c86376406 100644
--- a/app/soapbox/reducers/timelines.js
+++ b/app/soapbox/reducers/timelines.js
@@ -65,10 +65,10 @@ const expandNormalizedTimeline = (state, timeline, statuses, next, isPartial, is
}));
};
-const updateTimeline = (state, timeline, status) => {
+const updateTimeline = (state, timeline, statusId) => {
const top = state.getIn([timeline, 'top']);
const ids = state.getIn([timeline, 'items'], ImmutableList());
- const includesId = ids.includes(status.get('id'));
+ const includesId = ids.includes(statusId);
const unread = state.getIn([timeline, 'unread'], 0);
if (includesId) {
@@ -80,17 +80,17 @@ const updateTimeline = (state, timeline, status) => {
return state.update(timeline, initialTimeline, map => map.withMutations(mMap => {
if (!top) mMap.set('unread', unread + 1);
if (top && ids.size > 40) newIds = newIds.take(20);
- mMap.set('items', newIds.unshift(status.get('id')));
+ mMap.set('items', newIds.unshift(statusId));
}));
};
-const updateTimelineQueue = (state, timeline, status) => {
+const updateTimelineQueue = (state, timeline, statusId) => {
const queuedStatuses = state.getIn([timeline, 'queuedItems'], ImmutableList());
const listedStatuses = state.getIn([timeline, 'items'], ImmutableList());
const totalQueuedItemsCount = state.getIn([timeline, 'totalQueuedItemsCount'], 0);
- let alreadyExists = queuedStatuses.find(existingQueuedStatus => existingQueuedStatus.get('id') === status.get('id'));
- if (!alreadyExists) alreadyExists = listedStatuses.find(existingListedStatusId => existingListedStatusId === status.get('id'));
+ let alreadyExists = queuedStatuses.find(existingQueuedStatus => existingQueuedStatus === statusId);
+ if (!alreadyExists) alreadyExists = listedStatuses.find(existingListedStatusId => existingListedStatusId === statusId);
if (alreadyExists) {
return state;
@@ -100,7 +100,7 @@ const updateTimelineQueue = (state, timeline, status) => {
return state.update(timeline, initialTimeline, map => map.withMutations(mMap => {
if (totalQueuedItemsCount <= MAX_QUEUED_ITEMS) {
- mMap.set('queuedItems', newQueuedStatuses.push(status));
+ mMap.set('queuedItems', newQueuedStatuses.push(statusId));
}
mMap.set('totalQueuedItemsCount', totalQueuedItemsCount + 1);
}));
@@ -165,9 +165,9 @@ export default function timelines(state = initialState, action) {
case TIMELINE_EXPAND_SUCCESS:
return expandNormalizedTimeline(state, action.timeline, fromJS(action.statuses), action.next, action.partial, action.isLoadingRecent);
case TIMELINE_UPDATE:
- return updateTimeline(state, action.timeline, fromJS(action.status));
+ return updateTimeline(state, action.timeline, action.statusId);
case TIMELINE_UPDATE_QUEUE:
- return updateTimelineQueue(state, action.timeline, fromJS(action.status));
+ return updateTimelineQueue(state, action.timeline, action.statusId);
case TIMELINE_DEQUEUE:
return state.update(action.timeline, initialTimeline, map => map.withMutations(mMap => {
mMap.set('queuedItems', ImmutableList());
diff --git a/app/soapbox/utils/__tests__/emoji_reacts-test.js b/app/soapbox/utils/__tests__/emoji_reacts-test.js
index fcce0a11b..4663304b2 100644
--- a/app/soapbox/utils/__tests__/emoji_reacts-test.js
+++ b/app/soapbox/utils/__tests__/emoji_reacts-test.js
@@ -6,6 +6,7 @@ import {
reduceEmoji,
getReactForStatus,
simulateEmojiReact,
+ simulateUnEmojiReact,
} from '../emoji_reacts';
import { fromJS } from 'immutable';
@@ -205,3 +206,28 @@ describe('simulateEmojiReact', () => {
]));
});
});
+
+describe('simulateUnEmojiReact', () => {
+ it('removes the emoji from the list', () => {
+ const emojiReacts = fromJS([
+ { 'count': 2, 'me': false, 'name': '👍' },
+ { 'count': 3, 'me': true, 'name': '❤' },
+ ]);
+ expect(simulateUnEmojiReact(emojiReacts, '❤')).toEqual(fromJS([
+ { 'count': 2, 'me': false, 'name': '👍' },
+ { 'count': 2, 'me': false, 'name': '❤' },
+ ]));
+ });
+
+ it('removes the emoji if it\'s the last one in the list', () => {
+ const emojiReacts = fromJS([
+ { 'count': 2, 'me': false, 'name': '👍' },
+ { 'count': 2, 'me': false, 'name': '❤' },
+ { 'count': 1, 'me': true, 'name': '😯' },
+ ]);
+ expect(simulateUnEmojiReact(emojiReacts, '😯')).toEqual(fromJS([
+ { 'count': 2, 'me': false, 'name': '👍' },
+ { 'count': 2, 'me': false, 'name': '❤' },
+ ]));
+ });
+});
diff --git a/app/soapbox/utils/emoji_reacts.js b/app/soapbox/utils/emoji_reacts.js
index 2cb1b3fc3..39c194101 100644
--- a/app/soapbox/utils/emoji_reacts.js
+++ b/app/soapbox/utils/emoji_reacts.js
@@ -100,3 +100,20 @@ export const simulateEmojiReact = (emojiReacts, emoji) => {
}));
}
};
+
+export const simulateUnEmojiReact = (emojiReacts, emoji) => {
+ const idx = emojiReacts.findIndex(e =>
+ e.get('name') === emoji && e.get('me') === true);
+
+ if (idx > -1) {
+ const emojiReact = emojiReacts.get(idx);
+ const newCount = emojiReact.get('count') - 1;
+ if (newCount < 1) return emojiReacts.delete(idx);
+ return emojiReacts.set(idx, emojiReact.merge({
+ count: emojiReact.get('count') - 1,
+ me: false,
+ }));
+ } else {
+ return emojiReacts;
+ }
+};
diff --git a/app/soapbox/utils/features.js b/app/soapbox/utils/features.js
index 98266e3ca..213b4cad9 100644
--- a/app/soapbox/utils/features.js
+++ b/app/soapbox/utils/features.js
@@ -1,14 +1,14 @@
// Detect backend features to conditionally render elements
-import semver from 'semver';
+import gte from 'semver/functions/gte';
export const getFeatures = instance => {
const v = parseVersion(instance.get('version'));
return {
- suggestions: v.software === 'Mastodon' && semver.gte(v.compatVersion, '2.4.3'),
- trends: v.software === 'Mastodon' && semver.gte(v.compatVersion, '3.0.0'),
- emojiReacts: v.software === 'Pleroma' && semver.gte(v.version, '2.0.0'),
+ suggestions: v.software === 'Mastodon' && gte(v.compatVersion, '2.4.3'),
+ trends: v.software === 'Mastodon' && gte(v.compatVersion, '3.0.0'),
+ emojiReacts: v.software === 'Pleroma' && gte(v.version, '2.0.0'),
attachmentLimit: v.software === 'Pleroma' ? Infinity : 4,
- focalPoint: v.software === 'Mastodon' && semver.gte(v.compatVersion, '2.3.0'),
+ focalPoint: v.software === 'Mastodon' && gte(v.compatVersion, '2.3.0'),
};
};
diff --git a/app/soapbox/utils/theme.js b/app/soapbox/utils/theme.js
index a4fb01c8c..6e847b5a4 100644
--- a/app/soapbox/utils/theme.js
+++ b/app/soapbox/utils/theme.js
@@ -1,13 +1,72 @@
import { Map as ImmutableMap } from 'immutable';
-import { convert } from 'chromatism';
export const generateThemeCss = brandColor => {
if (!brandColor) return null;
return themeDataToCss(brandColorToThemeData(brandColor));
};
+// https://stackoverflow.com/a/5624139
+function hexToRgb(hex) {
+ // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
+ const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
+ hex = hex.replace(shorthandRegex, (m, r, g, b) => (
+ r + r + g + g + b + b
+ ));
+
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+ return result ? {
+ r: parseInt(result[1], 16),
+ g: parseInt(result[2], 16),
+ b: parseInt(result[3], 16),
+ } : {
+ // fall back to Azure
+ r: 4,
+ g: 130,
+ b: 216,
+ };
+}
+
+// Taken from chromatism.js
+// https://github.com/graypegg/chromatism/blob/master/src/conversions/rgb.js
+const rgbToHsl = value => {
+ var r = value.r / 255;
+ var g = value.g / 255;
+ var b = value.b / 255;
+ var rgbOrdered = [ r, g, b ].sort();
+ var l = ((rgbOrdered[0] + rgbOrdered[2]) / 2) * 100;
+ var s, h;
+ if (rgbOrdered[0] === rgbOrdered[2]) {
+ s = 0;
+ h = 0;
+ } else {
+ if (l >= 50) {
+ s = ((rgbOrdered[2] - rgbOrdered[0]) / ((2.0 - rgbOrdered[2]) - rgbOrdered[0])) * 100;
+ } else {
+ s = ((rgbOrdered[2] - rgbOrdered[0]) / (rgbOrdered[2] + rgbOrdered[0])) * 100;
+ }
+ if (rgbOrdered[2] === r) {
+ h = ((g - b) / (rgbOrdered[2] - rgbOrdered[0])) * 60;
+ } else if (rgbOrdered[2] === g) {
+ h = (2 + ((b - r) / (rgbOrdered[2] - rgbOrdered[0]))) * 60;
+ } else {
+ h = (4 + ((r - g) / (rgbOrdered[2] - rgbOrdered[0]))) * 60;
+ }
+ if (h < 0) {
+ h += 360;
+ } else if (h > 360) {
+ h = h % 360;
+ }
+ }
+
+ return {
+ h: h,
+ s: s,
+ l: l,
+ };
+};
+
export const brandColorToThemeData = brandColor => {
- const { h, s, l } = convert(brandColor).hsl;
+ const { h, s, l } = rgbToHsl(hexToRgb(brandColor));
return ImmutableMap({
'brand-color_h': h,
'brand-color_s': `${s}%`,
diff --git a/app/styles/about.scss b/app/styles/about.scss
index 77d93917e..6a09753a6 100644
--- a/app/styles/about.scss
+++ b/app/styles/about.scss
@@ -1,7 +1,5 @@
$maximum-width: 1235px;
$fluid-breakpoint: $maximum-width + 20px;
-$column-breakpoint: 700px;
-$small-breakpoint: 960px;
.public-layout {
.container {
@@ -194,498 +192,6 @@ $small-breakpoint: 960px;
}
}
}
-
- $no-columns-breakpoint: 600px;
-
- .grid {
- display: grid;
- grid-gap: 10px;
- grid-template-columns: minmax(300px, 3fr) minmax(298px, 1fr);
- grid-auto-columns: 25%;
- grid-auto-rows: max-content;
-
- .column-0 {
- grid-row: 1;
- grid-column: 1;
- }
-
- .column-1 {
- grid-row: 1;
- grid-column: 2;
- }
-
- @media screen and (max-width: $no-columns-breakpoint) {
- grid-template-columns: 100%;
- grid-gap: 0;
-
- .column-1 {
- display: none;
- }
- }
- }
-
- .public-account-header {
- overflow: hidden;
- margin-bottom: 10px;
- box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
-
- &.inactive {
- opacity: 0.5;
-
- .public-account-header__image,
- .avatar {
- filter: grayscale(100%);
- }
-
- .logo-button {
- background-color: var(--primary-text-color--faint);
- }
- }
-
- &__image {
- border-radius: 4px 4px 0 0;
- overflow: hidden;
- height: 300px;
- position: relative;
- background: var(--background-color);
-
- &::after {
- content: "";
- display: block;
- position: absolute;
- width: 100%;
- height: 100%;
- box-shadow: inset 0 -1px 1px 1px rgba($base-shadow-color, 0.15);
- top: 0;
- left: 0;
- }
-
- img {
- object-fit: cover;
- display: block;
- width: 100%;
- height: 100%;
- margin: 0;
- border-radius: 4px 4px 0 0;
- }
-
- @media screen and (max-width: 600px) {
- height: 200px;
- }
- }
-
- &--no-bar {
- margin-bottom: 0;
-
- .public-account-header__image,
- .public-account-header__image img {
- border-radius: 4px;
-
- @media screen and (max-width: $no-gap-breakpoint) {
- border-radius: 0;
- }
- }
- }
-
- @media screen and (max-width: $no-gap-breakpoint) {
- margin-bottom: 0;
- box-shadow: none;
-
- &__image::after {
- display: none;
- }
-
- &__image,
- &__image img {
- border-radius: 0;
- }
- }
-
- &__bar {
- position: relative;
- margin-top: -80px;
- display: flex;
- justify-content: flex-start;
-
- &::before {
- content: "";
- display: block;
- background: var(--brand-color--faint);
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
- height: 60px;
- border-radius: 0 0 4px 4px;
- z-index: -1;
- }
-
- .avatar {
- display: block;
- width: 120px;
- height: 120px;
- padding-left: 20px - 4px;
- flex: 0 0 auto;
-
- img {
- display: block;
- width: 100%;
- height: 100%;
- margin: 0;
- border-radius: 50%;
- border: 4px solid var(--brand-color--faint);
- background: var(--background-color);
- }
- }
-
- @media screen and (max-width: 600px) {
- margin-top: 0;
- background: var(--brand-color--faint);
- border-radius: 0 0 4px 4px;
- padding: 5px;
-
- &::before {
- display: none;
- }
-
- .avatar {
- width: 48px;
- height: 48px;
- padding: 7px 0;
- padding-left: 10px;
-
- img {
- border: 0;
- border-radius: 4px;
- }
-
- @media screen and (max-width: 360px) {
- display: none;
- }
- }
- }
-
- @media screen and (max-width: $no-gap-breakpoint) {
- border-radius: 0;
- }
-
- @media screen and (max-width: $no-columns-breakpoint) {
- flex-wrap: wrap;
- }
- }
-
- &__tabs {
- flex: 1 1 auto;
- margin-left: 20px;
-
- &__name {
- padding-top: 20px;
- padding-bottom: 8px;
-
- h1 {
- font-size: 20px;
- line-height: 18px * 1.5;
- color: var(--primary-text-color);
- font-weight: 500;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- text-shadow: 1px 1px 1px $base-shadow-color;
-
- small {
- display: block;
- font-size: 14px;
- color: var(--primary-text-color);
- font-weight: 400;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- }
- }
-
- @media screen and (max-width: 600px) {
- margin-left: 15px;
- display: flex;
- justify-content: space-between;
- align-items: center;
-
- &__name {
- padding-top: 0;
- padding-bottom: 0;
-
- h1 {
- font-size: 16px;
- line-height: 24px;
- text-shadow: none;
-
- small {
- color: var(--primary-text-color--faint);
- }
- }
- }
- }
-
- &__tabs {
- display: flex;
- justify-content: flex-start;
- align-items: stretch;
- height: 58px;
-
- .details-counters {
- display: flex;
- flex-direction: row;
- min-width: 300px;
- }
-
- @media screen and (max-width: $no-columns-breakpoint) {
- .details-counters {
- display: none;
- }
- }
-
- .counter {
- width: 33.3%;
- box-sizing: border-box;
- flex: 0 0 auto;
- color: var(--primary-text-color--faint);
- padding: 10px;
- border-right: 1px solid var(--brand-color--faint);
- cursor: default;
- text-align: center;
- position: relative;
-
- a {
- display: block;
- }
-
- &:last-child {
- border-right: 0;
- }
-
- &::after {
- display: block;
- content: "";
- position: absolute;
- bottom: 0;
- left: 0;
- width: 100%;
- border-bottom: 4px solid var(--brand-color);
- opacity: 0.5;
- transition: all 400ms ease;
- }
-
- &.active {
- &::after {
- border-bottom: 4px solid var(--highlight-text-color);
- opacity: 1;
- }
-
- &.inactive::after {
- border-bottom-color: var(--primary-text-color--faint);
- }
- }
-
- &:hover {
- &::after {
- opacity: 1;
- transition-duration: 100ms;
- }
- }
-
- a {
- text-decoration: none;
- color: inherit;
- }
-
- .counter-label {
- font-size: 12px;
- display: block;
- }
-
- .counter-number {
- font-weight: 500;
- font-size: 18px;
- margin-bottom: 5px;
- color: var(--primary-text-color);
- font-family: var(--font-display), sans-serif;
- }
- }
-
- .spacer {
- flex: 1 1 auto;
- height: 1px;
- }
-
- &__buttons {
- padding: 7px 8px;
- }
- }
- }
-
- &__extra {
- display: none;
- margin-top: 4px;
-
- .public-account-bio {
- border-radius: 0;
- box-shadow: none;
- background: transparent;
- margin: 0 -5px;
-
- .account__header__fields {
- border-top: 1px solid var(--brand-color--med);
- }
-
- .roles {
- display: none;
- }
- }
-
- &__links {
- margin-top: -15px;
- font-size: 14px;
- color: var(--primary-text-color--faint);
-
- a {
- display: inline-block;
- color: var(--primary-text-color--faint);
- text-decoration: none;
- padding: 15px;
- font-weight: 500;
-
- strong {
- font-weight: 700;
- color: var(--primary-text-color);
- }
- }
- }
-
- @media screen and (max-width: $no-columns-breakpoint) {
- display: block;
- flex: 100%;
- }
- }
- }
-
- .account__section-headline {
- border-radius: 4px 4px 0 0;
-
- @media screen and (max-width: $no-gap-breakpoint) {
- border-radius: 0;
- }
- }
-
- .detailed-status__meta {
- margin-top: 25px;
- }
-
- .public-account-bio {
- background: var(--brand-color--med);
- box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
- border-radius: 4px;
- overflow: hidden;
- margin-bottom: 10px;
-
- @media screen and (max-width: $no-gap-breakpoint) {
- box-shadow: none;
- margin-bottom: 0;
- border-radius: 0;
- }
-
- .account__header__fields {
- margin: 0;
- border-top: 0;
-
- a {
- color: var(--brand-color);
- }
-
- dl:first-child .verified {
- border-radius: 0 4px 0 0;
- }
-
- .verified a {
- color: $valid-value-color;
- }
- }
-
- .account__header__content {
- padding: 20px;
- padding-bottom: 0;
- color: var(--primary-text-color);
- }
-
- &__extra,
- .roles {
- padding: 20px;
- font-size: 14px;
- color: var(--primary-text-color--faint);
- }
-
- .roles {
- padding-bottom: 0;
- }
- }
-
- .static-icon-button {
- color: var(--brand-color);
- font-size: 18px;
-
- & > span {
- font-size: 14px;
- font-weight: 500;
- }
- }
-
- .card-grid {
- display: flex;
- flex-wrap: wrap;
- min-width: 100%;
- margin: 0 -5px;
-
- & > div {
- box-sizing: border-box;
- flex: 1 0 auto;
- width: 300px;
- padding: 0 5px;
- margin-bottom: 10px;
- max-width: 33.333%;
-
- @media screen and (max-width: 900px) {
- max-width: 50%;
- }
-
- @media screen and (max-width: 600px) {
- max-width: 100%;
- }
- }
-
- @media screen and (max-width: $no-gap-breakpoint) {
- margin: 0;
- border-top: 1px solid var(--brand-color--med);
-
- & > div {
- width: 100%;
- padding: 0;
- margin-bottom: 0;
- border-bottom: 1px solid var(--brand-color--med);
-
- &:last-child {
- border-bottom: 0;
- }
-
- .card__bar {
- background: var(--brand-color--med);
-
- &:hover,
- &:active,
- &:focus {
- background: var(--brand-color--faint);
- }
- }
- }
- }
- }
}
.container {
@@ -852,495 +358,6 @@ $small-breakpoint: 960px;
}
}
-.information-board {
- background: var(--brand-color--med);
- padding: 20px 0;
-
- .container-alt {
- position: relative;
- padding-right: 280px + 15px;
- }
-
- &__sections {
- display: flex;
- justify-content: space-between;
- flex-wrap: wrap;
- }
-
- &__section {
- flex: 1 0 0;
- font-family: var(--font-sans-serif), sans-serif;
- font-size: 16px;
- line-height: 28px;
- color: var(--primary-text-color);
- text-align: right;
- padding: 10px 15px;
-
- span,
- strong {
- display: block;
- }
-
- span {
- &:last-child {
- color: var(--primary-text-color--faint);
- }
- }
-
- strong {
- font-family: var(--font-display), sans-serif;
- font-weight: 500;
- font-size: 32px;
- line-height: 48px;
- }
-
- @media screen and (max-width: $column-breakpoint) {
- text-align: center;
- }
- }
-
- .panel {
- position: absolute;
- width: 280px;
- box-sizing: border-box;
- background: var(--background-color);
- padding: 20px;
- padding-top: 10px;
- border-radius: 4px 4px 0 0;
- right: 0;
- bottom: -40px;
-
- .panel-header {
- font-family: var(--font-display), sans-serif;
- font-size: 14px;
- line-height: 24px;
- font-weight: 500;
- color: var(--primary-text-color--faint);
- padding-bottom: 5px;
- margin-bottom: 15px;
- border-bottom: 1px solid var(--brand-color--faint);
- text-overflow: ellipsis;
- white-space: nowrap;
- overflow: hidden;
-
- a,
- span {
- font-weight: 400;
- color: var(--primary-text-color);
- }
-
- a {
- text-decoration: none;
- }
- }
- }
-
- .owner {
- text-align: center;
-
- .avatar {
- width: 80px;
- height: 80px;
- margin: 0 auto;
- margin-bottom: 15px;
-
- img {
- display: block;
- width: 80px;
- height: 80px;
- border-radius: 48px;
- }
- }
-
- .name {
- font-size: 14px;
-
- a {
- display: block;
- color: var(--primary-text-color);
- text-decoration: none;
-
- &:hover {
- .display_name {
- text-decoration: underline;
- }
- }
- }
-
- .username {
- display: block;
- color: var(--primary-text-color--faint);
- }
- }
- }
-}
-
-.landing-page {
- p,
- li {
- font-family: var(--font-sans-serif), sans-serif;
- font-size: 16px;
- font-weight: 400;
- font-size: 16px;
- line-height: 30px;
- margin-bottom: 12px;
- color: var(--primary-text-color--faint);
-
- a {
- color: var(--highlight-text-color);
- text-decoration: underline;
- }
- }
-
- em {
- display: inline;
- margin: 0;
- padding: 0;
- font-weight: 700;
- background: transparent;
- font-family: inherit;
- font-size: inherit;
- line-height: inherit;
- color: var(--primary-text-color);
- }
-
- h1 {
- font-family: var(--font-display), sans-serif;
- font-size: 26px;
- line-height: 30px;
- font-weight: 500;
- margin-bottom: 20px;
- color: var(--primary-text-color--faint);
-
- small {
- font-family: var(--font-sans-serif), sans-serif;
- display: block;
- font-size: 18px;
- font-weight: 400;
- color: var(--primary-text-color);
- }
- }
-
- h2 {
- font-family: var(--font-display), sans-serif;
- font-size: 22px;
- line-height: 26px;
- font-weight: 500;
- margin-bottom: 20px;
- color: var(--primary-text-color--faint);
- }
-
- h3 {
- font-family: var(--font-display), sans-serif;
- font-size: 18px;
- line-height: 24px;
- font-weight: 500;
- margin-bottom: 20px;
- color: var(--primary-text-color--faint);
- }
-
- h4 {
- font-family: var(--font-display), sans-serif;
- font-size: 16px;
- line-height: 24px;
- font-weight: 500;
- margin-bottom: 20px;
- color: var(--primary-text-color--faint);
- }
-
- h5 {
- font-family: var(--font-display), sans-serif;
- font-size: 14px;
- line-height: 24px;
- font-weight: 500;
- margin-bottom: 20px;
- color: var(--primary-text-color--faint);
- }
-
- h6 {
- font-family: var(--font-display), sans-serif;
- font-size: 12px;
- line-height: 24px;
- font-weight: 500;
- margin-bottom: 20px;
- color: var(--primary-text-color--faint);
- }
-
- ul,
- ol {
- margin-left: 20px;
-
- &[type='a'] {
- list-style-type: lower-alpha;
- }
-
- &[type='i'] {
- list-style-type: lower-roman;
- }
- }
-
- ul {
- list-style: disc;
- }
-
- ol {
- list-style: decimal;
- }
-
- li > ol,
- li > ul {
- margin-top: 6px;
- }
-
- hr {
- width: 100%;
- height: 0;
- border: 0;
- border-bottom: 1px solid hsla(var(--background-color_hsl), .6);
- margin: 20px 0;
-
- &.spacer {
- height: 1px;
- border: 0;
- }
- }
-
- &__information,
- &__forms {
- padding: 20px;
- }
-
- &__call-to-action {
- background: var(--brand-color--med);
- border-radius: 4px;
- padding: 25px 40px;
- overflow: hidden;
- box-sizing: border-box;
-
- .row {
- width: 100%;
- display: flex;
- flex-direction: row-reverse;
- flex-wrap: nowrap;
- justify-content: space-between;
- align-items: center;
- }
-
- .row__information-board {
- display: flex;
- justify-content: flex-end;
- align-items: flex-end;
-
- .information-board__section {
- flex: 1 0 auto;
- padding: 0 10px;
- }
-
- @media screen and (max-width: $no-gap-breakpoint) {
- width: 100%;
- justify-content: space-between;
- }
- }
-
- .row__mascot {
- flex: 1;
- margin: 10px -50px 0 0;
-
- @media screen and (max-width: $no-gap-breakpoint) {
- display: none;
- }
- }
- }
-
- &__logo {
- margin-right: 20px;
-
- img {
- height: 50px;
- width: auto;
- mix-blend-mode: lighten;
- }
- }
-
- &__information {
- padding: 45px 40px;
- margin-bottom: 10px;
-
- &:last-child {
- margin-bottom: 0;
- }
-
- strong {
- font-weight: 500;
- color: var(--primary-text-color);
- }
-
- .account {
- border-bottom: 0;
- padding: 0;
-
- &__display-name {
- align-items: center;
- display: flex;
- margin-right: 5px;
- }
-
- div.account__display-name {
- &:hover {
- .display-name strong {
- text-decoration: none;
- }
- }
-
- .account__avatar {
- cursor: default;
- }
- }
-
- &__avatar-wrapper {
- margin-left: 0;
- flex: 0 0 auto;
- }
-
- &__avatar {
- width: 44px;
- height: 44px;
- background-size: 44px 44px;
- }
-
- .display-name {
- font-size: 15px;
-
- &__account {
- font-size: 14px;
- }
- }
- }
-
- @media screen and (max-width: $small-breakpoint) {
- .contact {
- margin-top: 30px;
- }
- }
-
- @media screen and (max-width: $column-breakpoint) {
- padding: 25px 20px;
- }
- }
-
- &__information,
- &__forms,
- #soapbox-timeline {
- box-sizing: border-box;
- background: var(--brand-color--med);
- border-radius: 4px;
- box-shadow: 0 0 6px rgba(#000000, 0.1);
- }
-
- &__mascot {
- height: 104px;
- position: relative;
- left: -40px;
- bottom: 25px;
-
- img {
- height: 190px;
- width: auto;
- }
- }
-
- &__short-description {
- .row {
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- margin-bottom: 40px;
- }
-
- @media screen and (max-width: $column-breakpoint) {
- .row {
- margin-bottom: 20px;
- }
- }
-
- p a {
- color: var(--primary-text-color--faint);
- }
-
- h1 {
- font-weight: 500;
- color: var(--primary-text-color);
- margin-bottom: 0;
-
- small {
- color: var(--primary-text-color--faint);
-
- span {
- color: var(--primary-text-color--faint);
- }
- }
- }
-
- p:last-child {
- margin-bottom: 0;
- }
- }
-
- &__hero {
- margin-bottom: 10px;
-
- img {
- display: block;
- margin: 0;
- max-width: 100%;
- height: auto;
- border-radius: 4px;
- }
- }
-
- @media screen and (max-width: 840px) {
- .information-board {
- .container-alt {
- padding-right: 20px;
- }
-
- .panel {
- position: static;
- margin-top: 20px;
- width: 100%;
- border-radius: 4px;
-
- .panel-header {
- text-align: center;
- }
- }
- }
- }
-
- @media screen and (max-width: 675px) {
- .header-wrapper {
- padding-top: 0;
-
- &.compact {
- padding-bottom: 0;
- }
-
- &.compact .hero .heading {
- text-align: initial;
- }
- }
-
- .header .container-alt,
- .features .container-alt {
- display: block;
- }
- }
-
- .cta {
- margin: 20px;
- }
-}
-
.public-layout {
position: relative;
background-color: var(--brand-color);
@@ -1491,67 +508,6 @@ $small-breakpoint: 960px;
border-radius: 0;
}
- .hero-widget {
- margin-top: 30px;
- margin-bottom: 0;
-
- h4 {
- padding: 10px;
- text-transform: uppercase;
- font-weight: 700;
- font-size: 13px;
- color: var(--primary-text-color--faint);
- }
-
- &__text {
- border-radius: 0;
- padding-bottom: 0;
- }
-
- &__footer {
- background: var(--brand-color--med);
- padding: 10px;
- border-radius: 0 0 4px 4px;
- display: flex;
-
- &__column {
- flex: 1 1 50%;
- }
- }
-
- .account {
- padding: 10px 0;
- border-bottom: 0;
-
- .account__display-name {
- display: flex;
- align-items: center;
- }
-
- .account__avatar {
- width: 44px;
- height: 44px;
- background-size: 44px 44px;
- }
- }
-
- &__counter {
- padding: 10px;
-
- strong {
- font-family: var(--font-display), sans-serif;
- font-size: 15px;
- font-weight: 700;
- display: block;
- }
-
- span {
- font-size: 14px;
- color: var(--primary-text-color--faint);
- }
- }
- }
-
.simple_form .user_agreement .label_input > label {
font-weight: 400;
color: var(--primary-text-color--faint);
@@ -1580,18 +536,6 @@ $small-breakpoint: 960px;
grid-row: 1;
display: flex;
flex-direction: column;
-
- .box-widget {
- order: 2;
- flex: 0 0 auto;
- }
-
- .hero-widget {
- margin-top: 0;
- margin-bottom: 10px;
- order: 1;
- flex: 0 0 auto;
- }
}
&__column-registration {
@@ -1605,42 +549,6 @@ $small-breakpoint: 960px;
@media screen and (max-width: $no-gap-breakpoint) {
grid-gap: 0;
-
- .hero-widget {
- display: block;
- margin-bottom: 0;
- box-shadow: none;
-
- &__img,
- &__img img,
- &__footer {
- border-radius: 0;
- }
- }
-
- .hero-widget,
- .box-widget,
- .directory__tag {
- border-bottom: 1px solid var(--brand-color--med);
- }
-
- .directory {
- margin-top: 0;
-
- &__tag {
- margin-bottom: 0;
-
- & > a,
- & > div {
- border-radius: 0;
- box-shadow: none;
- }
-
- &:last-child {
- border-bottom: 0;
- }
- }
- }
}
}
}
@@ -1697,10 +605,6 @@ $small-breakpoint: 960px;
}
}
-.public-layout pre.canary {
- white-space: pre-wrap;
-}
-
.about-page {
background: var(--brand-color--faint);
border-radius: inherit;
diff --git a/app/styles/accounts.scss b/app/styles/accounts.scss
index d9bbe2283..5c70a64d3 100644
--- a/app/styles/accounts.scss
+++ b/app/styles/accounts.scss
@@ -177,31 +177,6 @@
}
}
-.nothing-here {
- background: var(--brand-color--med);
- box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
- color: var(--primary-text-color--faint);
- font-size: 14px;
- font-weight: 500;
- text-align: center;
- display: flex;
- justify-content: center;
- align-items: center;
- cursor: default;
- border-radius: 4px;
- padding: 20px;
- min-height: 30vh;
-
- &--under-tabs {
- border-radius: 0 0 4px 4px;
- }
-
- &--flexible {
- box-sizing: border-box;
- min-height: 100%;
- }
-}
-
.account-role {
display: inline-block;
padding: 4px 6px;
@@ -227,104 +202,6 @@
}
}
-.account__header__fields {
- padding: 0;
- margin: 15px -15px -15px;
- border: 0 none;
- border-top: 1px solid var(--brand-color--med);
- border-bottom: 1px solid var(--brand-color--med);
- font-size: 14px;
- line-height: 20px;
-
- dl {
- display: flex;
- border-bottom: 1px solid var(--brand-color--med);
- }
-
- dt,
- dd {
- box-sizing: border-box;
- padding: 14px;
- text-align: center;
- max-height: 48px;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- }
-
- dt {
- font-weight: 500;
- width: 120px;
- flex: 0 0 auto;
- color: var(--primary-text-color--faint);
- background: hsla(var(--background-color_hsl), 0.5);
- }
-
- dd {
- flex: 1 1 auto;
- color: var(--primary-text-color--faint);
- }
-
- a {
- color: var(--highlight-text-color);
- text-decoration: none;
-
- &:hover,
- &:focus,
- &:active {
- text-decoration: underline;
- }
- }
-
- .verified {
- border: 1px solid rgba($valid-value-color, 0.5);
- background: rgba($valid-value-color, 0.25);
-
- a {
- color: $valid-value-color;
- font-weight: 500;
- }
-
- &__mark {
- color: $valid-value-color;
- }
- }
-
- dl:last-child {
- border-bottom: 0;
- }
-}
-
-.directory__tag .trends__item__current {
- width: auto;
-}
-
-.pending-account {
- &__header {
- color: var(--primary-text-color--faint);
-
- a {
- color: var(--background-color);
- text-decoration: none;
-
- &:hover,
- &:active,
- &:focus {
- text-decoration: underline;
- }
- }
-
- strong {
- color: var(--primary-text-color);
- font-weight: 700;
- }
- }
-
- &__body {
- margin-top: 10px;
- }
-}
-
.account {
padding: 10px;
position: relative;
diff --git a/app/styles/application.scss b/app/styles/application.scss
index cb171dffd..06e2921d7 100644
--- a/app/styles/application.scss
+++ b/app/styles/application.scss
@@ -5,22 +5,16 @@
@import 'reset';
@import 'basics';
@import 'containers';
-@import 'lists';
@import 'footer';
-@import 'compact_header';
-@import 'widgets';
@import 'forms';
@import 'accounts';
-@import 'stream_entries';
@import 'boost';
@import 'loading';
@import 'ui';
@import 'polls';
-@import 'introduction';
+// @import 'introduction';
@import 'emoji_picker';
@import 'about';
-@import 'tables';
-@import 'dashboard';
@import 'rtl';
@import 'accessibility';
@import 'donations';
@@ -38,10 +32,10 @@
@import 'components/account-header';
@import 'components/user-panel';
@import 'components/compose-form';
-@import 'components/group-card';
-@import 'components/group-detail';
-@import 'components/group-form';
-@import 'components/group-sidebar-panel';
+// @import 'components/group-card';
+// @import 'components/group-detail';
+// @import 'components/group-form';
+// @import 'components/group-sidebar-panel';
@import 'components/sidebar-menu';
@import 'components/hotkeys-modal';
@import 'components/emoji-reacts';
diff --git a/app/styles/basics.scss b/app/styles/basics.scss
index 092f06202..4a5c8916b 100644
--- a/app/styles/basics.scss
+++ b/app/styles/basics.scss
@@ -176,13 +176,6 @@ body {
margin-top: 1em;
}
- &__dismiss {
- display: inline-block;
- text-transform: uppercase;
- margin-left: 5px;
- font-size: 13px;
- }
-
a {
color: var(--brand-color--hicontrast);
text-decoration: underline;
diff --git a/app/styles/chats.scss b/app/styles/chats.scss
index e6fc5d601..605ab0048 100644
--- a/app/styles/chats.scss
+++ b/app/styles/chats.scss
@@ -5,7 +5,7 @@
bottom: 0;
right: 20px;
width: 265px;
- height: 265px;
+ height: 350px;
max-height: calc(100vh - 70px);
display: flex;
flex-direction: column;
diff --git a/app/styles/compact_header.scss b/app/styles/compact_header.scss
deleted file mode 100644
index 3f6fc003e..000000000
--- a/app/styles/compact_header.scss
+++ /dev/null
@@ -1,34 +0,0 @@
-.compact-header {
- h1 {
- font-size: 24px;
- line-height: 28px;
- color: var(--primary-text-color--faint);
- font-weight: 500;
- margin-bottom: 20px;
- padding: 0 10px;
- word-wrap: break-word;
-
- @media screen and (max-width: 740px) {
- text-align: center;
- padding: 20px 10px 0;
- }
-
- a {
- color: inherit;
- text-decoration: none;
- }
-
- small {
- font-weight: 400;
- color: var(--primary-text-color--faint);
- }
-
- img {
- display: inline-block;
- margin-bottom: -5px;
- margin-right: 15px;
- width: 36px;
- height: 36px;
- }
- }
-}
diff --git a/app/styles/components/detailed-status.scss b/app/styles/components/detailed-status.scss
index ceab5899c..5bf63db28 100644
--- a/app/styles/components/detailed-status.scss
+++ b/app/styles/components/detailed-status.scss
@@ -114,6 +114,10 @@
color: var(--primary-text-color);
}
+ span.hover-ref-wrapper {
+ display: inline;
+ }
+
.display-name__account {
display: block;
margin-top: -10px;
diff --git a/app/styles/containers.scss b/app/styles/containers.scss
index 53bd272c2..74eebe61e 100644
--- a/app/styles/containers.scss
+++ b/app/styles/containers.scss
@@ -1,14 +1,3 @@
-.container-alt {
- width: 700px;
- margin: 0 auto;
- margin-top: 40px;
-
- @media screen and (max-width: 740px) {
- width: 100%;
- margin: 0;
- }
-}
-
.logo-container {
margin: 100px auto 50px;
@@ -111,80 +100,3 @@
margin-left: 8px;
}
}
-
-.grid-3 {
- display: grid;
- grid-gap: 10px;
- grid-template-columns: 3fr 1fr;
- grid-auto-columns: 25%;
- grid-auto-rows: max-content;
-
- .column-0 {
- grid-column: 1 / 3;
- grid-row: 1;
- }
-
- .column-1 {
- grid-column: 1;
- grid-row: 2;
- }
-
- .column-2 {
- grid-column: 2;
- grid-row: 2;
- }
-
- .column-3 {
- grid-column: 1 / 3;
- grid-row: 3;
- }
-
- .landing-page__call-to-action {
- min-height: 100%;
- }
-
- @media screen and (max-width: 738px) {
- grid-template-columns: minmax(0, 50%) minmax(0, 50%);
-
- .landing-page__call-to-action {
- padding: 20px;
- display: flex;
- align-items: center;
- justify-content: center;
- }
-
- .row__information-board {
- width: 100%;
- justify-content: center;
- align-items: center;
- }
-
- .row__mascot {
- display: none;
- }
- }
-
- @media screen and (max-width: $no-gap-breakpoint) {
- grid-gap: 0;
- grid-template-columns: minmax(0, 100%);
-
- .column-0 {
- grid-column: 1;
- }
-
- .column-1 {
- grid-column: 1;
- grid-row: 3;
- }
-
- .column-2 {
- grid-column: 1;
- grid-row: 2;
- }
-
- .column-3 {
- grid-column: 1;
- grid-row: 4;
- }
- }
-}
diff --git a/app/styles/dashboard.scss b/app/styles/dashboard.scss
deleted file mode 100644
index 2099c5312..000000000
--- a/app/styles/dashboard.scss
+++ /dev/null
@@ -1,76 +0,0 @@
-.dashboard__counters {
- display: flex;
- flex-wrap: wrap;
- margin: 0 -5px;
- margin-bottom: 20px;
-
- & > div {
- box-sizing: border-box;
- flex: 0 0 33.333%;
- padding: 0 5px;
- margin-bottom: 10px;
-
- & > div,
- & > a {
- padding: 20px;
- background: var(--brand-color--faint);
- border-radius: 4px;
- }
-
- & > a {
- text-decoration: none;
- color: inherit;
- display: block;
-
- &:hover,
- &:focus,
- &:active {
- background: var(--brand-color--med);
- }
- }
- }
-
- &__num,
- &__text {
- text-align: center;
- font-weight: 500;
- font-size: 24px;
- line-height: 21px;
- color: var(--primary-text-color);
- font-family: var(--font-display), sans-serif;
- margin-bottom: 20px;
- line-height: 30px;
- }
-
- &__text {
- font-size: 18px;
- }
-
- &__label {
- font-size: 14px;
- color: var(--primary-text-color--faint);
- text-align: center;
- font-weight: 500;
- }
-}
-
-.dashboard__widgets {
- display: flex;
- flex-wrap: wrap;
- margin: 0 -5px;
-
- & > div {
- flex: 0 0 33.333%;
- margin-bottom: 20px;
-
- & > div {
- padding: 0 5px;
- }
- }
-
- a:not(.name-tag) {
- color: var(--background-color);
- font-weight: 500;
- text-decoration: none;
- }
-}
diff --git a/app/styles/dyslexic.scss b/app/styles/dyslexic.scss
index 9dd348fec..6db2cadc2 100644
--- a/app/styles/dyslexic.scss
+++ b/app/styles/dyslexic.scss
@@ -1,5 +1,6 @@
.dyslexic {
font-family: 'OpenDyslexic' !important;
+ margin-bottom: 8px;
}
body.dyslexic {
diff --git a/app/styles/forms.scss b/app/styles/forms.scss
index 395a19a0b..7db0de5a6 100644
--- a/app/styles/forms.scss
+++ b/app/styles/forms.scss
@@ -13,7 +13,7 @@ code {
.simple_form {
.input {
- margin-bottom: 15px;
+ margin-bottom: 8px;
overflow: hidden;
&.hidden {
@@ -43,7 +43,6 @@ code {
&.boolean {
position: relative;
- margin-bottom: 0;
.label_input > label {
font-family: inherit;
@@ -111,7 +110,6 @@ code {
span.hint {
display: block;
font-size: 12px;
- margin-top: 4px;
}
p.hint {
@@ -172,15 +170,10 @@ code {
font-size: 14px;
color: var(--primary-text-color);
display: block;
- margin-bottom: 8px;
word-wrap: break-word;
font-weight: 500;
}
- .hint {
- margin-top: 6px;
- }
-
ul {
flex: 390px;
}
@@ -446,6 +439,7 @@ code {
}
select {
+ appearance: none;
box-sizing: border-box;
font-size: 16px;
color: var(--primary-text-color);
@@ -461,6 +455,23 @@ code {
padding-right: 30px;
height: 41px;
position: relative;
+ margin-top: 8px;
+ cursor: pointer;
+ }
+
+ .select-wrapper::after {
+ display: block;
+ font-family: 'ForkAwesome';
+ content: '';
+ width: 10px;
+ position: absolute;
+ right: 12px;
+ top: 1px;
+ border-left: 1px solid var(--highlight-text-color);
+ height: 39px;
+ padding: 12px;
+ box-sizing: border-box;
+ pointer-events: none;
}
.label_input {
@@ -645,155 +656,6 @@ code {
font-size: 24px;
}
-.flash-message {
- background: var(--brand-color--med);
- color: var(--primary-text-color--faint);
- border-radius: 4px;
- padding: 15px 10px;
- margin-bottom: 30px;
- text-align: center;
-
- &.notice {
- border: 1px solid rgba($valid-value-color, 0.5);
- background: rgba($valid-value-color, 0.25);
- color: $valid-value-color;
- }
-
- &.alert {
- border: 1px solid rgba($error-value-color, 0.5);
- background: rgba($error-value-color, 0.25);
- color: $error-value-color;
- }
-
- a {
- display: inline-block;
- color: var(--primary-text-color--faint);
- text-decoration: none;
-
- &:hover {
- color: var(--primary-text-color);
- text-decoration: underline;
- }
- }
-
- p {
- margin-bottom: 15px;
- }
-
- .oauth-code {
- outline: 0;
- box-sizing: border-box;
- display: block;
- width: 100%;
- border: 0;
- padding: 10px;
- font-family: var(--font-monospace), monospace;
- background: var(--brand-color--med);
- color: var(--primary-text-color);
- font-size: 14px;
- margin: 0;
-
- &::-moz-focus-inner {
- border: 0;
- }
-
- &::-moz-focus-inner,
- &:focus,
- &:active {
- outline: 0 !important;
- }
-
- &:focus {
- background: var(--brand-color--faint);
- }
- }
-
- strong {
- font-weight: 500;
-
- @each $lang in $cjk-langs {
- &:lang(#{$lang}) {
- font-weight: 700;
- }
- }
- }
-
- @media screen and (max-width: 740px) and (min-width: 441px) {
- margin-top: 40px;
- }
-}
-
-.form-footer {
- margin-top: 30px;
- text-align: center;
-
- a {
- color: var(--primary-text-color--faint);
- text-decoration: none;
-
- &:hover {
- text-decoration: underline;
- }
- }
-}
-
-.quick-nav {
- list-style: none;
- margin-bottom: 25px;
- font-size: 14px;
-
- li {
- display: inline-block;
- margin-right: 10px;
- }
-
- a {
- color: var(--highlight-text-color);
- text-transform: uppercase;
- text-decoration: none;
- font-weight: 700;
-
- &:hover,
- &:focus,
- &:active {
- color: var(--highlight-text-color);
- }
- }
-}
-
-.oauth-prompt,
-.follow-prompt {
- margin-bottom: 30px;
- color: var(--primary-text-color--faint);
-
- h2 {
- font-size: 16px;
- margin-bottom: 30px;
- text-align: center;
- }
-
- strong {
- color: var(--primary-text-color--faint);
- font-weight: 500;
-
- @each $lang in $cjk-langs {
- &:lang(#{$lang}) {
- font-weight: 700;
- }
- }
- }
-
- @media screen and (max-width: 740px) and (min-width: 441px) {
- margin-top: 40px;
- }
-}
-
-.qr-wrapper {
- display: flex;
- flex-wrap: wrap;
- align-items: flex-start;
-}
-
.qr-code {
flex: 0 0 auto;
background: var(--foreground-color);
@@ -808,35 +670,7 @@ code {
}
}
-.qr-alternative {
- margin-bottom: 20px;
- color: var(--primary-text-color--faint);
- flex: 150px;
-
- samp {
- display: block;
- font-size: 14px;
- }
-}
-
-.table-form {
- p {
- margin-bottom: 15px;
-
- strong {
- font-weight: 500;
-
- @each $lang in $cjk-langs {
- &:lang(#{$lang}) {
- font-weight: 700;
- }
- }
- }
- }
-}
-
-.simple_form,
-.table-form {
+.simple_form {
.warning {
box-sizing: border-box;
background: rgba($error-value-color, 0.5);
@@ -876,173 +710,6 @@ code {
}
}
-.action-pagination {
- display: flex;
- flex-wrap: wrap;
- align-items: center;
-
- .actions,
- .pagination {
- flex: 1 1 auto;
- }
-
- .actions {
- padding: 30px 0;
- padding-right: 20px;
- flex: 0 0 auto;
- }
-}
-
-.post-follow-actions {
- text-align: center;
- color: var(--primary-text-color--faint);
-
- div {
- margin-bottom: 4px;
- }
-}
-
-.alternative-login {
- margin-top: 20px;
- margin-bottom: 20px;
-
- h4 {
- font-size: 16px;
- color: var(--primary-text-color);
- text-align: center;
- margin-bottom: 20px;
- border: 0;
- padding: 0;
- }
-
- .button {
- display: block;
- }
-}
-
-.scope-danger {
- color: $warning-red;
-}
-
-.form_admin_settings_site_short_description,
-.form_admin_settings_site_description,
-.form_admin_settings_site_extended_description,
-.form_admin_settings_site_terms,
-.form_admin_settings_custom_css,
-.form_admin_settings_closed_registrations_message {
- textarea {
- font-family: var(--font-monospace), monospace;
- }
-}
-
-.input-copy {
- background: var(--background-color);
- border: 1px solid var(--background-color);
- border-radius: 4px;
- display: flex;
- align-items: center;
- padding-right: 4px;
- position: relative;
- top: 1px;
- transition: border-color 300ms linear;
-
- &__wrapper {
- flex: 1 1 auto;
- }
-
- input[type=text] {
- background: transparent;
- border: 0;
- padding: 10px;
- font-size: 14px;
- font-family: var(--font-monospace), monospace;
- }
-
- button {
- flex: 0 0 auto;
- margin: 4px;
- text-transform: none;
- font-weight: 400;
- font-size: 14px;
- padding: 7px 18px;
- padding-bottom: 6px;
- width: auto;
- transition: background 300ms linear;
- }
-
- &.copied {
- border-color: $valid-value-color;
- transition: none;
-
- button {
- background: $valid-value-color;
- transition: none;
- }
- }
-}
-
-.connection-prompt {
- margin-bottom: 25px;
-
- .fa-link {
- background-color: var(--brand-color--med);
- border-radius: 100%;
- font-size: 24px;
- padding: 10px;
- }
-
- &__column {
- align-items: center;
- display: flex;
- flex: 1;
- flex-direction: column;
- flex-shrink: 1;
- max-width: 50%;
-
- &-sep {
- align-self: center;
- flex-grow: 0;
- overflow: visible;
- position: relative;
- z-index: 1;
- }
-
- p {
- word-break: break-word;
- }
- }
-
- .account__avatar {
- margin-bottom: 20px;
- }
-
- &__connection {
- background-color: var(--brand-color--med);
- box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
- border-radius: 4px;
- padding: 25px 10px;
- position: relative;
- text-align: center;
-
- &::after {
- background-color: var(--brand-color--med);
- content: '';
- display: block;
- height: 100%;
- left: 50%;
- position: absolute;
- top: 0;
- width: 1px;
- }
- }
-
- &__row {
- align-items: flex-start;
- display: flex;
- flex-direction: row;
- }
-}
-
.columns-area {
form.simple_form {
padding: 15px;
diff --git a/app/styles/holiday/halloween.scss b/app/styles/holiday/halloween.scss
index 8f04e74c1..b63a1ed0f 100644
--- a/app/styles/holiday/halloween.scss
+++ b/app/styles/holiday/halloween.scss
@@ -43,13 +43,13 @@ body.halloween {
}
.app-holder {
- // Black vignette
+ // Vignette
&::before {
background-image: radial-gradient(
circle,
transparent 0%,
transparent 60%,
- #000 100%
+ var(--vignette-color) 100%
);
}
diff --git a/app/styles/lists.scss b/app/styles/lists.scss
deleted file mode 100644
index 6019cd800..000000000
--- a/app/styles/lists.scss
+++ /dev/null
@@ -1,19 +0,0 @@
-.no-list {
- list-style: none;
-
- li {
- display: inline-block;
- margin: 0 5px;
- }
-}
-
-.recovery-codes {
- list-style: none;
- margin: 0 auto;
-
- li {
- font-size: 125%;
- line-height: 1.5;
- letter-spacing: 1px;
- }
-}
diff --git a/app/styles/rtl.scss b/app/styles/rtl.scss
index 848547ee2..e8e59d734 100644
--- a/app/styles/rtl.scss
+++ b/app/styles/rtl.scss
@@ -7,16 +7,6 @@ body.rtl {
padding-right: 15px;
}
- .landing-page__logo {
- margin-right: 0;
- margin-left: 20px;
- }
-
- .landing-page .features-list .features-list__row .visual {
- margin-left: 0;
- margin-right: 15px;
- }
-
.column-link__icon,
.column-header__icon {
margin-right: 0;
@@ -83,23 +73,16 @@ body.rtl {
right: 10px;
}
- .status,
- .activity-stream .status.light {
+ .status {
padding-left: 10px;
padding-right: 68px;
}
- .status__info .status__display-name,
- .activity-stream .status.light .status__display-name {
+ .status__info .status__display-name {
padding-left: 25px;
padding-right: 0;
}
- .activity-stream .pre-header {
- padding-right: 68px;
- padding-left: 0;
- }
-
.status__prepend {
margin-left: 0;
margin-right: 68px;
@@ -110,11 +93,6 @@ body.rtl {
right: -26px;
}
- .activity-stream .pre-header .pre-header__icon {
- left: auto;
- right: 42px;
- }
-
.account__avatar-overlay-overlay {
right: auto;
left: 0;
@@ -125,8 +103,7 @@ body.rtl {
left: 0;
}
- .status__relative-time,
- .activity-stream .status.light .status__header .status__meta {
+ .status__relative-time {
float: left;
}
@@ -256,44 +233,6 @@ body.rtl {
margin-left: 45px;
}
- .landing-page .header-wrapper .mascot {
- right: 60px;
- left: auto;
- }
-
- .landing-page__call-to-action .row__information-board {
- direction: rtl;
- }
-
- .landing-page .header .hero .floats .float-1 {
- left: -120px;
- right: auto;
- }
-
- .landing-page .header .hero .floats .float-2 {
- left: 210px;
- right: auto;
- }
-
- .landing-page .header .hero .floats .float-3 {
- left: 110px;
- right: auto;
- }
-
- .landing-page .header .links .brand img {
- left: 0;
- }
-
- .landing-page .fa-external-link {
- padding-right: 5px;
- padding-left: 0 !important;
- }
-
- .landing-page .features #soapbox-timeline {
- margin-right: 0;
- margin-left: 30px;
- }
-
@media screen and (min-width: 631px) {
.column,
.drawer {
@@ -329,18 +268,6 @@ body.rtl {
}
}
- .landing-page__information {
- .account__display-name {
- margin-right: 0;
- margin-left: 5px;
- }
-
- .account__avatar-wrapper {
- margin-left: 12px;
- margin-right: 0;
- }
- }
-
.card__bar .display-name {
margin-left: 0;
margin-right: 15px;
diff --git a/app/styles/stream_entries.scss b/app/styles/stream_entries.scss
deleted file mode 100644
index 653454ae2..000000000
--- a/app/styles/stream_entries.scss
+++ /dev/null
@@ -1,159 +0,0 @@
-.activity-stream {
- box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
- border-radius: 4px;
- overflow: hidden;
- margin-bottom: 10px;
-
- @media screen and (max-width: $no-gap-breakpoint) {
- margin-bottom: 0;
- border-radius: 0;
- box-shadow: none;
- }
-
- &--headless {
- border-radius: 0;
- margin: 0;
- box-shadow: none;
-
- .detailed-status,
- .status {
- border-radius: 0 !important;
- }
- }
-
- div[data-component] {
- width: 100%;
- }
-
- .entry {
- background: var(--brand-color--med);
-
- .detailed-status,
- .status,
- .load-more {
- animation: none;
- }
-
- &:last-child {
- .detailed-status,
- .status,
- .load-more {
- border-bottom: 0;
- border-radius: 0 0 4px 4px;
- }
- }
-
- &:first-child {
- .detailed-status,
- .status,
- .load-more {
- border-radius: 4px 4px 0 0;
- }
-
- &:last-child {
- .detailed-status,
- .status,
- .load-more {
- border-radius: 4px;
- }
- }
- }
-
- @media screen and (max-width: 740px) {
- .detailed-status,
- .status,
- .load-more {
- border-radius: 0 !important;
- }
- }
- }
-
- &--highlighted .entry {
- background: var(--brand-color--med);
- }
-}
-
-.button.logo-button {
- flex: 0 auto;
- font-size: 14px;
- background: var(--brand-color);
- color: #fff;
- text-transform: none;
- line-height: 36px;
- height: auto;
- padding: 3px 15px;
- border: 0;
-
- svg {
- width: 20px;
- height: auto;
- vertical-align: middle;
- margin-right: 5px;
- fill: var(--primary-text-color);
- }
-
- &:active,
- &:focus,
- &:hover {
- background: var(--brand-color--hicontrast);
- }
-
- &:disabled,
- &.disabled {
- &:active,
- &:focus,
- &:hover {
- background: var(--brand-color--med);
- }
- }
-
- &.button--destructive {
- &:active,
- &:focus,
- &:hover {
- background: $error-red;
- }
- }
-
- @media screen and (max-width: $no-gap-breakpoint) {
- svg {
- display: none;
- }
- }
-}
-
-.embed,
-.public-layout {
- .detailed-status {
- padding: 15px;
- }
-
- .status {
- padding: 15px 15px 15px (48px + 15px * 2);
- min-height: 48px + 2px;
-
- &__avatar {
- left: 15px;
- top: 17px;
- }
-
- &__content {
- padding-top: 5px;
- }
-
- &__prepend {
- margin-left: 48px + 15px * 2;
- padding-top: 15px;
- }
-
- &__prepend-icon-wrapper {
- left: -32px;
- }
-
- .media-gallery,
- &__action-bar,
- .video-player {
- margin-top: 10px;
- }
- }
-}
diff --git a/app/styles/tables.scss b/app/styles/tables.scss
deleted file mode 100644
index 8fbe8cca6..000000000
--- a/app/styles/tables.scss
+++ /dev/null
@@ -1,243 +0,0 @@
-.table {
- width: 100%;
- max-width: 100%;
- border-spacing: 0;
- border-collapse: collapse;
-
- th,
- td {
- padding: 8px;
- line-height: 18px;
- vertical-align: top;
- border-top: 1px solid var(--brand-color--med);
- text-align: left;
- background: var(--brand-color--med);
- }
-
- & > thead > tr > th {
- vertical-align: bottom;
- border-bottom: 2px solid var(--brand-color--med);
- border-top: 0;
- font-weight: 500;
- }
-
- & > tbody > tr > th {
- font-weight: 500;
- }
-
- & > tbody > tr:nth-child(odd) > td,
- & > tbody > tr:nth-child(odd) > th {
- background: var(--brand-color--med);
- }
-
- a {
- color: var(--highlight-text-color);
- text-decoration: underline;
-
- &:hover {
- text-decoration: none;
- }
- }
-
- strong {
- font-weight: 500;
-
- @each $lang in $cjk-langs {
- &:lang(#{$lang}) {
- font-weight: 700;
- }
- }
- }
-
- &.inline-table {
- & > tbody > tr:nth-child(odd) {
- & > td,
- & > th {
- background: transparent;
- }
- }
-
- & > tbody > tr:first-child {
- & > td,
- & > th {
- border-top: 0;
- }
- }
- }
-
- &.batch-table {
- & > thead > tr > th {
- background: var(--brand-color--med);
- border-top: 1px solid var(--background-color);
- border-bottom: 1px solid var(--background-color);
-
- &:first-child {
- border-radius: 4px 0 0;
- border-left: 1px solid var(--background-color);
- }
-
- &:last-child {
- border-radius: 0 4px 0 0;
- border-right: 1px solid var(--background-color);
- }
- }
- }
-
- &--invites tbody td {
- vertical-align: middle;
- }
-}
-
-.table-wrapper {
- overflow: auto;
- margin-bottom: 20px;
-}
-
-samp {
- font-family: var(--font-monospace), monospace;
-}
-
-button.table-action-link {
- background: transparent;
- border: 0;
- font: inherit;
-}
-
-button.table-action-link,
-a.table-action-link {
- text-decoration: none;
- display: inline-block;
- margin-right: 5px;
- padding: 0 10px;
- color: var(--primary-text-color--faint);
- font-weight: 500;
-
- &:hover {
- color: var(--primary-text-color);
- }
-
- i.fa {
- font-weight: 400;
- margin-right: 5px;
- }
-
- &:first-child {
- padding-left: 0;
- }
-}
-
-.batch-table {
- &__toolbar,
- &__row {
- display: flex;
-
- &__select {
- box-sizing: border-box;
- padding: 8px 16px;
- cursor: pointer;
- min-height: 100%;
-
- input {
- margin-top: 8px;
- }
-
- &--aligned {
- display: flex;
- align-items: center;
-
- input {
- margin-top: 0;
- }
- }
-
- @media screen and (max-width: $no-gap-breakpoint) {
- display: none;
- }
- }
-
- &__actions,
- &__content {
- padding: 8px 0;
- padding-right: 16px;
- flex: 1 1 auto;
- }
- }
-
- &__toolbar {
- border: 1px solid var(--background-color);
- background: var(--brand-color--med);
- border-radius: 4px 0 0;
- height: 47px;
- align-items: center;
-
- &__actions {
- text-align: right;
- padding-right: 16px - 5px;
- }
-
- @media screen and (max-width: $no-gap-breakpoint) {
- display: none;
- }
- }
-
- &__row {
- border: 1px solid var(--background-color);
- border-top: 0;
- background: var(--brand-color--med);
-
- @media screen and (max-width: $no-gap-breakpoint) {
- &:first-child {
- border-top: 1px solid var(--background-color);
- }
- }
-
- &:hover {
- background: var(--background-color);
- }
-
- &:nth-child(even) {
- background: var(--brand-color--med);
-
- &:hover {
- background: var(--brand-color--faint);
- }
- }
-
- &__content {
- padding-top: 12px;
- padding-bottom: 16px;
-
- &--unpadded {
- padding: 0;
- }
- }
- }
-
- .status__content {
- padding-top: 0;
-
- summary {
- display: list-item;
- }
-
- strong {
- font-weight: 700;
- }
- }
-
- .nothing-here {
- border: 1px solid var(--background-color);
- border-top: 0;
- box-shadow: none;
-
- @media screen and (max-width: $no-gap-breakpoint) {
- border-top: 1px solid var(--background-color);
- }
- }
-
- @media screen and (max-width: 870px) {
- .accounts-table tbody td.optional {
- display: none;
- }
- }
-}
diff --git a/app/styles/themes.scss b/app/styles/themes.scss
index 13d952b17..a670b3086 100644
--- a/app/styles/themes.scss
+++ b/app/styles/themes.scss
@@ -64,6 +64,7 @@ body.theme-mode-light {
var(--brand-color_s),
calc(var(--brand-color_l) - 8%)
);
+ --vignette-color: transparent;
// Meta-variables
--primary-text-color_h: 0;
@@ -92,6 +93,7 @@ body.theme-mode-dark {
var(--brand-color_s),
calc(var(--brand-color_l) + 8%)
);
+ --vignette-color: #000;
// Meta-variables
--primary-text-color_h: 0;
diff --git a/app/styles/ui.scss b/app/styles/ui.scss
index a8f09663a..2d4d54f71 100644
--- a/app/styles/ui.scss
+++ b/app/styles/ui.scss
@@ -219,12 +219,6 @@
display: flex;
}
-.domain_buttons {
- height: 18px;
- padding: 10px;
- white-space: nowrap;
-}
-
.muted {
.status__content p,
.status__content a {
@@ -670,52 +664,6 @@
100% { opacity: 1; }
}
-.layout-toggle {
- display: flex;
- padding: 5px;
-
- button {
- box-sizing: border-box;
- flex: 0 0 50%;
- background: transparent;
- padding: 5px;
- border: 0;
- position: relative;
-
- &:hover,
- &:focus,
- &:active {
- svg path:first-child {
- fill: var(--background-color);
- }
- }
- }
-
- svg {
- width: 100%;
- height: auto;
-
- path:first-child {
- fill: var(--brand-color--med);
- }
-
- path:last-child {
- fill: var(--background-color);
- }
- }
-
- &__active {
- color: var(--brand-color);
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- background: var(--brand-color--med);
- border-radius: 50%;
- padding: 0.35rem;
- }
-}
-
.verified-icon {
display: inline-block;
margin: 0 4px 0 1px;
diff --git a/app/styles/widgets.scss b/app/styles/widgets.scss
deleted file mode 100644
index 92e8c7121..000000000
--- a/app/styles/widgets.scss
+++ /dev/null
@@ -1,534 +0,0 @@
-.hero-widget {
- margin-bottom: 10px;
- box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
-
- &__img {
- width: 100%;
- position: relative;
- overflow: hidden;
- border-radius: 4px 4px 0 0;
- background: $base-shadow-color;
-
- img {
- object-fit: cover;
- display: block;
- width: 100%;
- height: 100%;
- margin: 0;
- border-radius: 4px 4px 0 0;
- }
- }
-
- &__text {
- background: var(--brand-color--med);
- padding: 20px;
- border-radius: 0 0 4px 4px;
- font-size: 15px;
- color: var(--primary-text-color--faint);
- line-height: 20px;
- word-wrap: break-word;
- font-weight: 400;
-
- .emojione {
- width: 20px;
- height: 20px;
- margin: -3px 0 0;
- }
-
- p {
- margin-bottom: 20px;
-
- &:last-child {
- margin-bottom: 0;
- }
- }
-
- em {
- display: inline;
- margin: 0;
- padding: 0;
- font-weight: 700;
- background: transparent;
- font-family: inherit;
- font-size: inherit;
- line-height: inherit;
- color: var(--primary-text-color);
- }
-
- a {
- color: var(--primary-text-color--faint);
- text-decoration: none;
-
- &:hover {
- text-decoration: underline;
- }
- }
- }
-
- @media screen and (max-width: $no-gap-breakpoint) {
- display: none;
- }
-}
-
-.endorsements-widget {
- margin-bottom: 10px;
- padding-bottom: 10px;
-
- h4 {
- padding: 10px;
- text-transform: uppercase;
- font-weight: 700;
- font-size: 13px;
- color: var(--primary-text-color--faint);
- }
-
- .account {
- padding: 10px 0;
-
- &:last-child {
- border-bottom: 0;
- }
-
- .account__display-name {
- display: flex;
- align-items: center;
- }
-
- .account__avatar {
- width: 44px;
- height: 44px;
- background-size: 44px 44px;
- }
- }
-}
-
-.box-widget {
- padding: 20px;
- border-radius: 4px;
- background: var(--background-color);
- box-shadow: 0 0 1px 1px rgba($base-shadow-color, 0.2);
-}
-
-.contact-widget,
-.landing-page__information.contact-widget {
- box-sizing: border-box;
- padding: 20px;
- min-height: 100%;
- border-radius: 4px;
- background: var(--brand-color--med);
- box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
-}
-
-.contact-widget {
- font-size: 15px;
- color: var(--primary-text-color--faint);
- line-height: 20px;
- word-wrap: break-word;
- font-weight: 400;
-
- strong {
- font-weight: 500;
- }
-
- p {
- margin-bottom: 10px;
-
- &:last-child {
- margin-bottom: 0;
- }
- }
-
- &__mail {
- margin-top: 10px;
-
- a {
- color: var(--primary-text-color);
- text-decoration: none;
- }
- }
-}
-
-.moved-account-widget {
- padding: 15px;
- padding-bottom: 20px;
- border-radius: 4px;
- background: var(--brand-color--med);
- box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
- color: var(--primary-text-color--faint);
- font-weight: 400;
- margin-bottom: 10px;
-
- strong,
- a {
- font-weight: 500;
-
- @each $lang in $cjk-langs {
- &:lang(#{$lang}) {
- font-weight: 700;
- }
- }
- }
-
- a {
- color: inherit;
- text-decoration: underline;
-
- &.mention {
- text-decoration: none;
-
- span {
- text-decoration: none;
- }
-
- &:focus,
- &:hover,
- &:active {
- text-decoration: none;
-
- span {
- text-decoration: underline;
- }
- }
- }
- }
-
- &__message {
- margin-bottom: 15px;
-
- .fa {
- margin-right: 5px;
- color: var(--primary-text-color--faint);
- }
- }
-
- &__card {
- .detailed-status__display-avatar {
- position: relative;
- cursor: pointer;
- }
-
- .detailed-status__display-name {
- margin-bottom: 0;
- text-decoration: none;
-
- span {
- font-weight: 400;
- }
- }
- }
-}
-
-.memoriam-widget {
- padding: 20px;
- border-radius: 4px;
- background: $base-shadow-color;
- box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
- font-size: 14px;
- color: var(--primary-text-color--faint);
- margin-bottom: 10px;
-}
-
-.page-header {
- background: var(--brand-color--med);
- box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
- border-radius: 4px;
- padding: 60px 15px;
- text-align: center;
- margin: 10px 0;
-
- h1 {
- color: var(--primary-text-color);
- font-size: 36px;
- line-height: 1.1;
- font-weight: 700;
- margin-bottom: 10px;
- }
-
- p {
- font-size: 15px;
- color: var(--primary-text-color--faint);
- }
-
- @media screen and (max-width: $no-gap-breakpoint) {
- margin-top: 0;
- background: var(--brand-color--faint);
-
- h1 {
- font-size: 24px;
- }
- }
-}
-
-.directory {
- background: var(--brand-color--med);
- border-radius: 4px;
- box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
-
- &__tag {
- box-sizing: border-box;
- margin-bottom: 10px;
-
- & > a,
- & > div {
- display: flex;
- align-items: center;
- justify-content: space-between;
- background: var(--brand-color--med);
- border-radius: 4px;
- padding: 15px;
- text-decoration: none;
- color: inherit;
- box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
- }
-
- & > a {
- &:hover,
- &:active,
- &:focus {
- background: var(--brand-color--med);
- }
- }
-
- &.active > a {
- background: var(--brand-color);
- cursor: default;
- }
-
- &.disabled > div {
- opacity: 0.5;
- cursor: default;
- }
-
- h4 {
- flex: 1 1 auto;
- font-size: 18px;
- font-weight: 700;
- color: var(--primary-text-color);
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-
- .fa {
- color: var(--primary-text-color--faint);
- }
-
- small {
- display: block;
- font-weight: 400;
- font-size: 15px;
- margin-top: 8px;
- color: var(--primary-text-color--faint);
- }
- }
-
- &.active h4 {
- &,
- .fa,
- small {
- color: var(--primary-text-color);
- }
- }
-
- .avatar-stack {
- flex: 0 0 auto;
- width: (36px + 4px) * 3;
- }
-
- &.active .avatar-stack .account__avatar {
- border-color: var(--brand-color);
- }
- }
-}
-
-.avatar-stack {
- display: flex;
- justify-content: flex-end;
-
- .account__avatar {
- flex: 0 0 auto;
- width: 36px;
- height: 36px;
- border-radius: 50%;
- position: relative;
- margin-left: -10px;
- background: var(--background-color);
- border: 2px solid var(--brand-color--med);
-
- &:nth-child(1) {
- z-index: 1;
- }
-
- &:nth-child(2) {
- z-index: 2;
- }
-
- &:nth-child(3) {
- z-index: 3;
- }
- }
-}
-
-.accounts-table {
- width: 100%;
-
- .account {
- padding: 0;
- border: 0;
- }
-
- strong {
- font-weight: 700;
- }
-
- thead th {
- text-align: center;
- text-transform: uppercase;
- color: var(--primary-text-color--faint);
- font-weight: 700;
- padding: 10px;
-
- &:first-child {
- text-align: left;
- }
- }
-
- tbody td {
- padding: 15px 0;
- vertical-align: middle;
- border-bottom: 1px solid var(--brand-color--med);
- }
-
- tbody tr:last-child td {
- border-bottom: 0;
- }
-
- &__count {
- width: 120px;
- text-align: center;
- font-size: 15px;
- font-weight: 500;
- color: var(--primary-text-color);
-
- small {
- display: block;
- color: var(--primary-text-color--faint);
- font-weight: 400;
- font-size: 14px;
- }
- }
-
- &__comment {
- width: 50%;
- vertical-align: initial !important;
- }
-
- @media screen and (max-width: $no-gap-breakpoint) {
- tbody td.optional {
- display: none;
- }
- }
-}
-
-.moved-account-widget,
-.memoriam-widget,
-.box-widget,
-.contact-widget,
-.landing-page__information.contact-widget,
-.directory,
-.page-header {
- @media screen and (max-width: $no-gap-breakpoint) {
- margin-bottom: 0;
- box-shadow: none;
- border-radius: 0;
- }
-}
-
-$maximum-width: 1235px;
-$fluid-breakpoint: $maximum-width + 20px;
-
-.statuses-grid {
- min-height: 600px;
-
- @media screen and (max-width: 640px) {
- width: 100% !important; // Masonry layout is unnecessary at this width
- }
-
- &__item {
- width: (960px - 20px) / 3;
-
- @media screen and (max-width: $fluid-breakpoint) {
- width: (940px - 20px) / 3;
- }
-
- @media screen and (max-width: 640px) {
- width: 100%;
- }
-
- @media screen and (max-width: $no-gap-breakpoint) {
- width: 100vw;
- }
- }
-
- .detailed-status {
- border-radius: 4px;
-
- @media screen and (max-width: $no-gap-breakpoint) {
- border-top: 1px solid var(--background-color);
- }
-
- &.compact {
- .detailed-status__meta {
- margin-top: 15px;
- }
-
- .status__content {
- font-size: 15px;
- line-height: 20px;
-
- .emojione {
- width: 20px;
- height: 20px;
- margin: -3px 0 0;
- }
-
- .status__content__spoiler-link {
- line-height: 20px;
- margin: 0;
- }
- }
-
- .media-gallery,
- .status-card,
- .video-player {
- margin-top: 15px;
- }
- }
- }
-}
-
-.notice-widget {
- margin-bottom: 10px;
- color: var(--primary-text-color--faint);
-
- p {
- margin-bottom: 10px;
-
- &:last-child {
- margin-bottom: 0;
- }
- }
-
- a {
- font-size: 14px;
- line-height: 20px;
- text-decoration: none;
- font-weight: 500;
- color: var(--brand-color);
-
- &:hover,
- &:focus,
- &:active {
- text-decoration: underline;
- }
- }
-}
diff --git a/package.json b/package.json
index d42e7b002..f5189186a 100644
--- a/package.json
+++ b/package.json
@@ -58,7 +58,6 @@
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
"babel-runtime": "^6.26.0",
"blurhash": "^1.0.0",
- "chromatism": "^3.0.0",
"classnames": "^2.2.5",
"compression-webpack-plugin": "^3.0.0",
"cross-env": "^6.0.0",
@@ -103,7 +102,6 @@
"prop-types": "^15.5.10",
"punycode": "^2.1.0",
"qrcode.react": "^1.0.0",
- "rails-ujs": "^5.2.3",
"react": "^16.13.1",
"react-color": "^2.18.1",
"react-dom": "^16.13.1",
diff --git a/yarn.lock b/yarn.lock
index e1be9d3a8..51cfcb9d9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3077,11 +3077,6 @@ chownr@^1.1.1:
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494"
integrity sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==
-chromatism@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/chromatism/-/chromatism-3.0.0.tgz#a7249d353c1e4f3577e444ac41171c4e2e624b12"
- integrity sha1-pySdNTweTzV35ESsQRccTi5iSxI=
-
chrome-trace-event@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4"
@@ -9411,11 +9406,6 @@ railroad-diagrams@^1.0.0:
resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e"
integrity sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=
-rails-ujs@^5.2.3:
- version "5.2.3"
- resolved "https://registry.yarnpkg.com/rails-ujs/-/rails-ujs-5.2.3.tgz#4b65ea781a6befe62e96da6362165286a1fe4099"
- integrity sha512-rYgj185MowWFBJI1wdac2FkX4yFYe4+3jJPlB+CTY7a4rmIyg0TqE4vYZmSBBesp7blPUa57oqKzwQjN7eVbEQ==
-
randexp@0.4.6:
version "0.4.6"
resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3"