Merge pull request #2718 from ClearlyClaire/glitch-soc/merge-upstream

Merge upstream changes up to b6fd14f0e2
This commit is contained in:
Claire 2024-05-23 22:41:09 +02:00 committed by GitHub
commit ef01546957
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
119 changed files with 833 additions and 384 deletions

View file

@ -1,18 +1,10 @@
import type { ApiRelationshipJSON } from 'flavours/glitch/api_types/relationships'; import { apiSubmitAccountNote } from 'flavours/glitch/api/accounts';
import { createAppAsyncThunk } from 'flavours/glitch/store/typed_functions'; import { createDataLoadingThunk } from 'flavours/glitch/store/typed_functions';
import api from '../api'; export const submitAccountNote = createDataLoadingThunk(
export const submitAccountNote = createAppAsyncThunk(
'account_note/submit', 'account_note/submit',
async (args: { id: string; value: string }) => { ({ accountId, note }: { accountId: string; note: string }) =>
const response = await api().post<ApiRelationshipJSON>( apiSubmitAccountNote(accountId, note),
`/api/v1/accounts/${args.id}/note`, (relationship) => ({ relationship }),
{ { skipLoading: true },
comment: args.value,
},
);
return { relationship: response.data };
},
); );

View file

@ -3,10 +3,6 @@ import api, { getLinks } from '../api';
import { fetchRelationships } from './accounts'; import { fetchRelationships } from './accounts';
import { importFetchedAccounts, importFetchedStatus } from './importer'; import { importFetchedAccounts, importFetchedStatus } from './importer';
export const REBLOG_REQUEST = 'REBLOG_REQUEST';
export const REBLOG_SUCCESS = 'REBLOG_SUCCESS';
export const REBLOG_FAIL = 'REBLOG_FAIL';
export const REBLOGS_EXPAND_REQUEST = 'REBLOGS_EXPAND_REQUEST'; export const REBLOGS_EXPAND_REQUEST = 'REBLOGS_EXPAND_REQUEST';
export const REBLOGS_EXPAND_SUCCESS = 'REBLOGS_EXPAND_SUCCESS'; export const REBLOGS_EXPAND_SUCCESS = 'REBLOGS_EXPAND_SUCCESS';
export const REBLOGS_EXPAND_FAIL = 'REBLOGS_EXPAND_FAIL'; export const REBLOGS_EXPAND_FAIL = 'REBLOGS_EXPAND_FAIL';
@ -15,10 +11,6 @@ export const FAVOURITE_REQUEST = 'FAVOURITE_REQUEST';
export const FAVOURITE_SUCCESS = 'FAVOURITE_SUCCESS'; export const FAVOURITE_SUCCESS = 'FAVOURITE_SUCCESS';
export const FAVOURITE_FAIL = 'FAVOURITE_FAIL'; export const FAVOURITE_FAIL = 'FAVOURITE_FAIL';
export const UNREBLOG_REQUEST = 'UNREBLOG_REQUEST';
export const UNREBLOG_SUCCESS = 'UNREBLOG_SUCCESS';
export const UNREBLOG_FAIL = 'UNREBLOG_FAIL';
export const UNFAVOURITE_REQUEST = 'UNFAVOURITE_REQUEST'; export const UNFAVOURITE_REQUEST = 'UNFAVOURITE_REQUEST';
export const UNFAVOURITE_SUCCESS = 'UNFAVOURITE_SUCCESS'; export const UNFAVOURITE_SUCCESS = 'UNFAVOURITE_SUCCESS';
export const UNFAVOURITE_FAIL = 'UNFAVOURITE_FAIL'; export const UNFAVOURITE_FAIL = 'UNFAVOURITE_FAIL';
@ -51,83 +43,7 @@ export const UNBOOKMARK_REQUEST = 'UNBOOKMARKED_REQUEST';
export const UNBOOKMARK_SUCCESS = 'UNBOOKMARKED_SUCCESS'; export const UNBOOKMARK_SUCCESS = 'UNBOOKMARKED_SUCCESS';
export const UNBOOKMARK_FAIL = 'UNBOOKMARKED_FAIL'; export const UNBOOKMARK_FAIL = 'UNBOOKMARKED_FAIL';
export function reblog(status, visibility) { export * from "./interactions_typed";
return function (dispatch) {
dispatch(reblogRequest(status));
api().post(`/api/v1/statuses/${status.get('id')}/reblog`, { visibility }).then(function (response) {
// The reblog API method returns a new status wrapped around the original. In this case we are only
// interested in how the original is modified, hence passing it skipping the wrapper
dispatch(importFetchedStatus(response.data.reblog));
dispatch(reblogSuccess(status));
}).catch(function (error) {
dispatch(reblogFail(status, error));
});
};
}
export function unreblog(status) {
return (dispatch) => {
dispatch(unreblogRequest(status));
api().post(`/api/v1/statuses/${status.get('id')}/unreblog`).then(response => {
dispatch(importFetchedStatus(response.data));
dispatch(unreblogSuccess(status));
}).catch(error => {
dispatch(unreblogFail(status, error));
});
};
}
export function reblogRequest(status) {
return {
type: REBLOG_REQUEST,
status: status,
skipLoading: true,
};
}
export function reblogSuccess(status) {
return {
type: REBLOG_SUCCESS,
status: status,
skipLoading: true,
};
}
export function reblogFail(status, error) {
return {
type: REBLOG_FAIL,
status: status,
error: error,
skipLoading: true,
};
}
export function unreblogRequest(status) {
return {
type: UNREBLOG_REQUEST,
status: status,
skipLoading: true,
};
}
export function unreblogSuccess(status) {
return {
type: UNREBLOG_SUCCESS,
status: status,
skipLoading: true,
};
}
export function unreblogFail(status, error) {
return {
type: UNREBLOG_FAIL,
status: status,
error: error,
skipLoading: true,
};
}
export function favourite(status) { export function favourite(status) {
return function (dispatch) { return function (dispatch) {

View file

@ -0,0 +1,35 @@
import { apiReblog, apiUnreblog } from 'flavours/glitch/api/interactions';
import type { StatusVisibility } from 'flavours/glitch/models/status';
import { createDataLoadingThunk } from 'flavours/glitch/store/typed_functions';
import { importFetchedStatus } from './importer';
export const reblog = createDataLoadingThunk(
'status/reblog',
({
statusId,
visibility,
}: {
statusId: string;
visibility: StatusVisibility;
}) => apiReblog(statusId, visibility),
(data, { dispatch, discardLoadData }) => {
// The reblog API method returns a new status wrapped around the original. In this case we are only
// interested in how the original is modified, hence passing it skipping the wrapper
dispatch(importFetchedStatus(data.reblog));
// The payload is not used in any actions
return discardLoadData;
},
);
export const unreblog = createDataLoadingThunk(
'status/unreblog',
({ statusId }: { statusId: string }) => apiUnreblog(statusId),
(data, { dispatch, discardLoadData }) => {
dispatch(importFetchedStatus(data));
// The payload is not used in any actions
return discardLoadData;
},
);

View file

@ -1,4 +1,4 @@
import type { AxiosResponse, RawAxiosRequestHeaders } from 'axios'; import type { AxiosResponse, Method, RawAxiosRequestHeaders } from 'axios';
import axios from 'axios'; import axios from 'axios';
import LinkHeader from 'http-link-header'; import LinkHeader from 'http-link-header';
@ -40,11 +40,11 @@ const authorizationTokenFromInitialState = (): RawAxiosRequestHeaders => {
}; };
// eslint-disable-next-line import/no-default-export // eslint-disable-next-line import/no-default-export
export default function api() { export default function api(withAuthorization = true) {
return axios.create({ return axios.create({
headers: { headers: {
...csrfHeader, ...csrfHeader,
...authorizationTokenFromInitialState(), ...(withAuthorization ? authorizationTokenFromInitialState() : {}),
}, },
transformResponse: [ transformResponse: [
@ -58,3 +58,17 @@ export default function api() {
], ],
}); });
} }
export async function apiRequest<ApiResponse = unknown>(
method: Method,
url: string,
params?: Record<string, unknown>,
) {
const { data } = await api().request<ApiResponse>({
method,
url: '/api/' + url,
data: params,
});
return data;
}

View file

@ -0,0 +1,7 @@
import { apiRequest } from 'flavours/glitch/api';
import type { ApiRelationshipJSON } from 'flavours/glitch/api_types/relationships';
export const apiSubmitAccountNote = (id: string, value: string) =>
apiRequest<ApiRelationshipJSON>('post', `v1/accounts/${id}/note`, {
comment: value,
});

View file

@ -0,0 +1,10 @@
import { apiRequest } from 'flavours/glitch/api';
import type { Status, StatusVisibility } from 'flavours/glitch/models/status';
export const apiReblog = (statusId: string, visibility: StatusVisibility) =>
apiRequest<{ reblog: Status }>('post', `v1/statuses/${statusId}/reblog`, {
visibility,
});
export const apiUnreblog = (statusId: string) =>
apiRequest<Status>('post', `v1/statuses/${statusId}/unreblog`);

View file

@ -48,7 +48,7 @@ export default class Counter extends PureComponent {
componentDidMount () { componentDidMount () {
const { measure, start_at, end_at, params } = this.props; const { measure, start_at, end_at, params } = this.props;
api().post('/api/v1/admin/measures', { keys: [measure], start_at, end_at, [measure]: params }).then(res => { api(false).post('/api/v1/admin/measures', { keys: [measure], start_at, end_at, [measure]: params }).then(res => {
this.setState({ this.setState({
loading: false, loading: false,
data: res.data, data: res.data,

View file

@ -26,7 +26,7 @@ export default class Dimension extends PureComponent {
componentDidMount () { componentDidMount () {
const { start_at, end_at, dimension, limit, params } = this.props; const { start_at, end_at, dimension, limit, params } = this.props;
api().post('/api/v1/admin/dimensions', { keys: [dimension], start_at, end_at, limit, [dimension]: params }).then(res => { api(false).post('/api/v1/admin/dimensions', { keys: [dimension], start_at, end_at, limit, [dimension]: params }).then(res => {
this.setState({ this.setState({
loading: false, loading: false,
data: res.data, data: res.data,

View file

@ -27,7 +27,7 @@ export default class ImpactReport extends PureComponent {
include_subdomains: true, include_subdomains: true,
}; };
api().post('/api/v1/admin/measures', { api(false).post('/api/v1/admin/measures', {
keys: ['instance_accounts', 'instance_follows', 'instance_followers'], keys: ['instance_accounts', 'instance_follows', 'instance_followers'],
start_at: null, start_at: null,
end_at: null, end_at: null,

View file

@ -105,7 +105,7 @@ class ReportReasonSelector extends PureComponent {
}; };
componentDidMount() { componentDidMount() {
api().get('/api/v1/instance').then(res => { api(false).get('/api/v1/instance').then(res => {
this.setState({ this.setState({
rules: res.data.rules, rules: res.data.rules,
}); });
@ -122,7 +122,7 @@ class ReportReasonSelector extends PureComponent {
return; return;
} }
api().put(`/api/v1/admin/reports/${id}`, { api(false).put(`/api/v1/admin/reports/${id}`, {
category, category,
rule_ids: category === 'violation' ? rule_ids : [], rule_ids: category === 'violation' ? rule_ids : [],
}).catch(err => { }).catch(err => {

View file

@ -34,7 +34,7 @@ export default class Retention extends PureComponent {
componentDidMount () { componentDidMount () {
const { start_at, end_at, frequency } = this.props; const { start_at, end_at, frequency } = this.props;
api().post('/api/v1/admin/retention', { start_at, end_at, frequency }).then(res => { api(false).post('/api/v1/admin/retention', { start_at, end_at, frequency }).then(res => {
this.setState({ this.setState({
loading: false, loading: false,
data: res.data, data: res.data,

View file

@ -22,7 +22,7 @@ export default class Trends extends PureComponent {
componentDidMount () { componentDidMount () {
const { limit } = this.props; const { limit } = this.props;
api().get('/api/v1/admin/trends/tags', { params: { limit } }).then(res => { api(false).get('/api/v1/admin/trends/tags', { params: { limit } }).then(res => {
this.setState({ this.setState({
loading: false, loading: false,
data: res.data, data: res.data,

View file

@ -115,9 +115,9 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
onModalReblog (status, privacy) { onModalReblog (status, privacy) {
if (status.get('reblogged')) { if (status.get('reblogged')) {
dispatch(unreblog(status)); dispatch(unreblog({ statusId: status.get('id') }));
} else { } else {
dispatch(reblog(status, privacy)); dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
} }
}, },

View file

@ -11,7 +11,7 @@ const mapStateToProps = (state, { account }) => ({
const mapDispatchToProps = (dispatch, { account }) => ({ const mapDispatchToProps = (dispatch, { account }) => ({
onSave (value) { onSave (value) {
dispatch(submitAccountNote({ id: account.get('id'), value})); dispatch(submitAccountNote({ accountId: account.get('id'), note: value }));
}, },
}); });

View file

@ -36,12 +36,12 @@ const mapDispatchToProps = dispatch => ({
}, },
onModalReblog (status, privacy) { onModalReblog (status, privacy) {
dispatch(reblog(status, privacy)); dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
}, },
onReblog (status, e) { onReblog (status, e) {
if (status.get('reblogged')) { if (status.get('reblogged')) {
dispatch(unreblog(status)); dispatch(unreblog({ statusId: status.get('id') }));
} else { } else {
if (e.shiftKey || !boostModal) { if (e.shiftKey || !boostModal) {
this.onModalReblog(status); this.onModalReblog(status);

View file

@ -125,7 +125,7 @@ class Footer extends ImmutablePureComponent {
_performReblog = (status, privacy) => { _performReblog = (status, privacy) => {
const { dispatch } = this.props; const { dispatch } = this.props;
dispatch(reblog(status, privacy)); dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
}; };
handleReblogClick = e => { handleReblogClick = e => {
@ -134,7 +134,7 @@ class Footer extends ImmutablePureComponent {
if (signedIn) { if (signedIn) {
if (status.get('reblogged')) { if (status.get('reblogged')) {
dispatch(unreblog(status)); dispatch(unreblog({ statusId: status.get('id') }));
} else if ((e && e.shiftKey) || !boostModal) { } else if ((e && e.shiftKey) || !boostModal) {
this._performReblog(status); this._performReblog(status);
} else { } else {

View file

@ -71,12 +71,12 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
}, },
onModalReblog (status, privacy) { onModalReblog (status, privacy) {
dispatch(reblog(status, privacy)); dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
}, },
onReblog (status, e) { onReblog (status, e) {
if (status.get('reblogged')) { if (status.get('reblogged')) {
dispatch(unreblog(status)); dispatch(unreblog({ statusId: status.get('id') }));
} else { } else {
if (e.shiftKey || !boostModal) { if (e.shiftKey || !boostModal) {
this.onModalReblog(status); this.onModalReblog(status);

View file

@ -346,9 +346,9 @@ class Status extends ImmutablePureComponent {
const { dispatch } = this.props; const { dispatch } = this.props;
if (status.get('reblogged')) { if (status.get('reblogged')) {
dispatch(unreblog(status)); dispatch(unreblog({ statusId: status.get('id') }));
} else { } else {
dispatch(reblog(status, privacy)); dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
} }
}; };

View file

@ -3,10 +3,6 @@ import { Map as ImmutableMap, fromJS } from 'immutable';
import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer'; import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer';
import { normalizeStatusTranslation } from '../actions/importer/normalizer'; import { normalizeStatusTranslation } from '../actions/importer/normalizer';
import { import {
REBLOG_REQUEST,
REBLOG_FAIL,
UNREBLOG_REQUEST,
UNREBLOG_FAIL,
FAVOURITE_REQUEST, FAVOURITE_REQUEST,
FAVOURITE_FAIL, FAVOURITE_FAIL,
UNFAVOURITE_REQUEST, UNFAVOURITE_REQUEST,
@ -16,6 +12,10 @@ import {
UNBOOKMARK_REQUEST, UNBOOKMARK_REQUEST,
UNBOOKMARK_FAIL, UNBOOKMARK_FAIL,
} from '../actions/interactions'; } from '../actions/interactions';
import {
reblog,
unreblog,
} from '../actions/interactions_typed';
import { import {
STATUS_MUTE_SUCCESS, STATUS_MUTE_SUCCESS,
STATUS_UNMUTE_SUCCESS, STATUS_UNMUTE_SUCCESS,
@ -65,6 +65,7 @@ const statusTranslateUndo = (state, id) => {
const initialState = ImmutableMap(); const initialState = ImmutableMap();
/** @type {import('@reduxjs/toolkit').Reducer<typeof initialState>} */
export default function statuses(state = initialState, action) { export default function statuses(state = initialState, action) {
switch(action.type) { switch(action.type) {
case STATUS_FETCH_REQUEST: case STATUS_FETCH_REQUEST:
@ -91,14 +92,6 @@ export default function statuses(state = initialState, action) {
return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'bookmarked'], false); return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'bookmarked'], false);
case UNBOOKMARK_FAIL: case UNBOOKMARK_FAIL:
return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'bookmarked'], true); return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'bookmarked'], true);
case REBLOG_REQUEST:
return state.setIn([action.status.get('id'), 'reblogged'], true);
case REBLOG_FAIL:
return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'reblogged'], false);
case UNREBLOG_REQUEST:
return state.setIn([action.status.get('id'), 'reblogged'], false);
case UNREBLOG_FAIL:
return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'reblogged'], true);
case STATUS_MUTE_SUCCESS: case STATUS_MUTE_SUCCESS:
return state.setIn([action.id, 'muted'], true); return state.setIn([action.id, 'muted'], true);
case STATUS_UNMUTE_SUCCESS: case STATUS_UNMUTE_SUCCESS:
@ -128,6 +121,15 @@ export default function statuses(state = initialState, action) {
case STATUS_TRANSLATE_UNDO: case STATUS_TRANSLATE_UNDO:
return statusTranslateUndo(state, action.id); return statusTranslateUndo(state, action.id);
default: default:
return state; if(reblog.pending.match(action))
return state.setIn([action.meta.arg.statusId, 'reblogged'], true);
else if(reblog.rejected.match(action))
return state.get(action.meta.arg.statusId) === undefined ? state : state.setIn([action.meta.arg.statusId, 'reblogged'], false);
else if(unreblog.pending.match(action))
return state.setIn([action.meta.arg.statusId, 'reblogged'], false);
else if(unreblog.rejected.match(action))
return state.get(action.meta.arg.statusId) === undefined ? state : state.setIn([action.meta.arg.statusId, 'reblogged'], true);
else
return state;
} }
} }

View file

@ -2,6 +2,8 @@ import { createAsyncThunk } from '@reduxjs/toolkit';
// eslint-disable-next-line @typescript-eslint/no-restricted-imports // eslint-disable-next-line @typescript-eslint/no-restricted-imports
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import type { BaseThunkAPI } from '@reduxjs/toolkit/dist/createAsyncThunk';
import type { AppDispatch, RootState } from './store'; import type { AppDispatch, RootState } from './store';
export const useAppDispatch = useDispatch.withTypes<AppDispatch>(); export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
@ -13,8 +15,192 @@ export interface AsyncThunkRejectValue {
error?: unknown; error?: unknown;
} }
interface AppMeta {
skipLoading?: boolean;
}
export const createAppAsyncThunk = createAsyncThunk.withTypes<{ export const createAppAsyncThunk = createAsyncThunk.withTypes<{
state: RootState; state: RootState;
dispatch: AppDispatch; dispatch: AppDispatch;
rejectValue: AsyncThunkRejectValue; rejectValue: AsyncThunkRejectValue;
}>(); }>();
type AppThunkApi = Pick<
BaseThunkAPI<
RootState,
unknown,
AppDispatch,
AsyncThunkRejectValue,
AppMeta,
AppMeta
>,
'getState' | 'dispatch'
>;
interface AppThunkOptions {
skipLoading?: boolean;
}
const createBaseAsyncThunk = createAsyncThunk.withTypes<{
state: RootState;
dispatch: AppDispatch;
rejectValue: AsyncThunkRejectValue;
fulfilledMeta: AppMeta;
rejectedMeta: AppMeta;
}>();
export function createThunk<Arg = void, Returned = void>(
name: string,
creator: (arg: Arg, api: AppThunkApi) => Returned | Promise<Returned>,
options: AppThunkOptions = {},
) {
return createBaseAsyncThunk(
name,
async (
arg: Arg,
{ getState, dispatch, fulfillWithValue, rejectWithValue },
) => {
try {
const result = await creator(arg, { dispatch, getState });
return fulfillWithValue(result, {
skipLoading: options.skipLoading,
});
} catch (error) {
return rejectWithValue({ error }, { skipLoading: true });
}
},
{
getPendingMeta() {
if (options.skipLoading) return { skipLoading: true };
return {};
},
},
);
}
const discardLoadDataInPayload = Symbol('discardLoadDataInPayload');
type DiscardLoadData = typeof discardLoadDataInPayload;
type OnData<LoadDataResult, ReturnedData> = (
data: LoadDataResult,
api: AppThunkApi & {
discardLoadData: DiscardLoadData;
},
) => ReturnedData | DiscardLoadData | Promise<ReturnedData | DiscardLoadData>;
// Overload when there is no `onData` method, the payload is the `onData` result
export function createDataLoadingThunk<
LoadDataResult,
Args extends Record<string, unknown>,
>(
name: string,
loadData: (args: Args) => Promise<LoadDataResult>,
thunkOptions?: AppThunkOptions,
): ReturnType<typeof createThunk<Args, LoadDataResult>>;
// Overload when the `onData` method returns discardLoadDataInPayload, then the payload is empty
export function createDataLoadingThunk<
LoadDataResult,
Args extends Record<string, unknown>,
>(
name: string,
loadData: (args: Args) => Promise<LoadDataResult>,
onDataOrThunkOptions?:
| AppThunkOptions
| OnData<LoadDataResult, DiscardLoadData>,
thunkOptions?: AppThunkOptions,
): ReturnType<typeof createThunk<Args, void>>;
// Overload when the `onData` method returns nothing, then the mayload is the `onData` result
export function createDataLoadingThunk<
LoadDataResult,
Args extends Record<string, unknown>,
>(
name: string,
loadData: (args: Args) => Promise<LoadDataResult>,
onDataOrThunkOptions?: AppThunkOptions | OnData<LoadDataResult, void>,
thunkOptions?: AppThunkOptions,
): ReturnType<typeof createThunk<Args, LoadDataResult>>;
// Overload when there is an `onData` method returning something
export function createDataLoadingThunk<
LoadDataResult,
Args extends Record<string, unknown>,
Returned,
>(
name: string,
loadData: (args: Args) => Promise<LoadDataResult>,
onDataOrThunkOptions?: AppThunkOptions | OnData<LoadDataResult, Returned>,
thunkOptions?: AppThunkOptions,
): ReturnType<typeof createThunk<Args, Returned>>;
/**
* This function creates a Redux Thunk that handles loading data asynchronously (usually from the API), dispatching `pending`, `fullfilled` and `rejected` actions.
*
* You can run a callback on the `onData` results to either dispatch side effects or modify the payload.
*
* It is a wrapper around RTK's [`createAsyncThunk`](https://redux-toolkit.js.org/api/createAsyncThunk)
* @param name Prefix for the actions types
* @param loadData Function that loads the data. It's (object) argument will become the thunk's argument
* @param onDataOrThunkOptions
* Callback called on the results from `loadData`.
*
* First argument will be the return from `loadData`.
*
* Second argument is an object with: `dispatch`, `getState` and `discardLoadData`.
* It can return:
* - `undefined` (or no explicit return), meaning that the `onData` results will be the payload
* - `discardLoadData` to discard the `onData` results and return an empty payload
* - anything else, which will be the payload
*
* You can also omit this parameter and pass `thunkOptions` directly
* @param maybeThunkOptions
* Additional Mastodon specific options for the thunk. Currently supports:
* - `skipLoading` to avoid showing the loading bar when the request is in progress
* @returns The created thunk
*/
export function createDataLoadingThunk<
LoadDataResult,
Args extends Record<string, unknown>,
Returned,
>(
name: string,
loadData: (args: Args) => Promise<LoadDataResult>,
onDataOrThunkOptions?: AppThunkOptions | OnData<LoadDataResult, Returned>,
maybeThunkOptions?: AppThunkOptions,
) {
let onData: OnData<LoadDataResult, Returned> | undefined;
let thunkOptions: AppThunkOptions | undefined;
if (typeof onDataOrThunkOptions === 'function') onData = onDataOrThunkOptions;
else if (typeof onDataOrThunkOptions === 'object')
thunkOptions = onDataOrThunkOptions;
if (maybeThunkOptions) {
thunkOptions = maybeThunkOptions;
}
return createThunk<Args, Returned>(
name,
async (arg, { getState, dispatch }) => {
const data = await loadData(arg);
if (!onData) return data as Returned;
const result = await onData(data, {
dispatch,
getState,
discardLoadData: discardLoadDataInPayload,
});
// if there is no return in `onData`, we return the `onData` result
if (typeof result === 'undefined') return data as Returned;
// the user explicitely asked to discard the payload
else if (result === discardLoadDataInPayload)
return undefined as Returned;
else return result;
},
thunkOptions,
);
}

View file

@ -1,18 +1,10 @@
import type { ApiRelationshipJSON } from 'mastodon/api_types/relationships'; import { apiSubmitAccountNote } from 'mastodon/api/accounts';
import { createAppAsyncThunk } from 'mastodon/store/typed_functions'; import { createDataLoadingThunk } from 'mastodon/store/typed_functions';
import api from '../api'; export const submitAccountNote = createDataLoadingThunk(
export const submitAccountNote = createAppAsyncThunk(
'account_note/submit', 'account_note/submit',
async (args: { id: string; value: string }) => { ({ accountId, note }: { accountId: string; note: string }) =>
const response = await api().post<ApiRelationshipJSON>( apiSubmitAccountNote(accountId, note),
`/api/v1/accounts/${args.id}/note`, (relationship) => ({ relationship }),
{ { skipLoading: true },
comment: args.value,
},
);
return { relationship: response.data };
},
); );

View file

@ -3,10 +3,6 @@ import api, { getLinks } from '../api';
import { fetchRelationships } from './accounts'; import { fetchRelationships } from './accounts';
import { importFetchedAccounts, importFetchedStatus } from './importer'; import { importFetchedAccounts, importFetchedStatus } from './importer';
export const REBLOG_REQUEST = 'REBLOG_REQUEST';
export const REBLOG_SUCCESS = 'REBLOG_SUCCESS';
export const REBLOG_FAIL = 'REBLOG_FAIL';
export const REBLOGS_EXPAND_REQUEST = 'REBLOGS_EXPAND_REQUEST'; export const REBLOGS_EXPAND_REQUEST = 'REBLOGS_EXPAND_REQUEST';
export const REBLOGS_EXPAND_SUCCESS = 'REBLOGS_EXPAND_SUCCESS'; export const REBLOGS_EXPAND_SUCCESS = 'REBLOGS_EXPAND_SUCCESS';
export const REBLOGS_EXPAND_FAIL = 'REBLOGS_EXPAND_FAIL'; export const REBLOGS_EXPAND_FAIL = 'REBLOGS_EXPAND_FAIL';
@ -15,10 +11,6 @@ export const FAVOURITE_REQUEST = 'FAVOURITE_REQUEST';
export const FAVOURITE_SUCCESS = 'FAVOURITE_SUCCESS'; export const FAVOURITE_SUCCESS = 'FAVOURITE_SUCCESS';
export const FAVOURITE_FAIL = 'FAVOURITE_FAIL'; export const FAVOURITE_FAIL = 'FAVOURITE_FAIL';
export const UNREBLOG_REQUEST = 'UNREBLOG_REQUEST';
export const UNREBLOG_SUCCESS = 'UNREBLOG_SUCCESS';
export const UNREBLOG_FAIL = 'UNREBLOG_FAIL';
export const UNFAVOURITE_REQUEST = 'UNFAVOURITE_REQUEST'; export const UNFAVOURITE_REQUEST = 'UNFAVOURITE_REQUEST';
export const UNFAVOURITE_SUCCESS = 'UNFAVOURITE_SUCCESS'; export const UNFAVOURITE_SUCCESS = 'UNFAVOURITE_SUCCESS';
export const UNFAVOURITE_FAIL = 'UNFAVOURITE_FAIL'; export const UNFAVOURITE_FAIL = 'UNFAVOURITE_FAIL';
@ -51,83 +43,7 @@ export const UNBOOKMARK_REQUEST = 'UNBOOKMARKED_REQUEST';
export const UNBOOKMARK_SUCCESS = 'UNBOOKMARKED_SUCCESS'; export const UNBOOKMARK_SUCCESS = 'UNBOOKMARKED_SUCCESS';
export const UNBOOKMARK_FAIL = 'UNBOOKMARKED_FAIL'; export const UNBOOKMARK_FAIL = 'UNBOOKMARKED_FAIL';
export function reblog(status, visibility) { export * from "./interactions_typed";
return function (dispatch) {
dispatch(reblogRequest(status));
api().post(`/api/v1/statuses/${status.get('id')}/reblog`, { visibility }).then(function (response) {
// The reblog API method returns a new status wrapped around the original. In this case we are only
// interested in how the original is modified, hence passing it skipping the wrapper
dispatch(importFetchedStatus(response.data.reblog));
dispatch(reblogSuccess(status));
}).catch(function (error) {
dispatch(reblogFail(status, error));
});
};
}
export function unreblog(status) {
return (dispatch) => {
dispatch(unreblogRequest(status));
api().post(`/api/v1/statuses/${status.get('id')}/unreblog`).then(response => {
dispatch(importFetchedStatus(response.data));
dispatch(unreblogSuccess(status));
}).catch(error => {
dispatch(unreblogFail(status, error));
});
};
}
export function reblogRequest(status) {
return {
type: REBLOG_REQUEST,
status: status,
skipLoading: true,
};
}
export function reblogSuccess(status) {
return {
type: REBLOG_SUCCESS,
status: status,
skipLoading: true,
};
}
export function reblogFail(status, error) {
return {
type: REBLOG_FAIL,
status: status,
error: error,
skipLoading: true,
};
}
export function unreblogRequest(status) {
return {
type: UNREBLOG_REQUEST,
status: status,
skipLoading: true,
};
}
export function unreblogSuccess(status) {
return {
type: UNREBLOG_SUCCESS,
status: status,
skipLoading: true,
};
}
export function unreblogFail(status, error) {
return {
type: UNREBLOG_FAIL,
status: status,
error: error,
skipLoading: true,
};
}
export function favourite(status) { export function favourite(status) {
return function (dispatch) { return function (dispatch) {

View file

@ -0,0 +1,35 @@
import { apiReblog, apiUnreblog } from 'mastodon/api/interactions';
import type { StatusVisibility } from 'mastodon/models/status';
import { createDataLoadingThunk } from 'mastodon/store/typed_functions';
import { importFetchedStatus } from './importer';
export const reblog = createDataLoadingThunk(
'status/reblog',
({
statusId,
visibility,
}: {
statusId: string;
visibility: StatusVisibility;
}) => apiReblog(statusId, visibility),
(data, { dispatch, discardLoadData }) => {
// The reblog API method returns a new status wrapped around the original. In this case we are only
// interested in how the original is modified, hence passing it skipping the wrapper
dispatch(importFetchedStatus(data.reblog));
// The payload is not used in any actions
return discardLoadData;
},
);
export const unreblog = createDataLoadingThunk(
'status/unreblog',
({ statusId }: { statusId: string }) => apiUnreblog(statusId),
(data, { dispatch, discardLoadData }) => {
dispatch(importFetchedStatus(data));
// The payload is not used in any actions
return discardLoadData;
},
);

View file

@ -1,4 +1,4 @@
import type { AxiosResponse, RawAxiosRequestHeaders } from 'axios'; import type { AxiosResponse, Method, RawAxiosRequestHeaders } from 'axios';
import axios from 'axios'; import axios from 'axios';
import LinkHeader from 'http-link-header'; import LinkHeader from 'http-link-header';
@ -40,11 +40,11 @@ const authorizationTokenFromInitialState = (): RawAxiosRequestHeaders => {
}; };
// eslint-disable-next-line import/no-default-export // eslint-disable-next-line import/no-default-export
export default function api() { export default function api(withAuthorization = true) {
return axios.create({ return axios.create({
headers: { headers: {
...csrfHeader, ...csrfHeader,
...authorizationTokenFromInitialState(), ...(withAuthorization ? authorizationTokenFromInitialState() : {}),
}, },
transformResponse: [ transformResponse: [
@ -58,3 +58,17 @@ export default function api() {
], ],
}); });
} }
export async function apiRequest<ApiResponse = unknown>(
method: Method,
url: string,
params?: Record<string, unknown>,
) {
const { data } = await api().request<ApiResponse>({
method,
url: '/api/' + url,
data: params,
});
return data;
}

View file

@ -0,0 +1,7 @@
import { apiRequest } from 'mastodon/api';
import type { ApiRelationshipJSON } from 'mastodon/api_types/relationships';
export const apiSubmitAccountNote = (id: string, value: string) =>
apiRequest<ApiRelationshipJSON>('post', `v1/accounts/${id}/note`, {
comment: value,
});

View file

@ -0,0 +1,10 @@
import { apiRequest } from 'mastodon/api';
import type { Status, StatusVisibility } from 'mastodon/models/status';
export const apiReblog = (statusId: string, visibility: StatusVisibility) =>
apiRequest<{ reblog: Status }>('post', `v1/statuses/${statusId}/reblog`, {
visibility,
});
export const apiUnreblog = (statusId: string) =>
apiRequest<Status>('post', `v1/statuses/${statusId}/unreblog`);

View file

@ -48,7 +48,7 @@ export default class Counter extends PureComponent {
componentDidMount () { componentDidMount () {
const { measure, start_at, end_at, params } = this.props; const { measure, start_at, end_at, params } = this.props;
api().post('/api/v1/admin/measures', { keys: [measure], start_at, end_at, [measure]: params }).then(res => { api(false).post('/api/v1/admin/measures', { keys: [measure], start_at, end_at, [measure]: params }).then(res => {
this.setState({ this.setState({
loading: false, loading: false,
data: res.data, data: res.data,

View file

@ -26,7 +26,7 @@ export default class Dimension extends PureComponent {
componentDidMount () { componentDidMount () {
const { start_at, end_at, dimension, limit, params } = this.props; const { start_at, end_at, dimension, limit, params } = this.props;
api().post('/api/v1/admin/dimensions', { keys: [dimension], start_at, end_at, limit, [dimension]: params }).then(res => { api(false).post('/api/v1/admin/dimensions', { keys: [dimension], start_at, end_at, limit, [dimension]: params }).then(res => {
this.setState({ this.setState({
loading: false, loading: false,
data: res.data, data: res.data,

View file

@ -27,7 +27,7 @@ export default class ImpactReport extends PureComponent {
include_subdomains: true, include_subdomains: true,
}; };
api().post('/api/v1/admin/measures', { api(false).post('/api/v1/admin/measures', {
keys: ['instance_accounts', 'instance_follows', 'instance_followers'], keys: ['instance_accounts', 'instance_follows', 'instance_followers'],
start_at: null, start_at: null,
end_at: null, end_at: null,

View file

@ -105,7 +105,7 @@ class ReportReasonSelector extends PureComponent {
}; };
componentDidMount() { componentDidMount() {
api().get('/api/v1/instance').then(res => { api(false).get('/api/v1/instance').then(res => {
this.setState({ this.setState({
rules: res.data.rules, rules: res.data.rules,
}); });
@ -122,7 +122,7 @@ class ReportReasonSelector extends PureComponent {
return; return;
} }
api().put(`/api/v1/admin/reports/${id}`, { api(false).put(`/api/v1/admin/reports/${id}`, {
category, category,
rule_ids: category === 'violation' ? rule_ids : [], rule_ids: category === 'violation' ? rule_ids : [],
}).catch(err => { }).catch(err => {

View file

@ -34,7 +34,7 @@ export default class Retention extends PureComponent {
componentDidMount () { componentDidMount () {
const { start_at, end_at, frequency } = this.props; const { start_at, end_at, frequency } = this.props;
api().post('/api/v1/admin/retention', { start_at, end_at, frequency }).then(res => { api(false).post('/api/v1/admin/retention', { start_at, end_at, frequency }).then(res => {
this.setState({ this.setState({
loading: false, loading: false,
data: res.data, data: res.data,

View file

@ -22,7 +22,7 @@ export default class Trends extends PureComponent {
componentDidMount () { componentDidMount () {
const { limit } = this.props; const { limit } = this.props;
api().get('/api/v1/admin/trends/tags', { params: { limit } }).then(res => { api(false).get('/api/v1/admin/trends/tags', { params: { limit } }).then(res => {
this.setState({ this.setState({
loading: false, loading: false,
data: res.data, data: res.data,

View file

@ -96,9 +96,9 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
onModalReblog (status, privacy) { onModalReblog (status, privacy) {
if (status.get('reblogged')) { if (status.get('reblogged')) {
dispatch(unreblog(status)); dispatch(unreblog({ statusId: status.get('id') }));
} else { } else {
dispatch(reblog(status, privacy)); dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
} }
}, },

View file

@ -11,7 +11,7 @@ const mapStateToProps = (state, { account }) => ({
const mapDispatchToProps = (dispatch, { account }) => ({ const mapDispatchToProps = (dispatch, { account }) => ({
onSave (value) { onSave (value) {
dispatch(submitAccountNote({ id: account.get('id'), value})); dispatch(submitAccountNote({ accountId: account.get('id'), note: value }));
}, },
}); });

View file

@ -39,12 +39,12 @@ const mapDispatchToProps = dispatch => ({
}, },
onModalReblog (status, privacy) { onModalReblog (status, privacy) {
dispatch(reblog(status, privacy)); dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
}, },
onReblog (status, e) { onReblog (status, e) {
if (status.get('reblogged')) { if (status.get('reblogged')) {
dispatch(unreblog(status)); dispatch(unreblog({ statusId: status.get('id') }));
} else { } else {
if (e.shiftKey || !boostModal) { if (e.shiftKey || !boostModal) {
this.onModalReblog(status); this.onModalReblog(status);

View file

@ -123,7 +123,7 @@ class Footer extends ImmutablePureComponent {
_performReblog = (status, privacy) => { _performReblog = (status, privacy) => {
const { dispatch } = this.props; const { dispatch } = this.props;
dispatch(reblog(status, privacy)); dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
}; };
handleReblogClick = e => { handleReblogClick = e => {
@ -132,7 +132,7 @@ class Footer extends ImmutablePureComponent {
if (signedIn) { if (signedIn) {
if (status.get('reblogged')) { if (status.get('reblogged')) {
dispatch(unreblog(status)); dispatch(unreblog({ statusId: status.get('id') }));
} else if ((e && e.shiftKey) || !boostModal) { } else if ((e && e.shiftKey) || !boostModal) {
this._performReblog(status); this._performReblog(status);
} else { } else {

View file

@ -74,12 +74,12 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
}, },
onModalReblog (status, privacy) { onModalReblog (status, privacy) {
dispatch(reblog(status, privacy)); dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
}, },
onReblog (status, e) { onReblog (status, e) {
if (status.get('reblogged')) { if (status.get('reblogged')) {
dispatch(unreblog(status)); dispatch(unreblog({ statusId: status.get('id') }));
} else { } else {
if (e.shiftKey || !boostModal) { if (e.shiftKey || !boostModal) {
this.onModalReblog(status); this.onModalReblog(status);

View file

@ -299,7 +299,7 @@ class Status extends ImmutablePureComponent {
}; };
handleModalReblog = (status, privacy) => { handleModalReblog = (status, privacy) => {
this.props.dispatch(reblog(status, privacy)); this.props.dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
}; };
handleReblogClick = (status, e) => { handleReblogClick = (status, e) => {
@ -308,7 +308,7 @@ class Status extends ImmutablePureComponent {
if (signedIn) { if (signedIn) {
if (status.get('reblogged')) { if (status.get('reblogged')) {
dispatch(unreblog(status)); dispatch(unreblog({ statusId: status.get('id') }));
} else { } else {
if ((e && e.shiftKey) || !boostModal) { if ((e && e.shiftKey) || !boostModal) {
this.handleModalReblog(status); this.handleModalReblog(status);

View file

@ -469,6 +469,7 @@
"notification.follow": "{name} падпісаўся на вас", "notification.follow": "{name} падпісаўся на вас",
"notification.follow_request": "{name} адправіў запыт на падпіску", "notification.follow_request": "{name} адправіў запыт на падпіску",
"notification.mention": "{name} згадаў вас", "notification.mention": "{name} згадаў вас",
"notification.moderation-warning.learn_more": "Даведацца больш",
"notification.own_poll": "Ваша апытанне скончылася", "notification.own_poll": "Ваша апытанне скончылася",
"notification.poll": "Апытанне, дзе вы прынялі ўдзел, скончылася", "notification.poll": "Апытанне, дзе вы прынялі ўдзел, скончылася",
"notification.reblog": "{name} пашырыў ваш допіс", "notification.reblog": "{name} пашырыў ваш допіс",

View file

@ -491,7 +491,7 @@
"onboarding.actions.go_to_home": "Dodieties uz manu mājas plūsmu", "onboarding.actions.go_to_home": "Dodieties uz manu mājas plūsmu",
"onboarding.compose.template": "Sveiki, #Mastodon!", "onboarding.compose.template": "Sveiki, #Mastodon!",
"onboarding.follows.empty": "Diemžēl pašlaik nevar parādīt rezultātus. Vari mēģināt izmantot meklēšanu vai pārlūkot izpētes lapu, lai atrastu cilvēkus, kuriem sekot, vai vēlāk mēģināt vēlreiz.", "onboarding.follows.empty": "Diemžēl pašlaik nevar parādīt rezultātus. Vari mēģināt izmantot meklēšanu vai pārlūkot izpētes lapu, lai atrastu cilvēkus, kuriem sekot, vai vēlāk mēģināt vēlreiz.",
"onboarding.follows.lead": "Tava mājas plūsma ir galvenais veids, kā izbaudīt Mastodon. Jo vairāk cilvēku sekosi, jo aktīvāk un interesantāk tas būs. Lai sāktu, šeit ir daži ieteikumi:", "onboarding.follows.lead": "Tava mājas plūsma ir galvenais veids, kā pieredzēt Mastodon. Jo vairāk cilvēkiem sekosi, jo dzīvīgāka un aizraujošāka tā būs. Lai sāktu, šeit ir daži ieteikumi:",
"onboarding.follows.title": "Pielāgo savu mājas barotni", "onboarding.follows.title": "Pielāgo savu mājas barotni",
"onboarding.profile.discoverable": "Padarīt manu profilu atklājamu", "onboarding.profile.discoverable": "Padarīt manu profilu atklājamu",
"onboarding.profile.display_name": "Attēlojamais vārds", "onboarding.profile.display_name": "Attēlojamais vārds",

View file

@ -3,10 +3,6 @@ import { Map as ImmutableMap, fromJS } from 'immutable';
import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer'; import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer';
import { normalizeStatusTranslation } from '../actions/importer/normalizer'; import { normalizeStatusTranslation } from '../actions/importer/normalizer';
import { import {
REBLOG_REQUEST,
REBLOG_FAIL,
UNREBLOG_REQUEST,
UNREBLOG_FAIL,
FAVOURITE_REQUEST, FAVOURITE_REQUEST,
FAVOURITE_FAIL, FAVOURITE_FAIL,
UNFAVOURITE_REQUEST, UNFAVOURITE_REQUEST,
@ -16,6 +12,10 @@ import {
UNBOOKMARK_REQUEST, UNBOOKMARK_REQUEST,
UNBOOKMARK_FAIL, UNBOOKMARK_FAIL,
} from '../actions/interactions'; } from '../actions/interactions';
import {
reblog,
unreblog,
} from '../actions/interactions_typed';
import { import {
STATUS_MUTE_SUCCESS, STATUS_MUTE_SUCCESS,
STATUS_UNMUTE_SUCCESS, STATUS_UNMUTE_SUCCESS,
@ -65,6 +65,7 @@ const statusTranslateUndo = (state, id) => {
const initialState = ImmutableMap(); const initialState = ImmutableMap();
/** @type {import('@reduxjs/toolkit').Reducer<typeof initialState>} */
export default function statuses(state = initialState, action) { export default function statuses(state = initialState, action) {
switch(action.type) { switch(action.type) {
case STATUS_FETCH_REQUEST: case STATUS_FETCH_REQUEST:
@ -91,14 +92,6 @@ export default function statuses(state = initialState, action) {
return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'bookmarked'], false); return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'bookmarked'], false);
case UNBOOKMARK_FAIL: case UNBOOKMARK_FAIL:
return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'bookmarked'], true); return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'bookmarked'], true);
case REBLOG_REQUEST:
return state.setIn([action.status.get('id'), 'reblogged'], true);
case REBLOG_FAIL:
return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'reblogged'], false);
case UNREBLOG_REQUEST:
return state.setIn([action.status.get('id'), 'reblogged'], false);
case UNREBLOG_FAIL:
return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'reblogged'], true);
case STATUS_MUTE_SUCCESS: case STATUS_MUTE_SUCCESS:
return state.setIn([action.id, 'muted'], true); return state.setIn([action.id, 'muted'], true);
case STATUS_UNMUTE_SUCCESS: case STATUS_UNMUTE_SUCCESS:
@ -128,6 +121,15 @@ export default function statuses(state = initialState, action) {
case STATUS_TRANSLATE_UNDO: case STATUS_TRANSLATE_UNDO:
return statusTranslateUndo(state, action.id); return statusTranslateUndo(state, action.id);
default: default:
return state; if(reblog.pending.match(action))
return state.setIn([action.meta.arg.statusId, 'reblogged'], true);
else if(reblog.rejected.match(action))
return state.get(action.meta.arg.statusId) === undefined ? state : state.setIn([action.meta.arg.statusId, 'reblogged'], false);
else if(unreblog.pending.match(action))
return state.setIn([action.meta.arg.statusId, 'reblogged'], false);
else if(unreblog.rejected.match(action))
return state.get(action.meta.arg.statusId) === undefined ? state : state.setIn([action.meta.arg.statusId, 'reblogged'], true);
else
return state;
} }
} }

View file

@ -2,6 +2,8 @@ import { createAsyncThunk } from '@reduxjs/toolkit';
// eslint-disable-next-line @typescript-eslint/no-restricted-imports // eslint-disable-next-line @typescript-eslint/no-restricted-imports
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import type { BaseThunkAPI } from '@reduxjs/toolkit/dist/createAsyncThunk';
import type { AppDispatch, RootState } from './store'; import type { AppDispatch, RootState } from './store';
export const useAppDispatch = useDispatch.withTypes<AppDispatch>(); export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
@ -13,8 +15,192 @@ export interface AsyncThunkRejectValue {
error?: unknown; error?: unknown;
} }
interface AppMeta {
skipLoading?: boolean;
}
export const createAppAsyncThunk = createAsyncThunk.withTypes<{ export const createAppAsyncThunk = createAsyncThunk.withTypes<{
state: RootState; state: RootState;
dispatch: AppDispatch; dispatch: AppDispatch;
rejectValue: AsyncThunkRejectValue; rejectValue: AsyncThunkRejectValue;
}>(); }>();
type AppThunkApi = Pick<
BaseThunkAPI<
RootState,
unknown,
AppDispatch,
AsyncThunkRejectValue,
AppMeta,
AppMeta
>,
'getState' | 'dispatch'
>;
interface AppThunkOptions {
skipLoading?: boolean;
}
const createBaseAsyncThunk = createAsyncThunk.withTypes<{
state: RootState;
dispatch: AppDispatch;
rejectValue: AsyncThunkRejectValue;
fulfilledMeta: AppMeta;
rejectedMeta: AppMeta;
}>();
export function createThunk<Arg = void, Returned = void>(
name: string,
creator: (arg: Arg, api: AppThunkApi) => Returned | Promise<Returned>,
options: AppThunkOptions = {},
) {
return createBaseAsyncThunk(
name,
async (
arg: Arg,
{ getState, dispatch, fulfillWithValue, rejectWithValue },
) => {
try {
const result = await creator(arg, { dispatch, getState });
return fulfillWithValue(result, {
skipLoading: options.skipLoading,
});
} catch (error) {
return rejectWithValue({ error }, { skipLoading: true });
}
},
{
getPendingMeta() {
if (options.skipLoading) return { skipLoading: true };
return {};
},
},
);
}
const discardLoadDataInPayload = Symbol('discardLoadDataInPayload');
type DiscardLoadData = typeof discardLoadDataInPayload;
type OnData<LoadDataResult, ReturnedData> = (
data: LoadDataResult,
api: AppThunkApi & {
discardLoadData: DiscardLoadData;
},
) => ReturnedData | DiscardLoadData | Promise<ReturnedData | DiscardLoadData>;
// Overload when there is no `onData` method, the payload is the `onData` result
export function createDataLoadingThunk<
LoadDataResult,
Args extends Record<string, unknown>,
>(
name: string,
loadData: (args: Args) => Promise<LoadDataResult>,
thunkOptions?: AppThunkOptions,
): ReturnType<typeof createThunk<Args, LoadDataResult>>;
// Overload when the `onData` method returns discardLoadDataInPayload, then the payload is empty
export function createDataLoadingThunk<
LoadDataResult,
Args extends Record<string, unknown>,
>(
name: string,
loadData: (args: Args) => Promise<LoadDataResult>,
onDataOrThunkOptions?:
| AppThunkOptions
| OnData<LoadDataResult, DiscardLoadData>,
thunkOptions?: AppThunkOptions,
): ReturnType<typeof createThunk<Args, void>>;
// Overload when the `onData` method returns nothing, then the mayload is the `onData` result
export function createDataLoadingThunk<
LoadDataResult,
Args extends Record<string, unknown>,
>(
name: string,
loadData: (args: Args) => Promise<LoadDataResult>,
onDataOrThunkOptions?: AppThunkOptions | OnData<LoadDataResult, void>,
thunkOptions?: AppThunkOptions,
): ReturnType<typeof createThunk<Args, LoadDataResult>>;
// Overload when there is an `onData` method returning something
export function createDataLoadingThunk<
LoadDataResult,
Args extends Record<string, unknown>,
Returned,
>(
name: string,
loadData: (args: Args) => Promise<LoadDataResult>,
onDataOrThunkOptions?: AppThunkOptions | OnData<LoadDataResult, Returned>,
thunkOptions?: AppThunkOptions,
): ReturnType<typeof createThunk<Args, Returned>>;
/**
* This function creates a Redux Thunk that handles loading data asynchronously (usually from the API), dispatching `pending`, `fullfilled` and `rejected` actions.
*
* You can run a callback on the `onData` results to either dispatch side effects or modify the payload.
*
* It is a wrapper around RTK's [`createAsyncThunk`](https://redux-toolkit.js.org/api/createAsyncThunk)
* @param name Prefix for the actions types
* @param loadData Function that loads the data. It's (object) argument will become the thunk's argument
* @param onDataOrThunkOptions
* Callback called on the results from `loadData`.
*
* First argument will be the return from `loadData`.
*
* Second argument is an object with: `dispatch`, `getState` and `discardLoadData`.
* It can return:
* - `undefined` (or no explicit return), meaning that the `onData` results will be the payload
* - `discardLoadData` to discard the `onData` results and return an empty payload
* - anything else, which will be the payload
*
* You can also omit this parameter and pass `thunkOptions` directly
* @param maybeThunkOptions
* Additional Mastodon specific options for the thunk. Currently supports:
* - `skipLoading` to avoid showing the loading bar when the request is in progress
* @returns The created thunk
*/
export function createDataLoadingThunk<
LoadDataResult,
Args extends Record<string, unknown>,
Returned,
>(
name: string,
loadData: (args: Args) => Promise<LoadDataResult>,
onDataOrThunkOptions?: AppThunkOptions | OnData<LoadDataResult, Returned>,
maybeThunkOptions?: AppThunkOptions,
) {
let onData: OnData<LoadDataResult, Returned> | undefined;
let thunkOptions: AppThunkOptions | undefined;
if (typeof onDataOrThunkOptions === 'function') onData = onDataOrThunkOptions;
else if (typeof onDataOrThunkOptions === 'object')
thunkOptions = onDataOrThunkOptions;
if (maybeThunkOptions) {
thunkOptions = maybeThunkOptions;
}
return createThunk<Args, Returned>(
name,
async (arg, { getState, dispatch }) => {
const data = await loadData(arg);
if (!onData) return data as Returned;
const result = await onData(data, {
dispatch,
getState,
discardLoadData: discardLoadDataInPayload,
});
// if there is no return in `onData`, we return the `onData` result
if (typeof result === 'undefined') return data as Returned;
// the user explicitely asked to discard the payload
else if (result === discardLoadDataInPayload)
return undefined as Returned;
else return result;
},
thunkOptions,
);
}

View file

@ -3,6 +3,8 @@
class ActivityPub::Parser::StatusParser class ActivityPub::Parser::StatusParser
include JsonLdHelper include JsonLdHelper
NORMALIZED_LOCALE_NAMES = LanguagesHelper::SUPPORTED_LOCALES.keys.index_by(&:downcase).freeze
# @param [Hash] json # @param [Hash] json
# @param [Hash] options # @param [Hash] options
# @option options [String] :followers_collection # @option options [String] :followers_collection
@ -89,6 +91,13 @@ class ActivityPub::Parser::StatusParser
end end
def language def language
lang = raw_language_code
lang.presence && NORMALIZED_LOCALE_NAMES.fetch(lang.downcase.to_sym, lang)
end
private
def raw_language_code
if content_language_map? if content_language_map?
@object['contentMap'].keys.first @object['contentMap'].keys.first
elsif name_language_map? elsif name_language_map?
@ -102,8 +111,6 @@ class ActivityPub::Parser::StatusParser
@object['directMessage'] @object['directMessage']
end end
private
def audience_to def audience_to
as_array(@object['to'] || @json['to']).map { |x| value_or_id(x) } as_array(@object['to'] || @json['to']).map { |x| value_or_id(x) }
end end

View file

@ -852,7 +852,6 @@ an:
delete: Borrar delete: Borrar
edit_preset: Editar aviso predeterminau edit_preset: Editar aviso predeterminau
empty: Encara no has definiu garra preajuste d'alvertencia. empty: Encara no has definiu garra preajuste d'alvertencia.
title: Editar configuración predeterminada d'avisos
webhooks: webhooks:
add_new: Anyadir endpoint add_new: Anyadir endpoint
delete: Eliminar delete: Eliminar

View file

@ -1013,7 +1013,6 @@ ar:
delete: حذف delete: حذف
edit_preset: تعديل نموذج التحذير edit_preset: تعديل نموذج التحذير
empty: لم تحدد أي إعدادات تحذير مسبقة بعد. empty: لم تحدد أي إعدادات تحذير مسبقة بعد.
title: إدارة نماذج التحذير
webhooks: webhooks:
add_new: إضافة نقطة نهاية add_new: إضافة نقطة نهاية
delete: حذف delete: حذف

View file

@ -400,8 +400,6 @@ ast:
usable: Pue usase usable: Pue usase
title: Tendencies title: Tendencies
trending: En tendencia trending: En tendencia
warning_presets:
title: Xestión d'alvertencies preconfiguraes
webhooks: webhooks:
add_new: Amestar un estremu add_new: Amestar un estremu
delete: Desaniciar delete: Desaniciar

View file

@ -983,7 +983,6 @@ be:
delete: Выдаліць delete: Выдаліць
edit_preset: Рэдагаваць шаблон папярэджання edit_preset: Рэдагаваць шаблон папярэджання
empty: Вы яшчэ не вызначылі ніякіх шаблонаў папярэджанняў. empty: Вы яшчэ не вызначылі ніякіх шаблонаў папярэджанняў.
title: Кіраванне шаблонамі папярэджанняў
webhooks: webhooks:
add_new: Дадаць канцавую кропку add_new: Дадаць канцавую кропку
delete: Выдаліць delete: Выдаліць

View file

@ -951,7 +951,7 @@ bg:
delete: Изтриване delete: Изтриване
edit_preset: Редакция на предварителните настройки edit_preset: Редакция на предварителните настройки
empty: Все още няма предварителни настройки за предупрежденията. empty: Все още няма предварителни настройки за предупрежденията.
title: Управление на предварителните настройки title: Предупредителни образци
webhooks: webhooks:
add_new: Добавяне на крайна точка add_new: Добавяне на крайна точка
delete: Изтриване delete: Изтриване

View file

@ -951,7 +951,7 @@ ca:
delete: Elimina delete: Elimina
edit_preset: Edita l'avís predeterminat edit_preset: Edita l'avís predeterminat
empty: Encara no has definit cap preavís. empty: Encara no has definit cap preavís.
title: Gestiona les configuracions predefinides dels avisos title: Predefinicions d'avís
webhooks: webhooks:
add_new: Afegir extrem add_new: Afegir extrem
delete: Elimina delete: Elimina

View file

@ -548,7 +548,6 @@ ckb:
add_new: زیادکردنی نوێ add_new: زیادکردنی نوێ
delete: سڕینەوە delete: سڕینەوە
edit_preset: دەستکاریکردنی ئاگاداری پێشگریمان edit_preset: دەستکاریکردنی ئاگاداری پێشگریمان
title: بەڕێوەبردنی ئاگادارکردنەوە پێش‌سازدان
admin_mailer: admin_mailer:
new_pending_account: new_pending_account:
body: وردەکاریهەژمارە نوێیەکە لە خوارەوەیە. دەتوانیت ئەم نەرمەکالا پەسەند بکەیت یان ڕەت بکەیتەوە. body: وردەکاریهەژمارە نوێیەکە لە خوارەوەیە. دەتوانیت ئەم نەرمەکالا پەسەند بکەیت یان ڕەت بکەیتەوە.

View file

@ -510,7 +510,6 @@ co:
add_new: Aghjunghje add_new: Aghjunghje
delete: Sguassà delete: Sguassà
edit_preset: Cambià a preselezzione d'avertimentu edit_preset: Cambià a preselezzione d'avertimentu
title: Amministrà e preselezzione d'avertimentu
admin_mailer: admin_mailer:
new_pending_account: new_pending_account:
body: I ditagli di u novu contu sò quì sottu. Pudete appruvà o righjittà a dumanda. body: I ditagli di u novu contu sò quì sottu. Pudete appruvà o righjittà a dumanda.

View file

@ -984,7 +984,6 @@ cs:
delete: Smazat delete: Smazat
edit_preset: Upravit předlohu pro varování edit_preset: Upravit předlohu pro varování
empty: Zatím jste nedefinovali žádné předlohy varování. empty: Zatím jste nedefinovali žádné předlohy varování.
title: Spravovat předlohy pro varování
webhooks: webhooks:
add_new: Přidat koncový bod add_new: Přidat koncový bod
delete: Smazat delete: Smazat

View file

@ -297,6 +297,7 @@ cy:
update_custom_emoji_html: Mae %{name} wedi diweddaru emoji %{target} update_custom_emoji_html: Mae %{name} wedi diweddaru emoji %{target}
update_domain_block_html: Mae %{name} wedi diweddaru bloc parth %{target} update_domain_block_html: Mae %{name} wedi diweddaru bloc parth %{target}
update_ip_block_html: Mae %{name} wedi newid rheol IP %{target} update_ip_block_html: Mae %{name} wedi newid rheol IP %{target}
update_report_html: Mae %{name} wedi diweddaru adroddiad %{target}
update_status_html: Mae %{name} wedi diweddaru postiad gan %{target} update_status_html: Mae %{name} wedi diweddaru postiad gan %{target}
update_user_role_html: Mae %{name} wedi newid rôl %{target} update_user_role_html: Mae %{name} wedi newid rôl %{target}
deleted_account: cyfrif wedi'i ddileu deleted_account: cyfrif wedi'i ddileu
@ -1018,7 +1019,7 @@ cy:
delete: Dileu delete: Dileu
edit_preset: Golygu rhagosodiad rhybudd edit_preset: Golygu rhagosodiad rhybudd
empty: Nid ydych wedi diffinio unrhyw ragosodiadau rhybudd eto. empty: Nid ydych wedi diffinio unrhyw ragosodiadau rhybudd eto.
title: Rheoli rhagosodiadau rhybudd title: Rhagosodiadau rhybuddion
webhooks: webhooks:
add_new: Ychwanegu diweddbwynt add_new: Ychwanegu diweddbwynt
delete: Dileu delete: Dileu

View file

@ -951,7 +951,7 @@ da:
delete: Slet delete: Slet
edit_preset: Redigér advarselsforvalg edit_preset: Redigér advarselsforvalg
empty: Ingen advarselsforvalg defineret endnu. empty: Ingen advarselsforvalg defineret endnu.
title: Håndtérr advarselsforvalg title: Præindstillinger for advarsel
webhooks: webhooks:
add_new: Tilføj endepunkt add_new: Tilføj endepunkt
delete: Slet delete: Slet

View file

@ -951,7 +951,7 @@ de:
delete: Löschen delete: Löschen
edit_preset: Warnvorlage bearbeiten edit_preset: Warnvorlage bearbeiten
empty: Du hast noch keine Warnvorlagen hinzugefügt. empty: Du hast noch keine Warnvorlagen hinzugefügt.
title: Warnvorlagen verwalten title: Warnvorlagen
webhooks: webhooks:
add_new: Endpunkt hinzufügen add_new: Endpunkt hinzufügen
delete: Löschen delete: Löschen

View file

@ -22,7 +22,7 @@ lt:
action: Patvirtinti el. pašto adresą action: Patvirtinti el. pašto adresą
action_with_app: Patvirtinti ir grįžti į %{app} action_with_app: Patvirtinti ir grįžti į %{app}
explanation: Šiuo el. pašto adresu sukūrei paskyrą %{host}. Iki jos aktyvavimo liko vienas paspaudimas. Jei tai buvo ne tu, ignoruok šį el. laišką. explanation: Šiuo el. pašto adresu sukūrei paskyrą %{host}. Iki jos aktyvavimo liko vienas paspaudimas. Jei tai buvo ne tu, ignoruok šį el. laišką.
explanation_when_pending: Šiuo el. pašto adresu pateikei paraišką pakvietimui į %{host}. Kai patvirtinsi savo el. pašto adresą, mes peržiūrėsime tavo paraišką. Gali prisijungti ir pakeisti savo duomenis arba ištrinti paskyrą, tačiau negalėsi naudotis daugeliu funkcijų, kol tavo paskyra nebus patvirtinta. Jei tavo paraiška bus atmesta, duomenys bus pašalinti, todėl jokių papildomų veiksmų iš tavęs nereikės. Jei tai buvo ne tu, ignoruok šį el. laišką. explanation_when_pending: Šiuo el. pašto adresu pateikei paraišką pakvietimui į %{host}. Kai patvirtinsi savo el. pašto adresą, mes peržiūrėsime tavo paraišką. Gali prisijungti ir pakeisti savo duomenis arba ištrinti paskyrą, bet negalėsi naudotis daugeliu funkcijų, kol tavo paskyra nebus patvirtinta. Jei tavo paraiška bus atmesta, duomenys bus pašalinti, todėl jokių papildomų veiksmų iš tavęs nereikės. Jei tai buvo ne tu, ignoruok šį el. laišką.
extra_html: Taip pat peržiūrėk <a href="%{terms_path}">serverio taisykles</a> ir <a href="%{policy_path}">mūsų paslaugų teikimo sąlygas</a>. extra_html: Taip pat peržiūrėk <a href="%{terms_path}">serverio taisykles</a> ir <a href="%{policy_path}">mūsų paslaugų teikimo sąlygas</a>.
subject: 'Mastodon: patvirtinimo instrukcijos %{instance}' subject: 'Mastodon: patvirtinimo instrukcijos %{instance}'
title: Patvirtinti el. pašto adresą title: Patvirtinti el. pašto adresą

View file

@ -903,7 +903,6 @@ el:
delete: Διαγραφή delete: Διαγραφή
edit_preset: Ενημέρωση προκαθορισμένης προειδοποίησης edit_preset: Ενημέρωση προκαθορισμένης προειδοποίησης
empty: Δεν έχετε ακόμη ορίσει κάποια προκαθορισμένη προειδοποίηση. empty: Δεν έχετε ακόμη ορίσει κάποια προκαθορισμένη προειδοποίηση.
title: Διαχείριση προκαθορισμένων προειδοποιήσεων
webhooks: webhooks:
add_new: Προσθήκη σημείου τερματισμού add_new: Προσθήκη σημείου τερματισμού
delete: Διαγραφή delete: Διαγραφή

View file

@ -950,7 +950,6 @@ en-GB:
delete: Delete delete: Delete
edit_preset: Edit warning preset edit_preset: Edit warning preset
empty: You haven't defined any warning presets yet. empty: You haven't defined any warning presets yet.
title: Warning presets
webhooks: webhooks:
add_new: Add endpoint add_new: Add endpoint
delete: Delete delete: Delete

View file

@ -919,7 +919,6 @@ eo:
delete: Forigi delete: Forigi
edit_preset: Redakti avertan antaŭagordon edit_preset: Redakti avertan antaŭagordon
empty: Vi ankoraŭ ne difinis iun ajn antaŭagordon de averto. empty: Vi ankoraŭ ne difinis iun ajn antaŭagordon de averto.
title: Administri avertajn antaŭagordojn
webhooks: webhooks:
add_new: Aldoni finpunkton add_new: Aldoni finpunkton
delete: Forigi delete: Forigi

View file

@ -951,7 +951,7 @@ es-AR:
delete: Eliminar delete: Eliminar
edit_preset: Editar preajuste de advertencia edit_preset: Editar preajuste de advertencia
empty: Aún no ha definido ningún preajuste de advertencia. empty: Aún no ha definido ningún preajuste de advertencia.
title: Administrar preajustes de advertencia title: Preajustes de advertencia
webhooks: webhooks:
add_new: Agregar punto final add_new: Agregar punto final
delete: Eliminar delete: Eliminar

View file

@ -951,7 +951,7 @@ es-MX:
delete: Borrar delete: Borrar
edit_preset: Editar aviso predeterminado edit_preset: Editar aviso predeterminado
empty: Aún no has definido ningún preajuste de advertencia. empty: Aún no has definido ningún preajuste de advertencia.
title: Editar configuración predeterminada de avisos title: Preajustes de advertencia
webhooks: webhooks:
add_new: Añadir endpoint add_new: Añadir endpoint
delete: Eliminar delete: Eliminar

View file

@ -951,7 +951,7 @@ es:
delete: Borrar delete: Borrar
edit_preset: Editar aviso predeterminado edit_preset: Editar aviso predeterminado
empty: Aún no has definido ningún preajuste de advertencia. empty: Aún no has definido ningún preajuste de advertencia.
title: Editar configuración predeterminada de avisos title: Preajustes de advertencia
webhooks: webhooks:
add_new: Añadir endpoint add_new: Añadir endpoint
delete: Eliminar delete: Eliminar

View file

@ -949,7 +949,6 @@ et:
delete: Kustuta delete: Kustuta
edit_preset: Hoiatuse eelseadistuse muutmine edit_preset: Hoiatuse eelseadistuse muutmine
empty: Hoiatuste eelseadeid pole defineeritud. empty: Hoiatuste eelseadeid pole defineeritud.
title: Halda hoiatuste eelseadistusi
webhooks: webhooks:
add_new: Lisa lõpp-punkt add_new: Lisa lõpp-punkt
delete: Kustuta delete: Kustuta

View file

@ -952,7 +952,6 @@ eu:
delete: Ezabatu delete: Ezabatu
edit_preset: Editatu abisu aurre-ezarpena edit_preset: Editatu abisu aurre-ezarpena
empty: Ez duzu abisu aurrezarpenik definitu oraindik. empty: Ez duzu abisu aurrezarpenik definitu oraindik.
title: Kudeatu abisu aurre-ezarpenak
webhooks: webhooks:
add_new: Gehitu amaiera-puntua add_new: Gehitu amaiera-puntua
delete: Ezabatu delete: Ezabatu

View file

@ -808,7 +808,6 @@ fa:
delete: زدودن delete: زدودن
edit_preset: ویرایش هشدار پیش‌فرض edit_preset: ویرایش هشدار پیش‌فرض
empty: هنز هیچ پیش‌تنظیم هشداری را تعریف نکرده‌اید. empty: هنز هیچ پیش‌تنظیم هشداری را تعریف نکرده‌اید.
title: مدیریت هشدارهای پیش‌فرض
webhooks: webhooks:
add_new: افزودن نقطهٔ پایانی add_new: افزودن نقطهٔ پایانی
delete: حذف delete: حذف

View file

@ -951,7 +951,6 @@ fi:
delete: Poista delete: Poista
edit_preset: Muokkaa varoituksen esiasetusta edit_preset: Muokkaa varoituksen esiasetusta
empty: Et ole vielä määrittänyt yhtäkään varoitusten esiasetusta. empty: Et ole vielä määrittänyt yhtäkään varoitusten esiasetusta.
title: Hallitse varoitusten esiasetuksia
webhooks: webhooks:
add_new: Lisää päätepiste add_new: Lisää päätepiste
delete: Poista delete: Poista

View file

@ -951,7 +951,7 @@ fo:
delete: Strika delete: Strika
edit_preset: Rætta ávaringar-undanstilling edit_preset: Rætta ávaringar-undanstilling
empty: Tú hevur ikki ásett nakrar ávaringar-undanstillingar enn. empty: Tú hevur ikki ásett nakrar ávaringar-undanstillingar enn.
title: Stýr ávaringar-undanstillingar title: Undanstillingar fyri ávaring
webhooks: webhooks:
add_new: Legg endapunkt afturat add_new: Legg endapunkt afturat
delete: Strika delete: Strika

View file

@ -949,7 +949,6 @@ fr-CA:
delete: Supprimer delete: Supprimer
edit_preset: Éditer les avertissements prédéfinis edit_preset: Éditer les avertissements prédéfinis
empty: Vous n'avez pas encore créé de paramètres prédéfinis pour les avertissements. empty: Vous n'avez pas encore créé de paramètres prédéfinis pour les avertissements.
title: Gérer les avertissements prédéfinis
webhooks: webhooks:
add_new: Ajouter un point de terminaison add_new: Ajouter un point de terminaison
delete: Supprimer delete: Supprimer

View file

@ -949,7 +949,6 @@ fr:
delete: Supprimer delete: Supprimer
edit_preset: Éditer les avertissements prédéfinis edit_preset: Éditer les avertissements prédéfinis
empty: Vous n'avez pas encore créé de paramètres prédéfinis pour les avertissements. empty: Vous n'avez pas encore créé de paramètres prédéfinis pour les avertissements.
title: Gérer les avertissements prédéfinis
webhooks: webhooks:
add_new: Ajouter un point de terminaison add_new: Ajouter un point de terminaison
delete: Supprimer delete: Supprimer

View file

@ -949,7 +949,6 @@ fy:
delete: Fuortsmite delete: Fuortsmite
edit_preset: Foarynstelling foar warskôging bewurkje edit_preset: Foarynstelling foar warskôging bewurkje
empty: Jo hawwe noch gjin foarynstellingen foar warskôgingen tafoege. empty: Jo hawwe noch gjin foarynstellingen foar warskôgingen tafoege.
title: Foarynstellingen foar warskôgingen beheare
webhooks: webhooks:
add_new: Einpunt tafoegje add_new: Einpunt tafoegje
delete: Fuortsmite delete: Fuortsmite

View file

@ -983,7 +983,6 @@ gd:
delete: Sguab às delete: Sguab às
edit_preset: Deasaich rabhadh ro-shuidhichte edit_preset: Deasaich rabhadh ro-shuidhichte
empty: Cha do mhìnich thu ro-sheataichean rabhaidhean fhathast. empty: Cha do mhìnich thu ro-sheataichean rabhaidhean fhathast.
title: Stiùirich na rabhaidhean ro-shuidhichte
webhooks: webhooks:
add_new: Cuir puing-dheiridh ris add_new: Cuir puing-dheiridh ris
delete: Sguab às delete: Sguab às

View file

@ -951,7 +951,7 @@ gl:
delete: Eliminar delete: Eliminar
edit_preset: Editar aviso preestablecido edit_preset: Editar aviso preestablecido
empty: Non definiches os avisos prestablecidos. empty: Non definiches os avisos prestablecidos.
title: Xestionar avisos preestablecidos title: Preestablecidos de advertencia
webhooks: webhooks:
add_new: Engadir punto de extremo add_new: Engadir punto de extremo
delete: Eliminar delete: Eliminar

View file

@ -985,7 +985,7 @@ he:
delete: למחוק delete: למחוק
edit_preset: ערוך/י טקסט מוכן מראש לאזהרה edit_preset: ערוך/י טקסט מוכן מראש לאזהרה
empty: לא הגדרת עדיין שום טקסט מוכן מראש לאזהרה. empty: לא הגדרת עדיין שום טקסט מוכן מראש לאזהרה.
title: ניהול טקסטים מוכנים מראש לאזהרות title: תצורת אזהרות
webhooks: webhooks:
add_new: הוספת נקודת קצה add_new: הוספת נקודת קצה
delete: מחיקה delete: מחיקה

View file

@ -951,7 +951,7 @@ hu:
delete: Törlés delete: Törlés
edit_preset: Figyelmeztetés szerkesztése edit_preset: Figyelmeztetés szerkesztése
empty: Nem definiáltál még egyetlen figyelmeztetést sem. empty: Nem definiáltál még egyetlen figyelmeztetést sem.
title: Figyelmeztetések title: Figyelmeztető szövegek
webhooks: webhooks:
add_new: Végpont hozzáadása add_new: Végpont hozzáadása
delete: Törlés delete: Törlés

View file

@ -951,7 +951,7 @@ ia:
delete: Deler delete: Deler
edit_preset: Rediger aviso predefinite edit_preset: Rediger aviso predefinite
empty: Tu non ha ancora definite alcun avisos predefinite. empty: Tu non ha ancora definite alcun avisos predefinite.
title: Gerer avisos predefinite title: Predefinitiones de avisos
webhooks: webhooks:
add_new: Adder terminal add_new: Adder terminal
delete: Deler delete: Deler

View file

@ -831,7 +831,6 @@ id:
delete: Hapus delete: Hapus
edit_preset: Sunting preset peringatan edit_preset: Sunting preset peringatan
empty: Anda belum mendefinisikan peringatan apapun. empty: Anda belum mendefinisikan peringatan apapun.
title: Kelola preset peringatan
webhooks: webhooks:
add_new: Tambah titik akhir add_new: Tambah titik akhir
delete: Hapus delete: Hapus

View file

@ -950,7 +950,6 @@ ie:
delete: Deleter delete: Deleter
edit_preset: Modificar prefiguration de avise edit_preset: Modificar prefiguration de avise
empty: Vu ancor ha definit null prefigurationes de avise. empty: Vu ancor ha definit null prefigurationes de avise.
title: Modificar prefigurationes de avise
webhooks: webhooks:
add_new: Adjunter punctu terminal add_new: Adjunter punctu terminal
delete: Deleter delete: Deleter

View file

@ -928,7 +928,6 @@ io:
delete: Efacez delete: Efacez
edit_preset: Modifikez avertfixito edit_preset: Modifikez avertfixito
empty: Vu ne fixis irga avertfixito til nun. empty: Vu ne fixis irga avertfixito til nun.
title: Jerez avertfixiti
webhooks: webhooks:
add_new: Insertez finpunto add_new: Insertez finpunto
delete: Efacez delete: Efacez

View file

@ -953,7 +953,7 @@ is:
delete: Eyða delete: Eyða
edit_preset: Breyta forstilltri aðvörun edit_preset: Breyta forstilltri aðvörun
empty: Þú hefur ekki enn skilgreint neinar aðvaranaforstillingar. empty: Þú hefur ekki enn skilgreint neinar aðvaranaforstillingar.
title: Sýsla með forstilltar aðvaranir title: Forstilltar aðvaranir
webhooks: webhooks:
add_new: Bæta við endapunkti add_new: Bæta við endapunkti
delete: Eyða delete: Eyða

View file

@ -951,7 +951,7 @@ it:
delete: Cancella delete: Cancella
edit_preset: Modifica avviso predefinito edit_preset: Modifica avviso predefinito
empty: Non hai ancora definito alcun avviso preimpostato. empty: Non hai ancora definito alcun avviso preimpostato.
title: Gestisci avvisi predefiniti title: Preimpostazioni di avviso
webhooks: webhooks:
add_new: Aggiungi endpoint add_new: Aggiungi endpoint
delete: Elimina delete: Elimina

View file

@ -933,7 +933,6 @@ ja:
delete: 削除 delete: 削除
edit_preset: プリセット警告文を編集 edit_preset: プリセット警告文を編集
empty: まだプリセット警告文が作成されていません。 empty: まだプリセット警告文が作成されていません。
title: プリセット警告文を管理
webhooks: webhooks:
add_new: エンドポイントを追加 add_new: エンドポイントを追加
delete: 削除 delete: 削除

View file

@ -299,7 +299,6 @@ kk:
add_new: Add nеw add_new: Add nеw
delete: Deletе delete: Deletе
edit_preset: Edit warning prеset edit_preset: Edit warning prеset
title: Manage warning presеts
admin_mailer: admin_mailer:
new_pending_account: new_pending_account:
body: Жаңа есептік жазба туралы мәліметтер төменде берілген. Бұл қолданбаны мақұлдауыңызға немесе қабылдамауыңызға болады. body: Жаңа есептік жазба туралы мәліметтер төменде берілген. Бұл қолданбаны мақұлдауыңызға немесе қабылдамауыңызға болады.

View file

@ -936,7 +936,7 @@ ko:
delete: 삭제 delete: 삭제
edit_preset: 경고 프리셋 편집 edit_preset: 경고 프리셋 편집
empty: 아직 어떤 경고 틀도 정의되지 않았습니다. empty: 아직 어떤 경고 틀도 정의되지 않았습니다.
title: 경고 틀 관리 title: 경고 프리셋
webhooks: webhooks:
add_new: 엔드포인트 추가 add_new: 엔드포인트 추가
delete: 삭제 delete: 삭제

View file

@ -849,7 +849,6 @@ ku:
delete: Jê bibe delete: Jê bibe
edit_preset: Hişyariyên pêşsazkirî serrast bike edit_preset: Hişyariyên pêşsazkirî serrast bike
empty: Te hin tu hişyariyên pêşsazkirî destnîşan nekirine. empty: Te hin tu hişyariyên pêşsazkirî destnîşan nekirine.
title: Hişyariyên pêşsazkirî bi rêve bibe
webhooks: webhooks:
add_new: Xala dawîbûnê tevlî bike add_new: Xala dawîbûnê tevlî bike
delete: Jê bibe delete: Jê bibe

View file

@ -950,7 +950,6 @@ lad:
delete: Efasa delete: Efasa
edit_preset: Edita avizo predeterminado edit_preset: Edita avizo predeterminado
empty: Ainda no tienes definido ningun avizo predeterminado. empty: Ainda no tienes definido ningun avizo predeterminado.
title: Edita konfigurasyon predeterminada de avizos
webhooks: webhooks:
add_new: Adjusta endpoint add_new: Adjusta endpoint
delete: Efasa delete: Efasa

View file

@ -451,7 +451,7 @@ lt:
filter: filter:
all: Visi all: Visi
available: Pasiekiamas available: Pasiekiamas
expired: Pasibaigęs expired: Nebegaliojantis
title: Filtras title: Filtras
title: Kvietimai title: Kvietimai
relays: relays:
@ -505,9 +505,15 @@ lt:
unresolved: Neišspręsti unresolved: Neišspręsti
updated_at: Atnaujinti updated_at: Atnaujinti
roles: roles:
categories:
invites: Kvietimai
everyone: Numatytieji leidimai everyone: Numatytieji leidimai
everyone_full_description_html: Tai <strong>bazinis vaidmuo</strong>, turintis įtakos <strong>visiems naudotojams</strong>, net ir tiems, kurie neturi priskirto vaidmens. Visi kiti vaidmenys iš jo paveldi teises. everyone_full_description_html: Tai <strong>bazinis vaidmuo</strong>, turintis įtakos <strong>visiems naudotojams</strong>, net ir tiems, kurie neturi priskirto vaidmens. Visi kiti vaidmenys iš jo paveldi teises.
privileges: privileges:
invite_users: Kviesti naudotojus
invite_users_description: Leidžia naudotojams pakviesti naujus žmones į serverį.
manage_invites: Tvarkyti kvietimus
manage_invites_description: Leidžia naudotojams naršyti ir deaktyvuoti kvietimų nuorodas.
manage_taxonomies_description: Leidžia naudotojams peržiūrėti tendencingą turinį ir atnaujinti saitažodžių nustatymus manage_taxonomies_description: Leidžia naudotojams peržiūrėti tendencingą turinį ir atnaujinti saitažodžių nustatymus
settings: settings:
captcha_enabled: captcha_enabled:
@ -522,12 +528,34 @@ lt:
registrations: registrations:
moderation_recommandation: Prieš atidarant registraciją visiems, įsitikink, kad turi tinkamą ir reaguojančią prižiūrėjimo komandą! moderation_recommandation: Prieš atidarant registraciją visiems, įsitikink, kad turi tinkamą ir reaguojančią prižiūrėjimo komandą!
software_updates: software_updates:
description: Rekomenduojama nuolat atnaujinti Mastodon diegyklę, kad galėtum naudotis naujausiais pataisymais ir funkcijomis. Be to, kartais labai svarbu laiku naujinti Mastodon, kad būtų išvengta saugumo problemų. Dėl šių priežasčių Mastodon kas 30 minučių tikrina, ar yra atnaujinimų, ir praneša tau apie tai pagal tavo el. pašto pranešimų parinktis. description: Rekomenduojama nuolat atnaujinti Mastodon diegyklę, kad galėtum naudotis naujausiais pataisymais ir funkcijomis. Be to, kartais labai svarbu laiku atnaujinti Mastodon, kad būtų išvengta saugumo problemų. Dėl šių priežasčių Mastodon kas 30 minučių tikrina, ar yra naujinimų, ir praneša tau apie tai pagal tavo el. pašto pranešimų parinktis.
documentation_link: Sužinoti daugiau
release_notes: Leidimo informacija
title: Galimi naujinimai
type: Tipas
types:
major: Pagrindinis leidimas
minor: Nedidelis leidimas
patch: Pataiso leidimas riktų taisymai ir lengvai pritaikomi pakeitimai
version: Versija
statuses: statuses:
account: Autorius (-ė)
application: Programa
back_to_account: Grįžti į paskyros puslapį back_to_account: Grįžti į paskyros puslapį
back_to_report: Grįžti į ataskaitos puslapį
batch:
remove_from_report: Pašalinti iš ataskaitos
deleted: Ištrinta
favourites: Mėgstami
history: Versijų istorija
in_reply_to: Atsakydant į
language: Kalba
media: media:
title: Medija title: Medija
no_status_selected: Jokie statusai nebuvo pakeisti, nes niekas nepasirinkta metadata: Metaduomenys
no_status_selected: Jokie įrašai nebuvo pakeisti, nes nė vienas buvo pasirinktas
open: Atidaryti įrašą
original_status: Originalus įrašas
title: Paskyros statusai title: Paskyros statusai
trending: Tendencinga trending: Tendencinga
with_media: Su medija with_media: Su medija
@ -537,6 +565,7 @@ lt:
elasticsearch_preset: elasticsearch_preset:
message_html: Tavo Elasticsearch klasteris turi daugiau nei vieną mazgą, bet Mastodon nėra sukonfigūruotas juos naudoti. message_html: Tavo Elasticsearch klasteris turi daugiau nei vieną mazgą, bet Mastodon nėra sukonfigūruotas juos naudoti.
elasticsearch_preset_single_node: elasticsearch_preset_single_node:
action: Žiūrėti dokumentaciją
message_html: Tavo Elasticsearch klasteris turi tik vieną mazgą, <code>ES_PRESET</code> turėtų būti nustatyta į <code>single_node_cluster</code>. message_html: Tavo Elasticsearch klasteris turi tik vieną mazgą, <code>ES_PRESET</code> turėtų būti nustatyta į <code>single_node_cluster</code>.
title: Administracija title: Administracija
trends: trends:
@ -571,8 +600,20 @@ lt:
disallow_account: Neleisti autorių (-ę) disallow_account: Neleisti autorių (-ę)
no_status_selected: Jokie tendencingi įrašai nebuvo pakeisti, nes nė vienas iš jų nebuvo pasirinktas no_status_selected: Jokie tendencingi įrašai nebuvo pakeisti, nes nė vienas iš jų nebuvo pasirinktas
not_discoverable: Autorius (-ė) nesutiko, kad būtų galima juos atrasti not_discoverable: Autorius (-ė) nesutiko, kad būtų galima juos atrasti
shared_by:
few: Bendrinta arba pamėgta %{friendly_count} kartus
many: Bendrinta arba pamėgta %{friendly_count} karto
one: Bendrinta arba pamėgta vieną kartą
other: Bendrinta arba pamėgta %{friendly_count} kartų
title: Tendencingi įrašai title: Tendencingi įrašai
tags: tags:
dashboard:
tag_accounts_measure: unikalūs naudojimai
tag_languages_dimension: Populiariausios kalbos
tag_servers_dimension: Populiariausi serveriai
tag_servers_measure: skirtingi serveriai
tag_uses_measure: bendri naudojimai
listable: Gali būti siūloma
not_trendable: Nepasirodys tendencijose not_trendable: Nepasirodys tendencijose
title: Tendencingos saitažodžiai title: Tendencingos saitažodžiai
trendable: Gali pasirodyti tendencijose trendable: Gali pasirodyti tendencijose
@ -583,7 +624,6 @@ lt:
add_new: Pridėti naują add_new: Pridėti naują
delete: Ištrinti delete: Ištrinti
edit_preset: Keisti įspėjimo nustatymus edit_preset: Keisti įspėjimo nustatymus
title: Valdyti įspėjimo nustatymus
webhooks: webhooks:
description_html: "<strong>Webhook</strong> leidžia Mastodon siųsti <strong>realaus laiko pranešimus</strong> apie pasirinktus įvykius į tavo programą, kad programa galėtų <strong>automatiškai paleisti reakcijas</strong>." description_html: "<strong>Webhook</strong> leidžia Mastodon siųsti <strong>realaus laiko pranešimus</strong> apie pasirinktus įvykius į tavo programą, kad programa galėtų <strong>automatiškai paleisti reakcijas</strong>."
events: Įvykiai events: Įvykiai
@ -627,21 +667,32 @@ lt:
warning: Būkite atsargūs su šia informacija. Niekada jos nesidalinkite! warning: Būkite atsargūs su šia informacija. Niekada jos nesidalinkite!
your_token: Tavo prieigos raktas your_token: Tavo prieigos raktas
auth: auth:
confirmations:
welcome_title: Sveiki, %{name}!
delete_account: Ištrinti paskyrą delete_account: Ištrinti paskyrą
delete_account_html: Jeigu norite ištrinti savo paskyrą, galite eiti <a href="%{path}">čia</a>. Jūsų prašys patvirtinti pasirinkimą. delete_account_html: Jeigu norite ištrinti savo paskyrą, galite eiti <a href="%{path}">čia</a>. Jūsų prašys patvirtinti pasirinkimą.
description:
prefix_invited_by_user: "@%{name} kviečia prisijungti prie šio Mastodon serverio!"
prefix_sign_up: Užsiregistruok Mastodon šiandien!
didnt_get_confirmation: Negavai patvirtinimo nuorodos?
dont_have_your_security_key: Neturi saugumo rakto? dont_have_your_security_key: Neturi saugumo rakto?
forgot_password: Pamiršote slaptažodį? forgot_password: Pamiršai slaptažodį?
invalid_reset_password_token: Slaptažodžio atkūrimo žetonas netinkamas arba jo galiojimo laikas pasibaigęs. Prašykite naujo žetono. invalid_reset_password_token: Slaptažodžio atkūrimo raktas yra netinkamas arba nebegaliojantis. Paprašyk naujo.
log_in_with: Prisijungti su
login: Prisijungti login: Prisijungti
logout: Atsijungti logout: Atsijungti
migrate_account: Prisijungti prie kitos paskyros migrate_account: Prisijungti prie kitos paskyros
migrate_account_html: Jeigu norite nukreipti šią paskyrą į kita, galite tai <a href="%{path}">konfiguruoti čia</a>. migrate_account_html: Jeigu norite nukreipti šią paskyrą į kita, galite tai <a href="%{path}">konfiguruoti čia</a>.
or_log_in_with: Arba prisijungti su or_log_in_with: Arba prisijungti su
providers:
cas: CAS
saml: SAML
register: Užsiregistruoti register: Užsiregistruoti
reset_password: Atstatyti slaptažodį reset_password: Atstatyti slaptažodį
rules: rules:
invited_by: 'Gali prisijungti prie %{domain} pagal kvietimą, kurį gavai iš:' invited_by: 'Gali prisijungti prie %{domain} pagal kvietimą, kurį gavai iš:'
preamble_invited: Prieš tęsiant, atsižvelk į pagrindines taisykles, kurias nustatė %{domain} prižiūrėtojai. preamble_invited: Prieš tęsiant, atsižvelk į pagrindines taisykles, kurias nustatė %{domain} prižiūrėtojai.
title_invited: Esi pakviestas.
security: Apsauga security: Apsauga
set_new_password: Nustatyti naują slaptažodį set_new_password: Nustatyti naują slaptažodį
status: status:
@ -673,6 +724,9 @@ lt:
success_msg: Tavo paskyra buvo sėkmingai ištrinta success_msg: Tavo paskyra buvo sėkmingai ištrinta
disputes: disputes:
strikes: strikes:
created_at: Data
title_actions:
none: Įspėjimas
your_appeal_approved: Tavo apeliacija buvo patvirtinta your_appeal_approved: Tavo apeliacija buvo patvirtinta
your_appeal_pending: Pateikei apeliaciją your_appeal_pending: Pateikei apeliaciją
your_appeal_rejected: Tavo apeliacija buvo atmesta your_appeal_rejected: Tavo apeliacija buvo atmesta
@ -699,6 +753,8 @@ lt:
request: Prašyti savo archyvo request: Prašyti savo archyvo
size: Dydis size: Dydis
blocks: Jūs blokuojate blocks: Jūs blokuojate
bookmarks: Žymės
csv: CSV
domain_blocks: Domeno blokai domain_blocks: Domeno blokai
lists: Sąrašai lists: Sąrašai
mutes: Jūs tildote mutes: Jūs tildote
@ -708,11 +764,14 @@ lt:
hint_html: "<strong>Savo profilyje parodyk svarbiausius saitažodžius.</strong> Tai puikus įrankis kūrybiniams darbams ir ilgalaikiams projektams sekti, todėl svarbiausios saitažodžiai rodomi matomoje vietoje profilyje ir leidžia greitai pasiekti tavo paties įrašus." hint_html: "<strong>Savo profilyje parodyk svarbiausius saitažodžius.</strong> Tai puikus įrankis kūrybiniams darbams ir ilgalaikiams projektams sekti, todėl svarbiausios saitažodžiai rodomi matomoje vietoje profilyje ir leidžia greitai pasiekti tavo paties įrašus."
filters: filters:
contexts: contexts:
home: Namų laiko juosta account: Profiliai
notifications: Priminimai home: Pagrindinis ir sąrašai
notifications: Pranešimai
public: Viešieji laiko skalės public: Viešieji laiko skalės
thread: Pokalbiai thread: Pokalbiai
edit: edit:
add_keyword: Pridėti raktažodį
keywords: Raktažodžiai
title: Keisti filtrą title: Keisti filtrą
errors: errors:
invalid_context: Jokio arba netinkamas pateiktas kontekstas invalid_context: Jokio arba netinkamas pateiktas kontekstas
@ -726,9 +785,14 @@ lt:
all: Visi all: Visi
changes_saved_msg: Pakeitimai sėkmingai išsaugoti! changes_saved_msg: Pakeitimai sėkmingai išsaugoti!
copy: Kopijuoti copy: Kopijuoti
delete: Ištrinti
deselect: Panaikinti visus žymėjimus
order_by: Tvarkyti pagal order_by: Tvarkyti pagal
save_changes: Išsaugoti pakeitimus save_changes: Išsaugoti pakeitimus
today: šiandien
imports: imports:
errors:
too_large: Failas per didelis.
modes: modes:
merge: Sulieti merge: Sulieti
merge_long: Išsaugoti esančius įrašus ir pridėti naujus merge_long: Išsaugoti esančius įrašus ir pridėti naujus
@ -744,7 +808,7 @@ lt:
upload: Įkelti upload: Įkelti
invites: invites:
delete: Deaktyvuoti delete: Deaktyvuoti
expired: Pasibaigė expired: Nebegaliojantis
expires_in: expires_in:
'1800': 30 minučių '1800': 30 minučių
'21600': 6 valandų '21600': 6 valandų
@ -753,28 +817,29 @@ lt:
'604800': 1 savaitės '604800': 1 savaitės
'86400': 1 dienos '86400': 1 dienos
expires_in_prompt: Niekada expires_in_prompt: Niekada
generate: Generuoti generate: Generuoti kvietimo nuorodą
invalid: Šis kvietimas negalioja. invalid: Šis kvietimas negalioja.
invited_by: 'Jus pakvietė:' invited_by: 'Tave pakvietė:'
max_uses: max_uses:
few: "%{count} naudojimai" few: "%{count} naudojimai"
many: "%{count} naudojimo" many: "%{count} naudojimo"
one: 1 naudojimas one: 1 naudojimas
other: "%{count} naudojimų" other: "%{count} naudojimų"
max_uses_prompt: Nėra limito max_uses_prompt: Nėra ribojimo
prompt: Generuok ir bendrink nuorodas su kitais, kad suteiktum prieigą prie šio serverio prompt: Generuok ir bendrink nuorodas su kitais, kad suteiktum prieigą prie šio serverio
table: table:
expires_at: Baigsis expires_at: Baigsis
uses: Naudojimai uses: Naudojimai
title: Pakviesti žmones title: Kviesti žmones
media_attachments: media_attachments:
validations: validations:
images_and_video: Negalima pridėti video prie statuso, kuris jau turi nuotrauką images_and_video: Negalima pridėti video prie statuso, kuris jau turi nuotrauką
too_many: Negalima pridėti daugiau nei 4 failų too_many: Negalima pridėti daugiau nei 4 failų
migrations: migrations:
acct: slapyvardis@domenas naujam vartotojui acct: Perkelta į
cancel: Atšaukti nukreipimą
moderation: moderation:
title: Moderacija title: Prižiūrėjimas
notification_mailer: notification_mailer:
favourite: favourite:
body: 'Tavo įrašą pamėgo %{name}:' body: 'Tavo įrašą pamėgo %{name}:'
@ -801,11 +866,19 @@ lt:
notifications: notifications:
email_events: Įvykiai, skirti el. laiško pranešimams email_events: Įvykiai, skirti el. laiško pranešimams
email_events_hint: 'Pasirink įvykius, apie kuriuos nori gauti pranešimus:' email_events_hint: 'Pasirink įvykius, apie kuriuos nori gauti pranešimus:'
number:
human:
decimal_units:
units:
billion: mlrd.
million: mln.
thousand: tūkst.
pagination: pagination:
newer: Naujesnis newer: Naujesnis
next: Kitas next: Kitas
older: Senesnis older: Senesnis
prev: Ankstesnis prev: Ankstesnis
truncate: "&hellip;"
preferences: preferences:
other: Kita other: Kita
posting_defaults: Skelbimo numatytosios nuostatos posting_defaults: Skelbimo numatytosios nuostatos
@ -829,6 +902,7 @@ lt:
dormant: Neaktyvus dormant: Neaktyvus
followers: Sekėjai followers: Sekėjai
following: Sekama following: Sekama
invited: Pakviestas
last_active: Paskutinį kartą aktyvus last_active: Paskutinį kartą aktyvus
most_recent: Naujausias most_recent: Naujausias
moved: Perkelta moved: Perkelta
@ -851,24 +925,35 @@ lt:
date: Data date: Data
description: "%{browser} ant %{platform}" description: "%{browser} ant %{platform}"
explanation: Čia rodomos web naršyklės prijungtos prie Jūsų Mastodon paskyros. explanation: Čia rodomos web naršyklės prijungtos prie Jūsų Mastodon paskyros.
ip: IP
platforms: platforms:
adobe_air: Adobe Air
android: Android android: Android
blackberry: BlackBerry
chrome_os: ChromeOS
firefox_os: Firefox OS
ios: iOS ios: iOS
kai_os: KaiOS kai_os: KaiOS
linux: Linux
mac: macOS mac: macOS
unknown_platform: Nežinoma platforma
windows: Windows windows: Windows
windows_mobile: Windows Mobile windows_mobile: Windows Mobile
windows_phone: Windows Phone windows_phone: Windows Phone
revoke: Atšaukti revoke: Naikinti
revoke_success: Seansas sėkmingai panaikintas. revoke_success: Seansas sėkmingai panaikintas.
title: Seansai title: Seansai
settings: settings:
authorized_apps: Autorizuotos aplikacijos account: Paskyra
account_settings: Paskyros nustatymai
aliases: Paskyros pseudonimai
appearance: Išvaizda
authorized_apps: Leidžiamos programėlės
back: Grįžti į Mastodon back: Grįžti į Mastodon
delete: Paskyros trynimas delete: Paskyros trynimas
development: Plėtojimas development: Kūrimas
edit_profile: Keisti profilį edit_profile: Redaguoti profilį
export: Informacijos eksportas export: Duomenų eksportas
featured_tags: Rodomi saitažodžiai featured_tags: Rodomi saitažodžiai
import: Importuoti import: Importuoti
migrate: Paskyros migracija migrate: Paskyros migracija
@ -879,10 +964,22 @@ lt:
severed_relationships: Nutrūkę sąryšiai severed_relationships: Nutrūkę sąryšiai
two_factor_authentication: Dviejų veiksnių autentikacija two_factor_authentication: Dviejų veiksnių autentikacija
severed_relationships: severed_relationships:
download: Atsisiųsti (%{count})
preamble: Užblokavus domeną arba prižiūrėtojams nusprendus pristabdyti nuotolinio serverio veiklą, gali prarasti sekimus ir sekėjus. Kai taip atsitiks, galėsi atsisiųsti nutrauktų sąryšių sąrašus, kad juos patikrinti ir galbūt importuoti į kitą serverį. preamble: Užblokavus domeną arba prižiūrėtojams nusprendus pristabdyti nuotolinio serverio veiklą, gali prarasti sekimus ir sekėjus. Kai taip atsitiks, galėsi atsisiųsti nutrauktų sąryšių sąrašus, kad juos patikrinti ir galbūt importuoti į kitą serverį.
type: Įvykis
statuses: statuses:
attached: attached:
audio:
few: "%{count} garso įrašai"
many: "%{count} garso įrašo"
one: "%{count} garso įrašas"
other: "%{count} garso įrašų"
description: 'Pridėta: %{attached}' description: 'Pridėta: %{attached}'
image:
few: "%{count} vaizdai"
many: "%{count} vaizdo"
one: "%{count} vaizdas"
other: "%{count} vaizdų"
boosted_from_html: Pakelta iš %{acct_link} boosted_from_html: Pakelta iš %{acct_link}
content_warning: 'Turinio įspėjimas: %{warning}' content_warning: 'Turinio įspėjimas: %{warning}'
open_in_web: Atidaryti naudojan Web open_in_web: Atidaryti naudojan Web
@ -891,11 +988,14 @@ lt:
limit: Jūs jau prisegėte maksimalų toot'ų skaičų limit: Jūs jau prisegėte maksimalų toot'ų skaičų
ownership: Kitų vartotojų toot'ai negali būti prisegti ownership: Kitų vartotojų toot'ai negali būti prisegti
reblog: Pakeltos žinutės negali būti prisegtos reblog: Pakeltos žinutės negali būti prisegtos
show_more: Daugiau poll:
vote: Balsuoti
show_more: Rodyti daugiau
show_thread: Rodyti giją
visibilities: visibilities:
private: Tik sekėjams private: Tik sekėjams
private_long: rodyti tik sekėjams private_long: rodyti tik sekėjams
public: Viešas public: Vieša
public_long: visi gali matyti public_long: visi gali matyti
unlisted: Neįtrauktas į sąrašus unlisted: Neįtrauktas į sąrašus
unlisted_long: matyti gali visi, bet nėra išvardyti į viešąsias laiko skales unlisted_long: matyti gali visi, bet nėra išvardyti į viešąsias laiko skales
@ -904,6 +1004,7 @@ lt:
keep_polls_hint: Neištrina jokių tavo apklausų keep_polls_hint: Neištrina jokių tavo apklausų
keep_self_bookmark: Laikyti įrašus, kuriuos pažymėjai keep_self_bookmark: Laikyti įrašus, kuriuos pažymėjai
keep_self_bookmark_hint: Neištrina tavo pačių įrašų, jei esi juos pažymėjęs (-usi) keep_self_bookmark_hint: Neištrina tavo pačių įrašų, jei esi juos pažymėjęs (-usi)
keep_self_fav_hint: Neištrina tavo pačių įrašų, jei esi juos pamėgęs (-usi)
stream_entries: stream_entries:
sensitive_content: Jautrus turinys sensitive_content: Jautrus turinys
themes: themes:
@ -912,7 +1013,8 @@ lt:
mastodon-light: Mastodon (šviesi) mastodon-light: Mastodon (šviesi)
system: Automatinis (naudoti sistemos temą) system: Automatinis (naudoti sistemos temą)
two_factor_authentication: two_factor_authentication:
disable: Išjungti add: Pridėti
disable: Išjungti 2FA
enabled: Dviejų veiksnių autentikacija įjungta enabled: Dviejų veiksnių autentikacija įjungta
enabled_success: Dviejų veiksnių autentikacija sėkmingai įjungta enabled_success: Dviejų veiksnių autentikacija sėkmingai įjungta
generate_recovery_codes: Sugeneruoti atkūrimo kodus generate_recovery_codes: Sugeneruoti atkūrimo kodus

View file

@ -961,7 +961,6 @@ lv:
delete: Dzēst delete: Dzēst
edit_preset: Labot iepriekš iestatītus brīdinājumus edit_preset: Labot iepriekš iestatītus brīdinājumus
empty: Tu vēl neesi definējis iepriekš iestatītos brīdinājumus. empty: Tu vēl neesi definējis iepriekš iestatītos brīdinājumus.
title: Pārvaldīt brīdinājuma iestatījumus
webhooks: webhooks:
add_new: Pievienot galapunktu add_new: Pievienot galapunktu
delete: Dzēst delete: Dzēst

View file

@ -924,7 +924,6 @@ ms:
delete: Padam delete: Padam
edit_preset: Edit pratetap amaran edit_preset: Edit pratetap amaran
empty: Anda belum menentukan sebarang pratetap amaran lagi. empty: Anda belum menentukan sebarang pratetap amaran lagi.
title: Urus pratetap amaran
webhooks: webhooks:
add_new: Tambah titik akhir add_new: Tambah titik akhir
delete: Padam delete: Padam

View file

@ -909,7 +909,6 @@ my:
delete: ဖျက်ပါ delete: ဖျက်ပါ
edit_preset: ကြိုသတိပေးချက်ကို ပြင်ဆင်ပါ edit_preset: ကြိုသတိပေးချက်ကို ပြင်ဆင်ပါ
empty: ကြိုသတိပေးချက်များကို မသတ်မှတ်ရသေးပါ။ empty: ကြိုသတိပေးချက်များကို မသတ်မှတ်ရသေးပါ။
title: ကြိုသတိပေးချက်များကို စီမံပါ
webhooks: webhooks:
add_new: ဆုံးမှတ် ထည့်ပါ add_new: ဆုံးမှတ် ထည့်ပါ
delete: ဖျက်ပါ delete: ဖျက်ပါ

View file

@ -951,7 +951,7 @@ nl:
delete: Verwijderen delete: Verwijderen
edit_preset: Preset voor waarschuwing bewerken edit_preset: Preset voor waarschuwing bewerken
empty: Je hebt nog geen presets voor waarschuwingen toegevoegd. empty: Je hebt nog geen presets voor waarschuwingen toegevoegd.
title: Presets voor waarschuwingen beheren title: Presets voor waarschuwingen
webhooks: webhooks:
add_new: Eindpunt toevoegen add_new: Eindpunt toevoegen
delete: Verwijderen delete: Verwijderen

View file

@ -950,7 +950,6 @@ nn:
delete: Slett delete: Slett
edit_preset: Endr åtvaringsoppsett edit_preset: Endr åtvaringsoppsett
empty: Du har ikke definert noen forhåndsinnstillinger for advarsler enda. empty: Du har ikke definert noen forhåndsinnstillinger for advarsler enda.
title: Handsam åtvaringsoppsett
webhooks: webhooks:
add_new: Legg til endepunkt add_new: Legg til endepunkt
delete: Slett delete: Slett

View file

@ -944,7 +944,6 @@
delete: Slett delete: Slett
edit_preset: Rediger advarsel forhåndsinnstilling edit_preset: Rediger advarsel forhåndsinnstilling
empty: Du har ikke definert noen forhåndsinnstillinger for varsler enda. empty: Du har ikke definert noen forhåndsinnstillinger for varsler enda.
title: Endre forhåndsinnstillinger for advarsler
webhooks: webhooks:
add_new: Legg til endepunkt add_new: Legg til endepunkt
delete: Slett delete: Slett

View file

@ -430,7 +430,6 @@ oc:
add_new: Najustar un nòu add_new: Najustar un nòu
delete: Escafar delete: Escafar
edit_preset: Modificar lo tèxt predefinit davertiment edit_preset: Modificar lo tèxt predefinit davertiment
title: Gerir los tèxtes predefinits
webhooks: webhooks:
delete: Suprimir delete: Suprimir
disable: Desactivar disable: Desactivar

View file

@ -985,7 +985,7 @@ pl:
delete: Usuń delete: Usuń
edit_preset: Edytuj szablon ostrzeżenia edit_preset: Edytuj szablon ostrzeżenia
empty: Nie zdefiniowano jeszcze żadnych szablonów ostrzegawczych. empty: Nie zdefiniowano jeszcze żadnych szablonów ostrzegawczych.
title: Zarządzaj szablonami ostrzeżeń title: Zapisane ostrzeżenia
webhooks: webhooks:
add_new: Dodaj punkt końcowy add_new: Dodaj punkt końcowy
delete: Usuń delete: Usuń

View file

@ -950,7 +950,6 @@ pt-BR:
delete: Excluir delete: Excluir
edit_preset: Editar o aviso pré-definido edit_preset: Editar o aviso pré-definido
empty: Você ainda não definiu nenhuma predefinição de alerta. empty: Você ainda não definiu nenhuma predefinição de alerta.
title: Gerenciar os avisos pré-definidos
webhooks: webhooks:
add_new: Adicionar endpoint add_new: Adicionar endpoint
delete: Excluir delete: Excluir

View file

@ -951,7 +951,7 @@ pt-PT:
delete: Eliminar delete: Eliminar
edit_preset: Editar o aviso predefinido edit_preset: Editar o aviso predefinido
empty: Ainda não definiu nenhum aviso predefinido. empty: Ainda não definiu nenhum aviso predefinido.
title: Gerir os avisos predefinidos title: Predefinições de aviso
webhooks: webhooks:
add_new: Adicionar endpoint add_new: Adicionar endpoint
delete: Eliminar delete: Eliminar

View file

@ -978,7 +978,6 @@ ru:
delete: Удалить delete: Удалить
edit_preset: Удалить шаблон предупреждения edit_preset: Удалить шаблон предупреждения
empty: Вы еще не определили пресеты предупреждений. empty: Вы еще не определили пресеты предупреждений.
title: Управление шаблонами предупреждений
webhooks: webhooks:
add_new: Добавить конечную точку add_new: Добавить конечную точку
delete: Удалить delete: Удалить

View file

@ -535,7 +535,6 @@ sc:
delete: Cantzella delete: Cantzella
edit_preset: Modìfica s'avisu predefinidu edit_preset: Modìfica s'avisu predefinidu
empty: No as cunfiguradu ancora perunu avisu predefinidu. empty: No as cunfiguradu ancora perunu avisu predefinidu.
title: Gesti is cunfiguratziones predefinidas de is avisos
webhooks: webhooks:
delete: Cantzella delete: Cantzella
disable: Disativa disable: Disativa

View file

@ -842,7 +842,6 @@ sco:
delete: Delete delete: Delete
edit_preset: Edit warnin preset edit_preset: Edit warnin preset
empty: Ye huvnae definit onie warnin presets yit. empty: Ye huvnae definit onie warnin presets yit.
title: Manage warnin presets
webhooks: webhooks:
add_new: Add enpynt add_new: Add enpynt
delete: Delete delete: Delete

View file

@ -726,7 +726,6 @@ si:
delete: මකන්න delete: මකන්න
edit_preset: අනතුරු ඇඟවීමේ පෙර සැකසුම සංස්කරණය කරන්න edit_preset: අනතුරු ඇඟවීමේ පෙර සැකසුම සංස්කරණය කරන්න
empty: ඔබ තවම කිසිදු අනතුරු ඇඟවීමේ පෙරසිටුවක් නිර්වචනය කර නැත. empty: ඔබ තවම කිසිදු අනතුරු ඇඟවීමේ පෙරසිටුවක් නිර්වචනය කර නැත.
title: අනතුරු ඇඟවීමේ පෙරසිටුවීම් කළමනාකරණය කරන්න
webhooks: webhooks:
add_new: අන්ත ලක්ෂ්‍යය එක් කරන්න add_new: අන්ත ලක්ෂ්‍යය එක් කරන්න
delete: මකන්න delete: මකන්න

Some files were not shown because too many files have changed in this diff Show more