Merge branch 'account-aliases' into 'develop'

Account aliases

See merge request soapbox-pub/soapbox-fe!663
groups^2
Alex Gleason 2021-08-11 17:02:56 +00:00
commit bde53541b7
13 zmienionych plików z 576 dodań i 23 usunięć

Wyświetl plik

@ -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,
});

Wyświetl plik

@ -33,6 +33,7 @@ const messages = defineMessages({
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' },
account_aliases: { id: 'navigation_bar.account_aliases', defaultMessage: 'Account aliases' },
security: { id: 'navigation_bar.security', defaultMessage: 'Security' },
logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
lists: { id: 'column.lists', defaultMessage: 'Lists' },
@ -258,6 +259,10 @@ class SidebarMenu extends ImmutablePureComponent {
<Icon id='cloud-upload' />
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.import_data)}</span>
</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}>
<Icon id='lock' />
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.security)}</span>

Wyświetl plik

@ -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>
);
}
}

Wyświetl plik

@ -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>
);
}
}

Wyświetl plik

@ -0,0 +1,81 @@
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']),
loaded: state.getIn(['aliases', 'suggestions', 'loaded']),
});
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, loaded } = 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 />
{
loaded && searchAccountIds.size === 0 ? (
<div className='aliases__accounts empty-column-indicator'>
<FormattedMessage id='empty_column.aliases.suggestions' defaultMessage='There are no account suggestions available for the provided term.' />
</div>
) : (
<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>
);
}
}

Wyświetl plik

@ -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><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/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><Link to='/about'><FormattedMessage id='navigation_bar.info' defaultMessage='About this server' /></Link></li>

Wyświetl plik

@ -98,6 +98,7 @@ import {
ScheduledStatuses,
UserIndex,
FederationRestrictions,
Aliases,
} from './util/async-components';
// 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/profile' page={DefaultPage} component={EditProfile} 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='/soapbox/config' page={DefaultPage} component={SoapboxConfig} content={children} />

Wyświetl plik

