sforkowany z mirror/soapbox
rodzic
29cdc4867b
commit
f203a4d389
|
@ -0,0 +1,128 @@
|
||||||
|
import { defineMessages } from 'react-intl';
|
||||||
|
import api from '../api';
|
||||||
|
import { importFetchedAccount, importFetchedAccounts } from './importer';
|
||||||
|
import { showAlertForError } from './alerts';
|
||||||
|
import snackbar from './snackbar';
|
||||||
|
import { isLoggedIn } from 'soapbox/utils/auth';
|
||||||
|
import { ME_PATCH_SUCCESS } from './me';
|
||||||
|
|
||||||
|
export const ALIASES_SUGGESTIONS_CHANGE = 'ALIASES_SUGGESTIONS_CHANGE';
|
||||||
|
export const ALIASES_SUGGESTIONS_READY = 'ALIASES_SUGGESTIONS_READY';
|
||||||
|
export const ALIASES_SUGGESTIONS_CLEAR = 'ALIASES_SUGGESTIONS_CLEAR';
|
||||||
|
|
||||||
|
export const ALIASES_ADD_REQUEST = 'ALIASES_ADD_REQUEST';
|
||||||
|
export const ALIASES_ADD_SUCCESS = 'ALIASES_ADD_SUCCESS';
|
||||||
|
export const ALIASES_ADD_FAIL = 'ALIASES_ADD_FAIL';
|
||||||
|
|
||||||
|
export const ALIASES_REMOVE_REQUEST = 'ALIASES_REMOVE_REQUEST';
|
||||||
|
export const ALIASES_REMOVE_SUCCESS = 'ALIASES_REMOVE_SUCCESS';
|
||||||
|
export const ALIASES_REMOVE_FAIL = 'ALIASES_REMOVE_FAIL';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
createSuccess: { id: 'aliases.success.add', defaultMessage: 'Account alias created successfully' },
|
||||||
|
removeSuccess: { id: 'aliases.success.remove', defaultMessage: 'Account alias removed successfully' },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const fetchAliasesSuggestions = q => (dispatch, getState) => {
|
||||||
|
if (!isLoggedIn(getState)) return;
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
q,
|
||||||
|
resolve: true,
|
||||||
|
limit: 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
api(getState).get('/api/v1/accounts/search', { params }).then(({ data }) => {
|
||||||
|
dispatch(importFetchedAccounts(data));
|
||||||
|
dispatch(fetchAliasesSuggestionsReady(q, data));
|
||||||
|
}).catch(error => dispatch(showAlertForError(error)));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchAliasesSuggestionsReady = (query, accounts) => ({
|
||||||
|
type: ALIASES_SUGGESTIONS_READY,
|
||||||
|
query,
|
||||||
|
accounts,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const clearAliasesSuggestions = () => ({
|
||||||
|
type: ALIASES_SUGGESTIONS_CLEAR,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const changeAliasesSuggestions = value => ({
|
||||||
|
type: ALIASES_SUGGESTIONS_CHANGE,
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const addToAliases = (intl, apId) => (dispatch, getState) => {
|
||||||
|
if (!isLoggedIn(getState)) return;
|
||||||
|
|
||||||
|
const alsoKnownAs = getState().getIn(['meta', 'pleroma', 'also_known_as']);
|
||||||
|
|
||||||
|
dispatch(addToAliasesRequest(apId));
|
||||||
|
|
||||||
|
api(getState).patch('/api/v1/accounts/update_credentials', { also_known_as: [...alsoKnownAs, apId] })
|
||||||
|
.then((response => {
|
||||||
|
dispatch(snackbar.success(intl.formatMessage(messages.createSuccess)));
|
||||||
|
dispatch(addToAliasesSuccess(response.data));
|
||||||
|
}))
|
||||||
|
.catch(err => dispatch(addToAliasesFail(err)));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const addToAliasesRequest = (apId) => ({
|
||||||
|
type: ALIASES_ADD_REQUEST,
|
||||||
|
apId,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const addToAliasesSuccess = me => dispatch => {
|
||||||
|
dispatch(importFetchedAccount(me));
|
||||||
|
dispatch({
|
||||||
|
type: ME_PATCH_SUCCESS,
|
||||||
|
me,
|
||||||
|
});
|
||||||
|
dispatch({
|
||||||
|
type: ALIASES_ADD_SUCCESS,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const addToAliasesFail = (apId, error) => ({
|
||||||
|
type: ALIASES_ADD_FAIL,
|
||||||
|
apId,
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const removeFromAliases = (intl, apId) => (dispatch, getState) => {
|
||||||
|
if (!isLoggedIn(getState)) return;
|
||||||
|
|
||||||
|
const alsoKnownAs = getState().getIn(['meta', 'pleroma', 'also_known_as']);
|
||||||
|
|
||||||
|
dispatch(removeFromAliasesRequest(apId));
|
||||||
|
|
||||||
|
api(getState).patch('/api/v1/accounts/update_credentials', { also_known_as: alsoKnownAs.filter(id => id !== apId) })
|
||||||
|
.then(response => {
|
||||||
|
dispatch(snackbar.success(intl.formatMessage(messages.removeSuccess)));
|
||||||
|
dispatch(removeFromAliasesSuccess(response.data));
|
||||||
|
})
|
||||||
|
.catch(err => dispatch(removeFromAliasesFail(apId, err)));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const removeFromAliasesRequest = (apId) => ({
|
||||||
|
type: ALIASES_REMOVE_REQUEST,
|
||||||
|
apId,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const removeFromAliasesSuccess = me => dispatch => {
|
||||||
|
dispatch(importFetchedAccount(me));
|
||||||
|
dispatch({
|
||||||
|
type: ME_PATCH_SUCCESS,
|
||||||
|
me,
|
||||||
|
});
|
||||||
|
dispatch({
|
||||||
|
type: ALIASES_REMOVE_SUCCESS,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const removeFromAliasesFail = (apId, error) => ({
|
||||||
|
type: ALIASES_REMOVE_FAIL,
|
||||||
|
apId,
|
||||||
|
error,
|
||||||
|
});
|
|
@ -33,6 +33,7 @@ const messages = defineMessages({
|
||||||
admin_settings: { id: 'navigation_bar.admin_settings', defaultMessage: 'Admin settings' },
|
admin_settings: { id: 'navigation_bar.admin_settings', defaultMessage: 'Admin settings' },
|
||||||
soapbox_config: { id: 'navigation_bar.soapbox_config', defaultMessage: 'Soapbox config' },
|
soapbox_config: { id: 'navigation_bar.soapbox_config', defaultMessage: 'Soapbox config' },
|
||||||
import_data: { id: 'navigation_bar.import_data', defaultMessage: 'Import data' },
|
import_data: { id: 'navigation_bar.import_data', defaultMessage: 'Import data' },
|
||||||
|
account_aliases: { id: 'navigation_bar.account_aliases', defaultMessage: 'Account aliases' },
|
||||||
security: { id: 'navigation_bar.security', defaultMessage: 'Security' },
|
security: { id: 'navigation_bar.security', defaultMessage: 'Security' },
|
||||||
logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
|
logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
|
||||||
lists: { id: 'column.lists', defaultMessage: 'Lists' },
|
lists: { id: 'column.lists', defaultMessage: 'Lists' },
|
||||||
|
@ -258,6 +259,10 @@ class SidebarMenu extends ImmutablePureComponent {
|
||||||
<Icon id='cloud-upload' />
|
<Icon id='cloud-upload' />
|
||||||
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.import_data)}</span>
|
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.import_data)}</span>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
<NavLink className='sidebar-menu-item' to='/settings/aliases' onClick={this.handleClose}>
|
||||||
|
<Icon id='suitcase' />
|
||||||
|
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.account_aliases)}</span>
|
||||||
|
</NavLink>
|
||||||
<NavLink className='sidebar-menu-item' to='/auth/edit' onClick={this.handleClose}>
|
<NavLink className='sidebar-menu-item' to='/auth/edit' onClick={this.handleClose}>
|
||||||
<Icon id='lock' />
|
<Icon id='lock' />
|
||||||
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.security)}</span>
|
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.security)}</span>
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { makeGetAccount } from '../../../selectors';
|
||||||
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
import Avatar from '../../../components/avatar';
|
||||||
|
import DisplayName from '../../../components/display_name';
|
||||||
|
import IconButton from '../../../components/icon_button';
|
||||||
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
|
import { addToAliases } from '../../../actions/aliases';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
add: { id: 'aliases.account.add', defaultMessage: 'Create alias' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const makeMapStateToProps = () => {
|
||||||
|
const getAccount = makeGetAccount();
|
||||||
|
|
||||||
|
const mapStateToProps = (state, { accountId, added }) => {
|
||||||
|
const account = getAccount(state, accountId);
|
||||||
|
const apId = account.getIn(['pleroma', 'ap_id']);
|
||||||
|
|
||||||
|
return {
|
||||||
|
account,
|
||||||
|
apId,
|
||||||
|
added: typeof added === 'undefined' ? state.getIn(['meta', 'pleroma', 'also_known_as']).includes(apId) : added,
|
||||||
|
me: state.get('me'),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return mapStateToProps;
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
onAdd: (intl, apId) => dispatch(addToAliases(intl, apId)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default @connect(makeMapStateToProps, mapDispatchToProps)
|
||||||
|
@injectIntl
|
||||||
|
class Account extends ImmutablePureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
account: ImmutablePropTypes.map.isRequired,
|
||||||
|
apId: PropTypes.string.isRequired,
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
onAdd: PropTypes.func.isRequired,
|
||||||
|
added: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
added: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
handleOnAdd = () => this.props.onAdd(this.props.intl, this.props.apId);
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { account, accountId, intl, added, me } = this.props;
|
||||||
|
|
||||||
|
let button;
|
||||||
|
|
||||||
|
if (!added && accountId !== me) {
|
||||||
|
button = (
|
||||||
|
<div className='account__relationship'>
|
||||||
|
<IconButton icon='plus' title={intl.formatMessage(messages.add)} onClick={this.handleOnAdd} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='account'>
|
||||||
|
<div className='account__wrapper'>
|
||||||
|
<div className='account__display-name'>
|
||||||
|
<div className='account__avatar-wrapper'><Avatar account={account} size={36} /></div>
|
||||||
|
<DisplayName account={account} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{button}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
|
import { fetchAliasesSuggestions, clearAliasesSuggestions, changeAliasesSuggestions } from '../../../actions/aliases';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import Icon from 'soapbox/components/icon';
|
||||||
|
import Button from 'soapbox/components/button';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
search: { id: 'aliases.search', defaultMessage: 'Search your old account' },
|
||||||
|
searchTitle: { id: 'tabs_bar.search', defaultMessage: 'Search' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapStateToProps = state => ({
|
||||||
|
value: state.getIn(['aliases', 'suggestions', 'value']),
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
onSubmit: value => dispatch(fetchAliasesSuggestions(value)),
|
||||||
|
onClear: () => dispatch(clearAliasesSuggestions()),
|
||||||
|
onChange: value => dispatch(changeAliasesSuggestions(value)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default @connect(mapStateToProps, mapDispatchToProps)
|
||||||
|
@injectIntl
|
||||||
|
class Search extends React.PureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
value: PropTypes.string.isRequired,
|
||||||
|
onChange: PropTypes.func.isRequired,
|
||||||
|
onSubmit: PropTypes.func.isRequired,
|
||||||
|
onClear: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
handleChange = e => {
|
||||||
|
this.props.onChange(e.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKeyUp = e => {
|
||||||
|
if (e.keyCode === 13) {
|
||||||
|
this.props.onSubmit(this.props.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit = () => {
|
||||||
|
this.props.onSubmit(this.props.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClear = () => {
|
||||||
|
this.props.onClear();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { value, intl } = this.props;
|
||||||
|
const hasValue = value.length > 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='aliases_search search'>
|
||||||
|
<label>
|
||||||
|
<span style={{ display: 'none' }}>{intl.formatMessage(messages.search)}</span>
|
||||||
|
|
||||||
|
<input
|
||||||
|
className='search__input'
|
||||||
|
type='text'
|
||||||
|
value={value}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
onKeyUp={this.handleKeyUp}
|
||||||
|
placeholder={intl.formatMessage(messages.search)}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div role='button' tabIndex='0' className='search__icon' onClick={this.handleClear}>
|
||||||
|
<Icon id='search' className={classNames({ active: !hasValue })} />
|
||||||
|
<Icon id='times-circle' aria-label={intl.formatMessage(messages.search)} className={classNames({ active: hasValue })} />
|
||||||
|
</div>
|
||||||
|
<Button onClick={this.handleSubmit}>{intl.formatMessage(messages.searchTitle)}</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
import Column from '../ui/components/column';
|
||||||
|
import ColumnSubheading from '../ui/components/column_subheading';
|
||||||
|
import ScrollableList from '../../components/scrollable_list';
|
||||||
|
import Icon from 'soapbox/components/icon';
|
||||||
|
import Search from './components/search';
|
||||||
|
import Account from './components/account';
|
||||||
|
import { removeFromAliases } from '../../actions/aliases';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
heading: { id: 'column.aliases', defaultMessage: 'Account aliases' },
|
||||||
|
subheading_add_new: { id: 'column.aliases.subheading_add_new', defaultMessage: 'Add New Alias' },
|
||||||
|
create_error: { id: 'column.aliases.create_error', defaultMessage: 'Error creating alias' },
|
||||||
|
delete_error: { id: 'column.aliases.delete_error', defaultMessage: 'Error deleting alias' },
|
||||||
|
subheading_aliases: { id: 'column.aliases.subheading_aliases', defaultMessage: 'Current aliases' },
|
||||||
|
delete: { id: 'column.aliases.delete', defaultMessage: 'Delete' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapStateToProps = state => ({
|
||||||
|
aliases: state.getIn(['meta', 'pleroma', 'also_known_as']),
|
||||||
|
searchAccountIds: state.getIn(['aliases', 'suggestions', 'items']),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default @connect(mapStateToProps)
|
||||||
|
@injectIntl
|
||||||
|
class Aliases extends ImmutablePureComponent {
|
||||||
|
|
||||||
|
handleFilterDelete = e => {
|
||||||
|
const { dispatch, intl } = this.props;
|
||||||
|
dispatch(removeFromAliases(intl, e.currentTarget.dataset.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { intl, aliases, searchAccountIds } = this.props;
|
||||||
|
|
||||||
|
const emptyMessage = <FormattedMessage id='empty_column.aliases' defaultMessage="You haven't created any account alias yet." />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Column className='aliases-settings-panel' icon='suitcase' heading={intl.formatMessage(messages.heading)} backBtnSlim>
|
||||||
|
<ColumnSubheading text={intl.formatMessage(messages.subheading_add_new)} />
|
||||||
|
<Search />
|
||||||
|
<div className='aliases__accounts'>
|
||||||
|
{searchAccountIds.map(accountId => <Account key={accountId} accountId={accountId} />)}
|
||||||
|
</div>
|
||||||
|
<ColumnSubheading text={intl.formatMessage(messages.subheading_aliases)} />
|
||||||
|
<div className='aliases-settings-panel'>
|
||||||
|
<ScrollableList
|
||||||
|
scrollKey='aliases'
|
||||||
|
emptyMessage={emptyMessage}
|
||||||
|
>
|
||||||
|
{aliases.map((alias, i) => (
|
||||||
|
<div key={i} className='alias__container'>
|
||||||
|
<div className='alias__details'>
|
||||||
|
<span className='alias__list-label'><FormattedMessage id='aliases.account_label' defaultMessage='Old account:' /></span>
|
||||||
|
<span className='alias__list-value'>{alias}</span>
|
||||||
|
</div>
|
||||||
|
<div className='alias__delete' role='button' tabIndex='0' onClick={this.handleFilterDelete} data-value={alias} aria-label={intl.formatMessage(messages.delete)}>
|
||||||
|
<Icon className='alias__delete-icon' id='times' size={40} />
|
||||||
|
<span className='alias__delete-label'><FormattedMessage id='aliases.aliases_list_delete' defaultMessage='Unlink alias' /></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</ScrollableList>
|
||||||
|
</div>
|
||||||
|
</Column>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -38,6 +38,7 @@ const LinkFooter = ({ onOpenHotkeys, account, onClickLogOut }) => (
|
||||||
{isAdmin(account) && <li><a href='/pleroma/admin'><FormattedMessage id='navigation_bar.admin_settings' defaultMessage='AdminFE' /></a></li>}
|
{isAdmin(account) && <li><a href='/pleroma/admin'><FormattedMessage id='navigation_bar.admin_settings' defaultMessage='AdminFE' /></a></li>}
|
||||||
{isAdmin(account) && <li><Link to='/soapbox/config'><FormattedMessage id='navigation_bar.soapbox_config' defaultMessage='Soapbox config' /></Link></li>}
|
{isAdmin(account) && <li><Link to='/soapbox/config'><FormattedMessage id='navigation_bar.soapbox_config' defaultMessage='Soapbox config' /></Link></li>}
|
||||||
<li><Link to='/settings/import'><FormattedMessage id='navigation_bar.import_data' defaultMessage='Import data' /></Link></li>
|
<li><Link to='/settings/import'><FormattedMessage id='navigation_bar.import_data' defaultMessage='Import data' /></Link></li>
|
||||||
|
<li><Link to='/settings/aliases'><FormattedMessage id='navigation_bar.account_aliases' defaultMessage='Account aliases' /></Link></li>
|
||||||
<li><a href='#' onClick={onOpenHotkeys}><FormattedMessage id='navigation_bar.keyboard_shortcuts' defaultMessage='Hotkeys' /></a></li>
|
<li><a href='#' onClick={onOpenHotkeys}><FormattedMessage id='navigation_bar.keyboard_shortcuts' defaultMessage='Hotkeys' /></a></li>
|
||||||
</>}
|
</>}
|
||||||
<li><Link to='/about'><FormattedMessage id='navigation_bar.info' defaultMessage='About this server' /></Link></li>
|
<li><Link to='/about'><FormattedMessage id='navigation_bar.info' defaultMessage='About this server' /></Link></li>
|
||||||
|
|
|
@ -98,6 +98,7 @@ import {
|
||||||
ScheduledStatuses,
|
ScheduledStatuses,
|
||||||
UserIndex,
|
UserIndex,
|
||||||
FederationRestrictions,
|
FederationRestrictions,
|
||||||
|
Aliases,
|
||||||
} from './util/async-components';
|
} from './util/async-components';
|
||||||
|
|
||||||
// Dummy import, to make sure that <Status /> ends up in the application bundle.
|
// Dummy import, to make sure that <Status /> ends up in the application bundle.
|
||||||
|
@ -261,6 +262,7 @@ class SwitchingColumnsArea extends React.PureComponent {
|
||||||
<WrappedRoute path='/settings/preferences' page={DefaultPage} component={Preferences} content={children} />
|
<WrappedRoute path='/settings/preferences' page={DefaultPage} component={Preferences} content={children} />
|
||||||
<WrappedRoute path='/settings/profile' page={DefaultPage} component={EditProfile} content={children} />
|
<WrappedRoute path='/settings/profile' page={DefaultPage} component={EditProfile} content={children} />
|
||||||
<WrappedRoute path='/settings/import' page={DefaultPage} component={ImportData} content={children} />
|
<WrappedRoute path='/settings/import' page={DefaultPage} component={ImportData} content={children} />
|
||||||
|
<WrappedRoute path='/settings/aliases' page={DefaultPage} component={Aliases} content={children} />
|
||||||
<WrappedRoute path='/backups' page={DefaultPage} component={Backups} content={children} />
|
<WrappedRoute path='/backups' page={DefaultPage} component={Backups} content={children} />
|
||||||
<WrappedRoute path='/soapbox/config' page={DefaultPage} component={SoapboxConfig} content={children} />
|
<WrappedRoute path='/soapbox/config' page={DefaultPage} component={SoapboxConfig} content={children} />
|
||||||
|
|
||||||
|
|
|
@ -249,3 +249,7 @@ export function UserIndex() {
|
||||||
export function FederationRestrictions() {
|
export function FederationRestrictions() {
|
||||||
return import(/* webpackChunkName: "features/federation_restrictions" */'../../federation_restrictions');
|
return import(/* webpackChunkName: "features/federation_restrictions" */'../../federation_restrictions');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function Aliases() {
|
||||||
|
return import(/* webpackChunkName: "features/aliases" */'../../aliases');
|
||||||
|
}
|
||||||
|
|
|
@ -110,6 +110,12 @@
|
||||||
"alert.unexpected.message": "Wystąpił nieoczekiwany błąd.",
|
"alert.unexpected.message": "Wystąpił nieoczekiwany błąd.",
|
||||||
"alert.unexpected.return_home": "Wróć na stronę główną",
|
"alert.unexpected.return_home": "Wróć na stronę główną",
|
||||||
"alert.unexpected.title": "O nie!",
|
"alert.unexpected.title": "O nie!",
|
||||||
|
"aliases.account_label": "Stare konto:",
|
||||||
|
"aliases.account.add": "Utwórz alias",
|
||||||
|
"aliases.aliases_list_delete": "Odłącz alias",
|
||||||
|
"aliases.search": "Szukaj swojego starego konta",
|
||||||
|
"aliases.success.add": "Pomyślnie utworzono alias konta",
|
||||||
|
"aliases.success.remove": "Pomyślnie usunięto alias konta",
|
||||||
"audio.close": "Zamknij dźwięk",
|
"audio.close": "Zamknij dźwięk",
|
||||||
"audio.expand": "Rozwiń dźwięk",
|
"audio.expand": "Rozwiń dźwięk",
|
||||||
"audio.hide": "Ukryj dźwięk",
|
"audio.hide": "Ukryj dźwięk",
|
||||||
|
@ -143,6 +149,12 @@
|
||||||
"column.admin.moderation_log": "Dziennik moderacyjny",
|
"column.admin.moderation_log": "Dziennik moderacyjny",
|
||||||
"column.admin.reports": "Zgłoszenia",
|
"column.admin.reports": "Zgłoszenia",
|
||||||
"column.admin.reports.menu.moderation_log": "Dziennik moderacji",
|
"column.admin.reports.menu.moderation_log": "Dziennik moderacji",
|
||||||
|
"column.aliases": "Aliasy kont",
|
||||||
|
"column.aliases.create_error": "Błąd tworzenia aliasu",
|
||||||
|
"column.aliases.delete": "Usuń",
|
||||||
|
"column.aliases.delete_error": "Błąd usuwania aliasu",
|
||||||
|
"column.aliases.subheading_add_new": "Dodaj nowy alias",
|
||||||
|
"column.aliases.subheading_aliases": "Istniejące aliasy",
|
||||||
"column.backups": "Kopie zapasowe",
|
"column.backups": "Kopie zapasowe",
|
||||||
"column.blocks": "Zablokowani użytkownicy",
|
"column.blocks": "Zablokowani użytkownicy",
|
||||||
"column.bookmarks": "Załadki",
|
"column.bookmarks": "Załadki",
|
||||||
|
@ -305,6 +317,7 @@
|
||||||
"emoji_button.travel": "Podróże i miejsca",
|
"emoji_button.travel": "Podróże i miejsca",
|
||||||
"empty_column.account_timeline": "Brak wpisów tutaj!",
|
"empty_column.account_timeline": "Brak wpisów tutaj!",
|
||||||
"empty_column.account_unavailable": "Profil niedostępny",
|
"empty_column.account_unavailable": "Profil niedostępny",
|
||||||
|
"empty_column.aliases": "Nie utworzyłeś(-aś) jeszcze żadnego aliasu konta.",
|
||||||
"empty_column.blocks": "Nie zablokowałeś(-aś) jeszcze żadnego użytkownika.",
|
"empty_column.blocks": "Nie zablokowałeś(-aś) jeszcze żadnego użytkownika.",
|
||||||
"empty_column.bookmarks": "Nie masz jeszcze żadnej zakładki. Kiedy dodasz jakąś, pojawi się ona tutaj.",
|
"empty_column.bookmarks": "Nie masz jeszcze żadnej zakładki. Kiedy dodasz jakąś, pojawi się ona tutaj.",
|
||||||
"empty_column.community": "Lokalna oś czasu jest pusta. Napisz coś publicznie, aby zagaić!",
|
"empty_column.community": "Lokalna oś czasu jest pusta. Napisz coś publicznie, aby zagaić!",
|
||||||
|
@ -480,6 +493,7 @@
|
||||||
"morefollows.followers_label": "…i {count} więcej {count, plural, one {obserwujący(-a)} few {obserwujących} many {obserwujących} other {obserwujących}} na zdalnych stronach.",
|
"morefollows.followers_label": "…i {count} więcej {count, plural, one {obserwujący(-a)} few {obserwujących} many {obserwujących} other {obserwujących}} na zdalnych stronach.",
|
||||||
"morefollows.following_label": "…i {count} więcej {count, plural, one {obserwowany(-a)} few {obserwowanych} many {obserwowanych} other {obserwowanych}} na zdalnych stronach.",
|
"morefollows.following_label": "…i {count} więcej {count, plural, one {obserwowany(-a)} few {obserwowanych} many {obserwowanych} other {obserwowanych}} na zdalnych stronach.",
|
||||||
"mute_modal.hide_notifications": "Chcesz ukryć powiadomienia od tego użytkownika?",
|
"mute_modal.hide_notifications": "Chcesz ukryć powiadomienia od tego użytkownika?",
|
||||||
|
"navigation_bar.account_aliases": "Aliasy kont",
|
||||||
"navigation_bar.admin_settings": "Ustawienia administracyjne",
|
"navigation_bar.admin_settings": "Ustawienia administracyjne",
|
||||||
"navigation_bar.blocks": "Zablokowani użytkownicy",
|
"navigation_bar.blocks": "Zablokowani użytkownicy",
|
||||||
"navigation_bar.compose": "Utwórz nowy wpis",
|
"navigation_bar.compose": "Utwórz nowy wpis",
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||||
|
import {
|
||||||
|
ALIASES_SUGGESTIONS_READY,
|
||||||
|
ALIASES_SUGGESTIONS_CLEAR,
|
||||||
|
ALIASES_SUGGESTIONS_CHANGE,
|
||||||
|
} from '../actions/aliases';
|
||||||
|
|
||||||
|
const initialState = ImmutableMap({
|
||||||
|
suggestions: ImmutableMap({
|
||||||
|
value: '',
|
||||||
|
items: ImmutableList(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function aliasesReducer(state = initialState, action) {
|
||||||
|
switch(action.type) {
|
||||||
|
case ALIASES_SUGGESTIONS_CHANGE:
|
||||||
|
return state.setIn(['suggestions', 'value'], action.value);
|
||||||
|
case ALIASES_SUGGESTIONS_READY:
|
||||||
|
return state.setIn(['suggestions', 'items'], ImmutableList(action.accounts.map(item => item.id)));
|
||||||
|
case ALIASES_SUGGESTIONS_CLEAR:
|
||||||
|
return state.update('suggestions', suggestions => suggestions.withMutations(map => {
|
||||||
|
map.set('items', ImmutableList());
|
||||||
|
map.set('value', '');
|
||||||
|
}));
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
};
|
|
@ -53,6 +53,7 @@ import backups from './backups';
|
||||||
import admin_log from './admin_log';
|
import admin_log from './admin_log';
|
||||||
import security from './security';
|
import security from './security';
|
||||||
import scheduled_statuses from './scheduled_statuses';
|
import scheduled_statuses from './scheduled_statuses';
|
||||||
|
import aliases from './aliases';
|
||||||
|
|
||||||
const appReducer = combineReducers({
|
const appReducer = combineReducers({
|
||||||
dropdown_menu,
|
dropdown_menu,
|
||||||
|
@ -107,6 +108,7 @@ const appReducer = combineReducers({
|
||||||
admin_log,
|
admin_log,
|
||||||
security,
|
security,
|
||||||
scheduled_statuses,
|
scheduled_statuses,
|
||||||
|
aliases,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Clear the state (mostly) when the user logs out
|
// Clear the state (mostly) when the user logs out
|
||||||
|
|
|
@ -84,6 +84,7 @@
|
||||||
@import 'components/datepicker';
|
@import 'components/datepicker';
|
||||||
@import 'components/remote-timeline';
|
@import 'components/remote-timeline';
|
||||||
@import 'components/federation-restrictions';
|
@import 'components/federation-restrictions';
|
||||||
|
@import 'components/aliases';
|
||||||
|
|
||||||
// Holiday
|
// Holiday
|
||||||
@import 'holiday/halloween';
|
@import 'holiday/halloween';
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
.aliases {
|
||||||
|
&__accounts {
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.account__display-name {
|
||||||
|
&:hover strong {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.account__avatar {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&_search {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
margin: 10px;
|
||||||
|
|
||||||
|
.search__input {
|
||||||
|
padding: 7px 30px 6px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> label {
|
||||||
|
flex: 1 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .search__icon .fa {
|
||||||
|
top: 8px;
|
||||||
|
right: 102px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .button {
|
||||||
|
width: 80px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.aliases-settings-panel {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.item-list article {
|
||||||
|
border-bottom: 1px solid var(--primary-text-color--faint);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.alias__container {
|
||||||
|
padding: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
span.alias__list-label {
|
||||||
|
padding-right: 5px;
|
||||||
|
color: var(--primary-text-color--faint);
|
||||||
|
}
|
||||||
|
|
||||||
|
span.alias__list-value span {
|
||||||
|
padding-right: 5px;
|
||||||
|
text-transform: capitalize;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: ',';
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-of-type {
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.alias__delete {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
span.alias__delete-label {
|
||||||
|
color: var(--primary-text-color--faint);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alias__delete-icon {
|
||||||
|
background: none;
|
||||||
|
color: var(--primary-text-color--faint);
|
||||||
|
padding: 0 5px;
|
||||||
|
margin: 0 auto;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.slist--flex {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
Ładowanie…
Reference in New Issue