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 { createAppAsyncThunk } from 'flavours/glitch/store/typed_functions';
import { apiSubmitAccountNote } from 'flavours/glitch/api/accounts';
import { createDataLoadingThunk } from 'flavours/glitch/store/typed_functions';
import api from '../api';
export const submitAccountNote = createAppAsyncThunk(
export const submitAccountNote = createDataLoadingThunk(
'account_note/submit',
async (args: { id: string; value: string }) => {
const response = await api().post<ApiRelationshipJSON>(
`/api/v1/accounts/${args.id}/note`,
{
comment: args.value,
},
);
return { relationship: response.data };
},
({ accountId, note }: { accountId: string; note: string }) =>
apiSubmitAccountNote(accountId, note),
(relationship) => ({ relationship }),
{ skipLoading: true },
);

View File

@ -3,10 +3,6 @@ import api, { getLinks } from '../api';
import { fetchRelationships } from './accounts';
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_SUCCESS = 'REBLOGS_EXPAND_SUCCESS';
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_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_SUCCESS = 'UNFAVOURITE_SUCCESS';
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_FAIL = 'UNBOOKMARKED_FAIL';
export function reblog(status, visibility) {
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 * from "./interactions_typed";
export function favourite(status) {
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 LinkHeader from 'http-link-header';
@ -40,11 +40,11 @@ const authorizationTokenFromInitialState = (): RawAxiosRequestHeaders => {
};
// eslint-disable-next-line import/no-default-export
export default function api() {
export default function api(withAuthorization = true) {
return axios.create({
headers: {
...csrfHeader,
...authorizationTokenFromInitialState(),
...(withAuthorization ? authorizationTokenFromInitialState() : {}),
},
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 () {
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({
loading: false,
data: res.data,

View File

@ -26,7 +26,7 @@ export default class Dimension extends PureComponent {
componentDidMount () {
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({
loading: false,
data: res.data,

View File

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

View File

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

View File

@ -34,7 +34,7 @@ export default class Retention extends PureComponent {
componentDidMount () {
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({
loading: false,
data: res.data,

View File

@ -22,7 +22,7 @@ export default class Trends extends PureComponent {
componentDidMount () {
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({
loading: false,
data: res.data,

View File

@ -115,9 +115,9 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
onModalReblog (status, privacy) {
if (status.get('reblogged')) {
dispatch(unreblog(status));
dispatch(unreblog({ statusId: status.get('id') }));
} 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 }) => ({
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) {
dispatch(reblog(status, privacy));
dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
},
onReblog (status, e) {
if (status.get('reblogged')) {
dispatch(unreblog(status));
dispatch(unreblog({ statusId: status.get('id') }));
} else {
if (e.shiftKey || !boostModal) {
this.onModalReblog(status);

View File

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

View File

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

View File

@ -346,9 +346,9 @@ class Status extends ImmutablePureComponent {
const { dispatch } = this.props;
if (status.get('reblogged')) {
dispatch(unreblog(status));
dispatch(unreblog({ statusId: status.get('id') }));
} 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 { normalizeStatusTranslation } from '../actions/importer/normalizer';
import {
REBLOG_REQUEST,
REBLOG_FAIL,
UNREBLOG_REQUEST,
UNREBLOG_FAIL,
FAVOURITE_REQUEST,
FAVOURITE_FAIL,
UNFAVOURITE_REQUEST,
@ -16,6 +12,10 @@ import {
UNBOOKMARK_REQUEST,
UNBOOKMARK_FAIL,
} from '../actions/interactions';
import {
reblog,
unreblog,
} from '../actions/interactions_typed';
import {
STATUS_MUTE_SUCCESS,
STATUS_UNMUTE_SUCCESS,
@ -65,6 +65,7 @@ const statusTranslateUndo = (state, id) => {
const initialState = ImmutableMap();
/** @type {import('@reduxjs/toolkit').Reducer<typeof initialState>} */
export default function statuses(state = initialState, action) {
switch(action.type) {
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);
case UNBOOKMARK_FAIL:
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:
return state.setIn([action.id, 'muted'], true);
case STATUS_UNMUTE_SUCCESS:
@ -128,6 +121,15 @@ export default function statuses(state = initialState, action) {
case STATUS_TRANSLATE_UNDO:
return statusTranslateUndo(state, action.id);
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
import { useDispatch, useSelector } from 'react-redux';
import type { BaseThunkAPI } from '@reduxjs/toolkit/dist/createAsyncThunk';
import type { AppDispatch, RootState } from './store';
export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
@ -13,8 +15,192 @@ export interface AsyncThunkRejectValue {
error?: unknown;
}
interface AppMeta {
skipLoading?: boolean;
}
export const createAppAsyncThunk = createAsyncThunk.withTypes<{
state: RootState;
dispatch: AppDispatch;
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 { createAppAsyncThunk } from 'mastodon/store/typed_functions';
import { apiSubmitAccountNote } from 'mastodon/api/accounts';
import { createDataLoadingThunk } from 'mastodon/store/typed_functions';
import api from '../api';
export const submitAccountNote = createAppAsyncThunk(
export const submitAccountNote = createDataLoadingThunk(
'account_note/submit',
async (args: { id: string; value: string }) => {
const response = await api().post<ApiRelationshipJSON>(
`/api/v1/accounts/${args.id}/note`,
{
comment: args.value,
},
);
return { relationship: response.data };
},
({ accountId, note }: { accountId: string; note: string }) =>
apiSubmitAccountNote(accountId, note),
(relationship) => ({ relationship }),
{ skipLoading: true },
);

View File

@ -3,10 +3,6 @@ import api, { getLinks } from '../api';
import { fetchRelationships } from './accounts';
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_SUCCESS = 'REBLOGS_EXPAND_SUCCESS';
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_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_SUCCESS = 'UNFAVOURITE_SUCCESS';
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_FAIL = 'UNBOOKMARKED_FAIL';
export function reblog(status, visibility) {
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 * from "./interactions_typed";
export function favourite(status) {
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 LinkHeader from 'http-link-header';
@ -40,11 +40,11 @@ const authorizationTokenFromInitialState = (): RawAxiosRequestHeaders => {
};
// eslint-disable-next-line import/no-default-export
export default function api() {
export default function api(withAuthorization = true) {
return axios.create({
headers: {
...csrfHeader,
...authorizationTokenFromInitialState(),
...(withAuthorization ? authorizationTokenFromInitialState() : {}),
},
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 () {
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({
loading: false,
data: res.data,

View File

@ -26,7 +26,7 @@ export default class Dimension extends PureComponent {
componentDidMount () {
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({
loading: false,
data: res.data,

View File

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

View File

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

View File

@ -34,7 +34,7 @@ export default class Retention extends PureComponent {
componentDidMount () {
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({
loading: false,
data: res.data,

View File

@ -22,7 +22,7 @@ export default class Trends extends PureComponent {
componentDidMount () {
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({
loading: false,
data: res.data,

View File

@ -96,9 +96,9 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
onModalReblog (status, privacy) {
if (status.get('reblogged')) {
dispatch(unreblog(status));
dispatch(unreblog({ statusId: status.get('id') }));
} 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 }) => ({
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) {
dispatch(reblog(status, privacy));
dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
},
onReblog (status, e) {
if (status.get('reblogged')) {
dispatch(unreblog(status));
dispatch(unreblog({ statusId: status.get('id') }));
} else {
if (e.shiftKey || !boostModal) {
this.onModalReblog(status);

View File

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

View File

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

View File

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

View File

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

View File

@ -491,7 +491,7 @@
"onboarding.actions.go_to_home": "Dodieties uz manu mājas plūsmu",
"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.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.profile.discoverable": "Padarīt manu profilu atklājamu",
"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 { normalizeStatusTranslation } from '../actions/importer/normalizer';
import {
REBLOG_REQUEST,
REBLOG_FAIL,
UNREBLOG_REQUEST,
UNREBLOG_FAIL,
FAVOURITE_REQUEST,
FAVOURITE_FAIL,
UNFAVOURITE_REQUEST,
@ -16,6 +12,10 @@ import {
UNBOOKMARK_REQUEST,
UNBOOKMARK_FAIL,
} from '../actions/interactions';
import {
reblog,
unreblog,
} from '../actions/interactions_typed';
import {
STATUS_MUTE_SUCCESS,
STATUS_UNMUTE_SUCCESS,
@ -65,6 +65,7 @@ const statusTranslateUndo = (state, id) => {
const initialState = ImmutableMap();
/** @type {import('@reduxjs/toolkit').Reducer<typeof initialState>} */
export default function statuses(state = initialState, action) {
switch(action.type) {
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);
case UNBOOKMARK_FAIL:
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:
return state.setIn([action.id, 'muted'], true);
case STATUS_UNMUTE_SUCCESS:
@ -128,6 +121,15 @@ export default function statuses(state = initialState, action) {
case STATUS_TRANSLATE_UNDO:
return statusTranslateUndo(state, action.id);
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
import { useDispatch, useSelector } from 'react-redux';
import type { BaseThunkAPI } from '@reduxjs/toolkit/dist/createAsyncThunk';
import type { AppDispatch, RootState } from './store';
export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
@ -13,8 +15,192 @@ export interface AsyncThunkRejectValue {
error?: unknown;
}
interface AppMeta {
skipLoading?: boolean;
}
export const createAppAsyncThunk = createAsyncThunk.withTypes<{
state: RootState;
dispatch: AppDispatch;
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
include JsonLdHelper
NORMALIZED_LOCALE_NAMES = LanguagesHelper::SUPPORTED_LOCALES.keys.index_by(&:downcase).freeze
# @param [Hash] json
# @param [Hash] options
# @option options [String] :followers_collection
@ -89,6 +91,13 @@ class ActivityPub::Parser::StatusParser
end
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?
@object['contentMap'].keys.first
elsif name_language_map?
@ -102,8 +111,6 @@ class ActivityPub::Parser::StatusParser
@object['directMessage']
end
private
def audience_to
as_array(@object['to'] || @json['to']).map { |x| value_or_id(x) }
end

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -510,7 +510,6 @@ co:
add_new: Aghjunghje
delete: Sguassà
edit_preset: Cambià a preselezzione d'avertimentu
title: Amministrà e preselezzione d'avertimentu
admin_mailer:
new_pending_account:
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
edit_preset: Upravit předlohu pro varování
empty: Zatím jste nedefinovali žádné předlohy varování.
title: Spravovat předlohy pro varování
webhooks:
add_new: Přidat koncový bod
delete: Smazat

View File

@ -297,6 +297,7 @@ cy:
update_custom_emoji_html: Mae %{name} wedi diweddaru emoji %{target}
update_domain_block_html: Mae %{name} wedi diweddaru bloc parth %{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_user_role_html: Mae %{name} wedi newid rôl %{target}
deleted_account: cyfrif wedi'i ddileu
@ -1018,7 +1019,7 @@ cy:
delete: Dileu
edit_preset: Golygu rhagosodiad rhybudd
empty: Nid ydych wedi diffinio unrhyw ragosodiadau rhybudd eto.
title: Rheoli rhagosodiadau rhybudd
title: Rhagosodiadau rhybuddion
webhooks:
add_new: Ychwanegu diweddbwynt
delete: Dileu

View File

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

View File

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

View File

@ -22,7 +22,7 @@ lt:
action: Patvirtinti el. pašto adresą
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_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>.
subject: 'Mastodon: patvirtinimo instrukcijos %{instance}'
title: Patvirtinti el. pašto adresą

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -949,7 +949,6 @@ fr-CA:
delete: Supprimer
edit_preset: Éditer les avertissements prédéfinis
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:
add_new: Ajouter un point de terminaison
delete: Supprimer

View File

@ -949,7 +949,6 @@ fr:
delete: Supprimer
edit_preset: Éditer les avertissements prédéfinis
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:
add_new: Ajouter un point de terminaison
delete: Supprimer

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -451,7 +451,7 @@ lt:
filter:
all: Visi
available: Pasiekiamas
expired: Pasibaigęs
expired: Nebegaliojantis
title: Filtras
title: Kvietimai
relays:
@ -505,9 +505,15 @@ lt:
unresolved: Neišspręsti
updated_at: Atnaujinti
roles:
categories:
invites: Kvietimai
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.
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
settings:
captcha_enabled:
@ -522,12 +528,34 @@ lt:
registrations:
moderation_recommandation: Prieš atidarant registraciją visiems, įsitikink, kad turi tinkamą ir reaguojančią prižiūrėjimo komandą!
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:
account: Autorius (-ė)
application: Programa
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:
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
trending: Tendencinga
with_media: Su medija
@ -537,6 +565,7 @@ lt:
elasticsearch_preset:
message_html: Tavo Elasticsearch klasteris turi daugiau nei vieną mazgą, bet Mastodon nėra sukonfigūruotas juos naudoti.
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>.
title: Administracija
trends:
@ -571,8 +600,20 @@ lt:
disallow_account: Neleisti autorių (-ę)
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
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
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
title: Tendencingos saitažodžiai
trendable: Gali pasirodyti tendencijose
@ -583,7 +624,6 @@ lt:
add_new: Pridėti naują
delete: Ištrinti
edit_preset: Keisti įspėjimo nustatymus
title: Valdyti įspėjimo nustatymus
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>."
events: Įvykiai
@ -627,21 +667,32 @@ lt:
warning: Būkite atsargūs su šia informacija. Niekada jos nesidalinkite!
your_token: Tavo prieigos raktas
auth:
confirmations:
welcome_title: Sveiki, %{name}!
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ą.
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?
forgot_password: Pamiršote slaptažodį?
invalid_reset_password_token: Slaptažodžio atkūrimo žetonas netinkamas arba jo galiojimo laikas pasibaigęs. Prašykite naujo žetono.
forgot_password: Pamiršai slaptažodį?
invalid_reset_password_token: Slaptažodžio atkūrimo raktas yra netinkamas arba nebegaliojantis. Paprašyk naujo.
log_in_with: Prisijungti su
login: Prisijungti
logout: Atsijungti
migrate_account: Prisijungti prie kitos paskyros
migrate_account_html: Jeigu norite nukreipti šią paskyrą į kita, galite tai <a href="%{path}">konfiguruoti čia</a>.
or_log_in_with: Arba prisijungti su
providers:
cas: CAS
saml: SAML
register: Užsiregistruoti
reset_password: Atstatyti slaptažodį
rules:
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.
title_invited: Esi pakviestas.
security: Apsauga
set_new_password: Nustatyti naują slaptažodį
status:
@ -673,6 +724,9 @@ lt:
success_msg: Tavo paskyra buvo sėkmingai ištrinta
disputes:
strikes:
created_at: Data
title_actions:
none: Įspėjimas
your_appeal_approved: Tavo apeliacija buvo patvirtinta
your_appeal_pending: Pateikei apeliaciją
your_appeal_rejected: Tavo apeliacija buvo atmesta
@ -699,6 +753,8 @@ lt:
request: Prašyti savo archyvo
size: Dydis
blocks: Jūs blokuojate
bookmarks: Žymės
csv: CSV
domain_blocks: Domeno blokai
lists: Sąrašai
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."
filters:
contexts:
home: Namų laiko juosta
notifications: Priminimai
account: Profiliai
home: Pagrindinis ir sąrašai
notifications: Pranešimai
public: Viešieji laiko skalės
thread: Pokalbiai
edit:
add_keyword: Pridėti raktažodį
keywords: Raktažodžiai
title: Keisti filtrą
errors:
invalid_context: Jokio arba netinkamas pateiktas kontekstas
@ -726,9 +785,14 @@ lt:
all: Visi
changes_saved_msg: Pakeitimai sėkmingai išsaugoti!
copy: Kopijuoti
delete: Ištrinti
deselect: Panaikinti visus žymėjimus
order_by: Tvarkyti pagal
save_changes: Išsaugoti pakeitimus
today: šiandien
imports:
errors:
too_large: Failas per didelis.
modes:
merge: Sulieti
merge_long: Išsaugoti esančius įrašus ir pridėti naujus
@ -744,7 +808,7 @@ lt:
upload: Įkelti
invites:
delete: Deaktyvuoti
expired: Pasibaigė
expired: Nebegaliojantis
expires_in:
'1800': 30 minučių
'21600': 6 valandų
@ -753,28 +817,29 @@ lt:
'604800': 1 savaitės
'86400': 1 dienos
expires_in_prompt: Niekada
generate: Generuoti
generate: Generuoti kvietimo nuorodą
invalid: Šis kvietimas negalioja.
invited_by: 'Jus pakvietė:'
invited_by: 'Tave pakvietė:'
max_uses:
few: "%{count} naudojimai"
many: "%{count} naudojimo"
one: 1 naudojimas
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
table:
expires_at: Baigsis
uses: Naudojimai
title: Pakviesti žmones
title: Kviesti žmones
media_attachments:
validations:
images_and_video: Negalima pridėti video prie statuso, kuris jau turi nuotrauką
too_many: Negalima pridėti daugiau nei 4 failų
migrations:
acct: slapyvardis@domenas naujam vartotojui
acct: Perkelta į
cancel: Atšaukti nukreipimą
moderation:
title: Moderacija
title: Prižiūrėjimas
notification_mailer:
favourite:
body: 'Tavo įrašą pamėgo %{name}:'
@ -801,11 +866,19 @@ lt:
notifications:
email_events: Įvykiai, skirti el. laiško pranešimams
email_events_hint: 'Pasirink įvykius, apie kuriuos nori gauti pranešimus:'
number:
human:
decimal_units:
units:
billion: mlrd.
million: mln.
thousand: tūkst.
pagination:
newer: Naujesnis
next: Kitas
older: Senesnis
prev: Ankstesnis
truncate: "&hellip;"
preferences:
other: Kita
posting_defaults: Skelbimo numatytosios nuostatos
@ -829,6 +902,7 @@ lt:
dormant: Neaktyvus
followers: Sekėjai
following: Sekama
invited: Pakviestas
last_active: Paskutinį kartą aktyvus
most_recent: Naujausias
moved: Perkelta
@ -851,24 +925,35 @@ lt:
date: Data
description: "%{browser} ant %{platform}"
explanation: Čia rodomos web naršyklės prijungtos prie Jūsų Mastodon paskyros.
ip: IP
platforms:
adobe_air: Adobe Air
android: Android
blackberry: BlackBerry
chrome_os: ChromeOS
firefox_os: Firefox OS
ios: iOS
kai_os: KaiOS
linux: Linux
mac: macOS
unknown_platform: Nežinoma platforma
windows: Windows
windows_mobile: Windows Mobile
windows_phone: Windows Phone
revoke: Atšaukti
revoke: Naikinti
revoke_success: Seansas sėkmingai panaikintas.
title: Seansai
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
delete: Paskyros trynimas
development: Plėtojimas
edit_profile: Keisti profilį
export: Informacijos eksportas
development: Kūrimas
edit_profile: Redaguoti profilį
export: Duomenų eksportas
featured_tags: Rodomi saitažodžiai
import: Importuoti
migrate: Paskyros migracija
@ -879,10 +964,22 @@ lt:
severed_relationships: Nutrūkę sąryšiai
two_factor_authentication: Dviejų veiksnių autentikacija
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į.
type: Įvykis
statuses:
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}'
image:
few: "%{count} vaizdai"
many: "%{count} vaizdo"
one: "%{count} vaizdas"
other: "%{count} vaizdų"
boosted_from_html: Pakelta iš %{acct_link}
content_warning: 'Turinio įspėjimas: %{warning}'
open_in_web: Atidaryti naudojan Web
@ -891,11 +988,14 @@ lt:
limit: Jūs jau prisegėte maksimalų toot'ų skaičų
ownership: Kitų vartotojų toot'ai negali būti prisegti
reblog: Pakeltos žinutės negali būti prisegtos
show_more: Daugiau
poll:
vote: Balsuoti
show_more: Rodyti daugiau
show_thread: Rodyti giją
visibilities:
private: Tik sekėjams
private_long: rodyti tik sekėjams
public: Viešas
public: Vieša
public_long: visi gali matyti
unlisted: Neįtrauktas į sąrašus
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_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_fav_hint: Neištrina tavo pačių įrašų, jei esi juos pamėgęs (-usi)
stream_entries:
sensitive_content: Jautrus turinys
themes:
@ -912,7 +1013,8 @@ lt:
mastodon-light: Mastodon (šviesi)
system: Automatinis (naudoti sistemos temą)
two_factor_authentication:
disable: Išjungti
add: Pridėti
disable: Išjungti 2FA
enabled: Dviejų veiksnių autentikacija įjungta
enabled_success: Dviejų veiksnių autentikacija sėkmingai įjungta
generate_recovery_codes: Sugeneruoti atkūrimo kodus

View File

@ -961,7 +961,6 @@ lv:
delete: Dzēst
edit_preset: Labot iepriekš iestatītus 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:
add_new: Pievienot galapunktu
delete: Dzēst

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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