@ -249,3 +249,7 @@ export function UserIndex() {
export function FederationRestrictions() {
return import(/* webpackChunkName: "features/federation_restrictions" */'../../federation_restrictions');
}
export function Aliases() {
return import(/* webpackChunkName: "features/aliases" */'../../aliases');
}

Wyświetl plik

@ -42,7 +42,8 @@
"account.share": "Udostępnij profil @{name}",
"account.show_reblogs": "Pokazuj podbicia od @{name}",
"account.subscribe": "Subskrybuj wpisy @{name}",
"account.unblock": "Odblokuj @{name}",
"account.subscribed": "Subscribed",
"account.unblock": "Zasubskrybowano",
"account.unblock_domain": "Odblokuj domenę {domain}",
"account.unendorse": "Przestań polecać",
"account.unfollow": "Przestań śledzić",
@ -50,8 +51,6 @@
"account.unmute_notifications": "Cofnij wyciszenie powiadomień od @{name}",
"account.unsubscribe": "Przestań subskrybować wpisy @{name}",
"account_gallery.none": "Brak zawartości multimedialnej do wyświetlenia.",
"auth.invalid_credentials": "Nieprawidłowa nazwa użytkownika lub hasło",
"auth.logged_out": "Wylogowano.",
"admin.awaiting_approval.approved_message": "Przyjęto {acct}!",
"admin.awaiting_approval.empty_message": "Nikt nie oczekuje przyjęcia. Gdy zarejestruje się nowy użytkownik, możesz zatwierdzić go tutaj.",
"admin.awaiting_approval.rejected_message": "Odrzucono {acct}!",
@ -95,7 +94,7 @@
"admin.users.actions.promote_to_admin": "Mianuj @{name} administratorem",
"admin.users.actions.promote_to_admin_message": "Mianowano @{acct} administratorem",
"admin.users.actions.promote_to_moderator": "Mianuj @{name} moderatorem",
"admin.users.actions.promote_to_moderatorem_message": "Mianowano @{acct} moderatorem",
"admin.users.actions.promote_to_moderator_message": "Mianowano @{acct} moderatorem",
"admin.users.actions.unverify_user": "Cofnij weryfikację @{name}",
"admin.users.actions.verify_user": "Weryfikuj @{name}",
"admin.users.user_deactivated_message": "Zdezaktywowano @{acct}",
@ -110,13 +109,14 @@
"alert.unexpected.message": "Wystąpił nieoczekiwany błąd.",
"alert.unexpected.return_home": "Wróć na stronę główną",
"alert.unexpected.title": "O nie!",
"audio.close": "Zamknij dźwięk",
"audio.expand": "Rozwiń dźwięk",
"audio.hide": "Ukryj dźwięk",
"audio.mute": "Wycisz",
"audio.pause": "Pauzuj",
"audio.play": "Odtwórz",
"audio.unmute": "Cofnij wyciszenie",
"aliases.account.add": "Utwórz alias",
"aliases.account_label": "Stare konto:",
"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",
"auth.invalid_credentials": "Nieprawidłowa nazwa użytkownika lub hasło",
"auth.logged_out": "Wylogowano.",
"backups.actions.create": "Utwórz kopię zapasową",
"backups.empty_message": "Nie znaleziono kopii zapasaowych. {action}",
"backups.empty_message.action": "Chcesz utworzyć?",
@ -143,14 +143,20 @@
"column.admin.moderation_log": "Dziennik moderacyjny",
"column.admin.reports": "Zgłoszenia",
"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.blocks": "Zablokowani użytkownicy",
"column.bookmarks": "Załadki",
"column.chats": "Rozmowy",
"column.community": "Lokalna oś czasu",
"column.crypto_donate": "Przekaż kryptowalutę",
"column.direct": "Wiadomości bezpośrednie",
"column.domain_blocks": "Ukryte domeny",
"column.crypto_donate": "Przekaż kryptowalutę",
"column.edit_profile": "Edytuj profil",
"column.federation_restrictions": "Ograniczenia federacji",
"column.filters": "Wyciszone słowa",
@ -195,6 +201,7 @@
"column_header.hide_settings": "Ukryj ustawienia",
"column_header.show_settings": "Pokaż ustawienia",
"community.column_settings.media_only": "Tylko zawartość multimedialna",
"compose.invalid_schedule": "Musisz zaplanować wpis przynajmniej 5 minut wcześniej.",
"compose_form.direct_message_warning": "Ten wpis będzie widoczny tylko dla wszystkich wspomnianych użytkowników.",
"compose_form.direct_message_warning_learn_more": "Dowiedz się więcej",
"compose_form.hashtag_warning": "Ten wpis nie będzie widoczny pod podanymi hashtagami, ponieważ jest oznaczony jako niewidoczny. Tylko publiczne wpisy mogą zostać znalezione z użyciem hashtagów.",
@ -250,10 +257,11 @@
"confirmations.reply.message": "W ten sposób utracisz wpis który obecnie tworzysz. Czy na pewno chcesz to zrobić?",
"confirmations.unfollow.confirm": "Przestań śledzić",
"confirmations.unfollow.message": "Czy na pewno zamierzasz przestać śledzić {name}?",
"crypto_donate_panel.actions.more": "Naciśnij, aby zobaczyć {count} more {count, plural, one {portfel} few {portfele} many {portfeli} other {portfeli}}",
"crypto_donate.explanation_box.message": "{siteTitle} przyjmuje darowizny w kryptowalutach. Możesz wysłać darowiznę na jeden z poniższych adresów. Dziękujemy za Wasze wsparcie!",
"crypto_donate_panel.intro.message": "{siteTitle} przyjmuje darowizny w kryptowalutach, aby utrzymać naszą usługę. Dziękujemy za Wasze wsparcie!",
"crypto_donate.explanation_box.title": "Przeaż darowiznę w kryptowalutach",
"crypto_donate_panel.actions.more": "Naciśnij, aby zobaczyć {count} more {count, plural, one {portfel} few {portfele} many {portfeli} other {portfeli}}",
"crypto_donate_panel.heading": "Przekaż kryptowalutę",
"crypto_donate_panel.intro.message": "{siteTitle} przyjmuje darowizny w kryptowalutach, aby utrzymać naszą usługę. Dziękujemy za Wasze wsparcie!",
"datepicker.hint": "Zaplanowano publikację na…",
"donate": "Przekaż darowiznę",
"donate_crypto": "Przekaż kryptowalutę",
@ -277,7 +285,7 @@
"edit_profile.fields.meta_fields_label": "Metadane profilu",
"edit_profile.fields.stranger_notifications_label": "Blokuj powiadomienia od nieznajomych",
"edit_profile.fields.verified_display_name": "Zweryfikowani użytkownicy nie mogą zmieniać nazwy wyświetlanej",
"edit_profile.hints.accepts_email_list_label": "Otrzymuj wiadomości i nowości marketingowe",
"edit_profile.hints.accepts_email_list": "Otrzymuj wiadomości i nowości marketingowe.",
"edit_profile.hints.avatar": "PNG, GIF lub JPG. Maksymalnie 2 MB. Zostanie zmniejszony do 400x400px",
"edit_profile.hints.bot": "To konto podejmuje głównie zautomatyzowane działania i może nie być nadzorowane",
"edit_profile.hints.header": "PNG, GIF lub JPG. Maksymalnie 2 MB. Zostanie zmniejszony do 1500x500px",
@ -306,6 +314,8 @@
"emoji_button.travel": "Podróże i miejsca",
"empty_column.account_timeline": "Brak wpisów tutaj!",
"empty_column.account_unavailable": "Profil niedostępny",
"empty_column.aliases": "Nie utworzyłeś(-aś) jeszcze żadnego aliasu konta.",
"empty_column.aliases.suggestions": "Brak propozycji kont dla podanej frazy.",
"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.community": "Lokalna oś czasu jest pusta. Napisz coś publicznie, aby zagaić!",
@ -334,11 +344,14 @@
"federation_restriction.full_media_removal": "Pełne usunięcie mediów",
"federation_restriction.media_nsfw": "Załączniki oznaczone jako NSFW",
"federation_restriction.partial_media_removal": "Częściowe usunięcie mediów",
"federation_restrictions.empty_message": "{siteTitle} nie nakłada restrykcji na żadne instancje.",
"federation_restrictions.explanation_box.message": "Zwykle serwery w Fediwersum mogą swobodnie porozumiewać się. {siteTitle} nakłada pewne ograniczenia na następujące serwery.",
"federation_restrictions.explanation_box.title": "Zasady dla poszczególnych instancji",
"federation_restrictions.not_disclosed_message": "{siteTitle} nie udostępnia informacji o ograniczeniach federacji przez API.",
"fediverse_tab.explanation_box.dismiss": "Nie pokazuj ponownie",
"fediverse_tab.explanation_box.explanation": "{site_title} jest częścią Fediwersum, sieci społecznościowej na którą składają się tysiące niezależnie funkcjonujących stron (serwerów). Wpisy które tu widzisz pochodzą z serwerów podmiotów trzecich. Możesz do woli wchodzić z nimi w interakcje lub blokować serwery których nie lubisz. Zwracaj uwagę na pełną nazwę użytkownika po znaku @, aby wiedzieć z jakiego serwera pochodzi on. Aby widzieć tylko wpisy z {site_title}, odwiedź kartę {local}.",
"fediverse_tab.explanation_box.title": "Czym jest Fediverse?",
"filters.added": "Dodano filtr.",
"filters.context_header": "Konteksty filtru",
"filters.context_hint": "Jedno lub więcej miejsc, gdzie filtr powinien zostać zaaplikowany",
"filters.filters_list_context_label": "Konteksty filtru:",
@ -348,12 +361,12 @@
"filters.filters_list_hide": "Ukrywaj",
"filters.filters_list_phrase_label": "Słowo kluczowe lub fraza:",
"filters.filters_list_whole-word": "Całe słowo",
"filters.added": "Dodano filtr.",
"filters.deleted": "Usunięto filtr.",
"filters.removed": "Usunięto filtr.",
"follow_request.authorize": "Autoryzuj",
"follow_request.reject": "Odrzuć",
"forms.copy": "Kopiuj",
"getting_started.open_source_notice": "{code_name} jest oprogramowaniem o otwartym źródle. Możesz pomóc w rozwoju lub zgłaszać błędy na GitLabie tutaj: {code_link} (v{code_version}).",
"group.detail.archived_group": "Zarchiwizowana grupa",
"group.members.empty": "Ta grupa nie ma żadnych członków.",
"group.removed_accounts.empty": "Ta grupa nie ma żadnych usuniętych kont.",
"groups.card.join": "Dołącz",
@ -362,13 +375,21 @@
"groups.card.roles.member": "Jesteś członkiem",
"groups.card.view": "Zobacz",
"groups.create": "Utwórz grupę",
"groups.detail.role_admin": "Jesteś administratorem",
"groups.edit": "Edytuj",
"groups.form.coverImage": "Wyślij obraz baneru (nieobowiązkowe)",
"groups.form.coverImageChange": "Wybrano obraz baneru",
"groups.form.create": "Utwórz grupę",
"groups.form.description": "Opis",
"groups.form.title": "Tytuł",
"groups.form.update": "Aktualizuj grupę",
"groups.join": "Dołącz do grupy",
"groups.leave": "Opuść grupę",
"groups.removed_accounts": "Usunięte konta",
"groups.sidebar-panel.item.no_recent_activity": "Brak ostatniej aktywności",
"groups.sidebar-panel.item.view": "nowe wpisy",
"groups.sidebar-panel.show_all": "Pokaż wszystkie",
"groups.sidebar-panel.title": "Grupy do których należysz",
"groups.tab_admin": "Zarządzaj",
"groups.tab_featured": "Wyróżnione",
"groups.tab_member": "Członek",
@ -407,9 +428,7 @@
"keyboard_shortcuts.back": "aby cofnąć się",
"keyboard_shortcuts.blocked": "aby przejść do listy zablokowanych użytkowników",
"keyboard_shortcuts.boost": "aby podbić wpis",
"keyboard_shortcuts.column": "aby przejść do wpisu z jednej z kolumn",
"keyboard_shortcuts.compose": "aby przejść do pola tworzenia wpisu",
"keyboard_shortcuts.direct": "aby otworzyć kolumnę wiadomości bezpośrednich",
"keyboard_shortcuts.down": "aby przejść na dół listy",
"keyboard_shortcuts.enter": "aby otworzyć wpis",
"keyboard_shortcuts.favourite": "aby dodać do ulubionych",
@ -428,7 +447,6 @@
"keyboard_shortcuts.reply": "aby odpowiedzieć",
"keyboard_shortcuts.requests": "aby przejść do listy próśb o możliwość śledzenia",
"keyboard_shortcuts.search": "aby przejść do pola wyszukiwania",
"keyboard_shortcuts.start": "aby otworzyć kolumnę „Rozpocznij”",
"keyboard_shortcuts.toggle_hidden": "aby wyświetlić lub ukryć wpis spod CW",
"keyboard_shortcuts.toggle_sensitivity": "by pokazać/ukryć multimedia",
"keyboard_shortcuts.toot": "aby utworzyć nowy wpis",
@ -484,8 +502,10 @@
"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.",
"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.blocks": "Zablokowani użytkownicy",
"navigation_bar.bookmarks": "Zakładki",
"navigation_bar.compose": "Utwórz nowy wpis",
"navigation_bar.domain_blocks": "Ukryte domeny",
"navigation_bar.favourites": "Ulubione",
@ -494,7 +514,9 @@
"navigation_bar.import_data": "Importuj dane",
"navigation_bar.info": "Szczegółowe informacje",
"navigation_bar.keyboard_shortcuts": "Skróty klawiszowe",
"navigation_bar.lists": "Lists",
"navigation_bar.logout": "Wyloguj",
"navigation_bar.messages": "Messages",
"navigation_bar.mutes": "Wyciszeni użytkownicy",
"navigation_bar.pins": "Przypięte wpisy",
"navigation_bar.preferences": "Preferencje",
@ -613,6 +635,7 @@
"relative_time.minutes": "{number} min.",
"relative_time.seconds": "{number} s.",
"remote_instance.edit_federation": "Edytuj federację",
"remote_instance.federation_panel.heading": "Federation Restrictions",
"remote_instance.federation_panel.no_restrictions_message": "{siteTitle} nie nakłada ograniczeń na {host}.",
"remote_instance.federation_panel.restricted_message": "{siteTitle} blokuje wszystkie aktywności z {host}.",
"remote_instance.federation_panel.some_restrictions_message": "{siteTitle} nakłada pewne ograniczenia na {host}.",
@ -641,8 +664,6 @@
"search_results.hashtags": "Hashtagi",
"search_results.statuses": "Wpisy",
"search_results.top": "Góra",
"search_results.total": "{count, number} {count, plural, one {wynik} few {wyniki} many {wyników} more {wyników}}",
"search_results.total.has_more": "{count, number} Ponad {count, plural, one {wynik} few {wyniki} many {wyników} more {wyników}}",
"security.codes.fail": "Nie udało się uzyskać zapasowych kodów",
"security.confirm.fail": "Nieprawidłowy kod lub hasło. Spróbuj ponownie.",
"security.delete_account.fail": "Nie udało się usunąć konta.",
@ -788,7 +809,6 @@
"upload_error.limit": "Przekroczono limit plików do wysłania.",
"upload_error.poll": "Dołączanie plików nie dozwolone z głosowaniami.",
"upload_form.description": "Wprowadź opis dla niewidomych i niedowidzących",
"upload_form.focus": "Zmień podgląd",
"upload_form.preview": "Podgląd",
"upload_form.undo": "Usuń",
"upload_progress.label": "Wysyłanie…",

Wyświetl plik

@ -0,0 +1,35 @@
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: '',
loaded: false,
items: ImmutableList(),
}),
});
export default function aliasesReducer(state = initialState, action) {
switch(action.type) {
case ALIASES_SUGGESTIONS_CHANGE:
return state
.setIn(['suggestions', 'value'], action.value)
.setIn(['suggestions', 'loaded'], false);
case ALIASES_SUGGESTIONS_READY:
return state
.setIn(['suggestions', 'items'], ImmutableList(action.accounts.map(item => item.id)))
.setIn(['suggestions', 'loaded'], true);
case ALIASES_SUGGESTIONS_CLEAR:
return state.update('suggestions', suggestions => suggestions.withMutations(map => {
map.set('items', ImmutableList());
map.set('value', '');
map.set('loaded', false);
}));
default:
return state;
}
};

Wyświetl plik

@ -53,6 +53,7 @@ import backups from './backups';
import admin_log from './admin_log';
import security from './security';
import scheduled_statuses from './scheduled_statuses';
import aliases from './aliases';
const appReducer = combineReducers({
dropdown_menu,
@ -107,6 +108,7 @@ const appReducer = combineReducers({
admin_log,
security,
scheduled_statuses,
aliases,
});
// Clear the state (mostly) when the user logs out

Wyświetl plik

@ -84,6 +84,7 @@
@import 'components/datepicker';
@import 'components/remote-timeline';
@import 'components/federation-restrictions';
@import 'components/aliases';
// Holiday
@import 'holiday/halloween';

Wyświetl plik

@ -0,0 +1,107 @@
.aliases {
&__accounts {
overflow-y: auto;
.account__display-name {
&:hover strong {
text-decoration: none;
}
}
.account__avatar {
cursor: default;
}
&.empty-column-indicator {
min-height: unset;
overflow-y: unset;
}
}
&_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%;
}
}