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"