instance v1 to v2 convesion
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
59115c8dc5
commit
7c752f088c
|
@ -1,10 +1,11 @@
|
||||||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
|
import { gte } from 'semver';
|
||||||
|
|
||||||
import KVStore from 'soapbox/storage/kv-store';
|
import KVStore from 'soapbox/storage/kv-store';
|
||||||
import { RootState } from 'soapbox/store';
|
import { RootState } from 'soapbox/store';
|
||||||
import { getAuthUserUrl, getMeUrl } from 'soapbox/utils/auth';
|
import { getAuthUserUrl, getMeUrl } from 'soapbox/utils/auth';
|
||||||
import { parseVersion } from 'soapbox/utils/features';
|
import { MASTODON, parseVersion, PLEROMA, REBASED } from 'soapbox/utils/features';
|
||||||
|
|
||||||
import api from '../api';
|
import api from '../api';
|
||||||
|
|
||||||
|
@ -22,25 +23,50 @@ export const getHost = (state: RootState) => {
|
||||||
export const rememberInstance = createAsyncThunk(
|
export const rememberInstance = createAsyncThunk(
|
||||||
'instance/remember',
|
'instance/remember',
|
||||||
async(host: string) => {
|
async(host: string) => {
|
||||||
return await KVStore.getItemOrError(`instance:${host}`);
|
const instance = await KVStore.getItemOrError(`instance:${host}`);
|
||||||
|
|
||||||
|
return { instance, host };
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const supportsInstanceV2 = (instance: Record<string, any>): boolean => {
|
||||||
|
const v = parseVersion(get(instance, 'version'));
|
||||||
|
return (v.software === MASTODON && gte(v.compatVersion, '4.0.0')) ||
|
||||||
|
(v.software === PLEROMA && v.build === REBASED && gte(v.version, '2.5.54'));
|
||||||
|
};
|
||||||
|
|
||||||
/** We may need to fetch nodeinfo on Pleroma < 2.1 */
|
/** We may need to fetch nodeinfo on Pleroma < 2.1 */
|
||||||
const needsNodeinfo = (instance: Record<string, any>): boolean => {
|
const needsNodeinfo = (instance: Record<string, any>): boolean => {
|
||||||
const v = parseVersion(get(instance, 'version'));
|
const v = parseVersion(get(instance, 'version'));
|
||||||
return v.software === 'Pleroma' && !get(instance, ['pleroma', 'metadata']);
|
return v.software === PLEROMA && !get(instance, ['pleroma', 'metadata']);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchInstance = createAsyncThunk<void, void, { state: RootState }>(
|
export const fetchInstance = createAsyncThunk<{ instance: Record<string, any>; host?: string | null }, string | null | undefined, { state: RootState }>(
|
||||||
'instance/fetch',
|
'instance/fetch',
|
||||||
async(_arg, { dispatch, getState, rejectWithValue }) => {
|
async(host, { dispatch, getState, rejectWithValue }) => {
|
||||||
try {
|
try {
|
||||||
const { data: instance } = await api(getState).get('/api/v1/instance');
|
const { data: instance } = await api(getState).get('/api/v1/instance');
|
||||||
|
|
||||||
|
if (supportsInstanceV2(instance)) {
|
||||||
|
return dispatch(fetchInstanceV2(host)) as any as { instance: Record<string, any>; host?: string | null };
|
||||||
|
}
|
||||||
|
|
||||||
if (needsNodeinfo(instance)) {
|
if (needsNodeinfo(instance)) {
|
||||||
dispatch(fetchNodeinfo());
|
dispatch(fetchNodeinfo());
|
||||||
}
|
}
|
||||||
return instance;
|
return { instance, host };
|
||||||
|
} catch (e) {
|
||||||
|
return rejectWithValue(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const fetchInstanceV2 = createAsyncThunk<{ instance: Record<string, any>; host?: string | null }, string | null | undefined, { state: RootState }>(
|
||||||
|
'instance/fetch',
|
||||||
|
async(host, { getState, rejectWithValue }) => {
|
||||||
|
try {
|
||||||
|
const { data: instance } = await api(getState).get('/api/v2/instance');
|
||||||
|
return { instance, host };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return rejectWithValue(e);
|
return rejectWithValue(e);
|
||||||
}
|
}
|
||||||
|
@ -52,10 +78,11 @@ export const loadInstance = createAsyncThunk<void, void, { state: RootState }>(
|
||||||
'instance/load',
|
'instance/load',
|
||||||
async(_arg, { dispatch, getState }) => {
|
async(_arg, { dispatch, getState }) => {
|
||||||
const host = getHost(getState());
|
const host = getHost(getState());
|
||||||
await Promise.all([
|
const rememberedInstance = await dispatch(rememberInstance(host || ''));
|
||||||
dispatch(rememberInstance(host || '')),
|
|
||||||
dispatch(fetchInstance()),
|
if (rememberedInstance.payload && supportsInstanceV2((rememberedInstance.payload as any).instance)) {
|
||||||
]);
|
await dispatch(fetchInstanceV2(host));
|
||||||
|
} else dispatch(fetchInstance(host));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ function useTimelineStream(...args: Parameters<typeof connectTimelineStream>) {
|
||||||
const stream = useRef<(() => void) | null>(null);
|
const stream = useRef<(() => void) | null>(null);
|
||||||
|
|
||||||
const accessToken = useAppSelector(getAccessToken);
|
const accessToken = useAppSelector(getAccessToken);
|
||||||
const streamingUrl = instance.urls?.streaming_api;
|
const streamingUrl = instance.configuration.urls.streaming;
|
||||||
|
|
||||||
const connect = () => {
|
const connect = () => {
|
||||||
if (enabled && streamingUrl && !stream.current) {
|
if (enabled && streamingUrl && !stream.current) {
|
||||||
|
|
|
@ -27,9 +27,9 @@ const generateConfig = (mode: RegistrationMode) => {
|
||||||
}];
|
}];
|
||||||
};
|
};
|
||||||
|
|
||||||
const modeFromInstance = (instance: Instance): RegistrationMode => {
|
const modeFromInstance = ({ registrations }: Instance): RegistrationMode => {
|
||||||
if (instance.approval_required && instance.registrations) return 'approval';
|
if (registrations.approval_required && registrations.enabled) return 'approval';
|
||||||
return instance.registrations ? 'open' : 'closed';
|
return registrations.enabled ? 'open' : 'closed';
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Allows changing the registration mode of the instance, eg "open", "closed", "approval" */
|
/** Allows changing the registration mode of the instance, eg "open", "closed", "approval" */
|
||||||
|
|
|
@ -47,7 +47,7 @@ const RegistrationForm: React.FC<IRegistrationForm> = ({ inviteToken }) => {
|
||||||
|
|
||||||
const locale = settings.get('locale');
|
const locale = settings.get('locale');
|
||||||
const needsConfirmation = instance.pleroma.metadata.account_activation_required;
|
const needsConfirmation = instance.pleroma.metadata.account_activation_required;
|
||||||
const needsApproval = instance.approval_required;
|
const needsApproval = instance.registrations.approval_required;
|
||||||
const supportsEmailList = features.emailList;
|
const supportsEmailList = features.emailList;
|
||||||
const supportsAccountLookup = features.accountLookup;
|
const supportsAccountLookup = features.accountLookup;
|
||||||
const birthdayRequired = instance.pleroma.metadata.birthday_required;
|
const birthdayRequired = instance.pleroma.metadata.birthday_required;
|
||||||
|
|
|
@ -12,7 +12,7 @@ interface IUploadCompose {
|
||||||
|
|
||||||
const UploadCompose: React.FC<IUploadCompose> = ({ composeId, id, onSubmit }) => {
|
const UploadCompose: React.FC<IUploadCompose> = ({ composeId, id, onSubmit }) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { description_limit: descriptionLimit } = useInstance();
|
const { pleroma: { metadata: { description_limit: descriptionLimit } } } = useInstance();
|
||||||
|
|
||||||
const media = useCompose(composeId).media_attachments.find(item => item.id === id)!;
|
const media = useCompose(composeId).media_attachments.find(item => item.id === id)!;
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { LogoText } from './logo-text';
|
||||||
|
|
||||||
const SiteBanner: React.FC = () => {
|
const SiteBanner: React.FC = () => {
|
||||||
const instance = useInstance();
|
const instance = useInstance();
|
||||||
const description = instance.short_description || instance.description;
|
const description = instance.description;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack space={3}>
|
<Stack space={3}>
|
||||||
|
|
|
@ -4,20 +4,24 @@ import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
|
||||||
import { ADMIN_CONFIG_UPDATE_REQUEST, ADMIN_CONFIG_UPDATE_SUCCESS } from 'soapbox/actions/admin';
|
import { ADMIN_CONFIG_UPDATE_REQUEST, ADMIN_CONFIG_UPDATE_SUCCESS } from 'soapbox/actions/admin';
|
||||||
import { PLEROMA_PRELOAD_IMPORT } from 'soapbox/actions/preload';
|
import { PLEROMA_PRELOAD_IMPORT } from 'soapbox/actions/preload';
|
||||||
import { type Instance, instanceSchema } from 'soapbox/schemas';
|
import { type Instance, instanceSchema } from 'soapbox/schemas';
|
||||||
|
import { instanceV1ToV2 } from 'soapbox/schemas/instance';
|
||||||
import KVStore from 'soapbox/storage/kv-store';
|
import KVStore from 'soapbox/storage/kv-store';
|
||||||
import { ConfigDB } from 'soapbox/utils/config-db';
|
import { ConfigDB } from 'soapbox/utils/config-db';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
rememberInstance,
|
rememberInstance,
|
||||||
fetchInstance,
|
fetchInstance,
|
||||||
|
fetchInstanceV2,
|
||||||
} from '../actions/instance';
|
} from '../actions/instance';
|
||||||
|
|
||||||
import type { AnyAction } from 'redux';
|
import type { AnyAction } from 'redux';
|
||||||
|
import type { APIEntity } from 'soapbox/types/entities';
|
||||||
|
|
||||||
const initialState: Instance = instanceSchema.parse({});
|
const initialState: Instance = instanceSchema.parse({});
|
||||||
|
|
||||||
const importInstance = (_state: typeof initialState, instance: unknown) => {
|
const importInstance = (_state: typeof initialState, instance: APIEntity) => {
|
||||||
return instanceSchema.parse(instance);
|
if (typeof instance.domain === 'string') return instanceSchema.parse(instance);
|
||||||
|
return instanceV1ToV2.parse(instance);
|
||||||
};
|
};
|
||||||
|
|
||||||
const preloadImport = (state: typeof initialState, action: Record<string, any>, path: string) => {
|
const preloadImport = (state: typeof initialState, action: Record<string, any>, path: string) => {
|
||||||
|
@ -45,8 +49,10 @@ const importConfigs = (state: typeof initialState, configs: ImmutableList<any>)
|
||||||
const registrationsOpen = getConfigValue(value, ':registrations_open') as boolean | undefined;
|
const registrationsOpen = getConfigValue(value, ':registrations_open') as boolean | undefined;
|
||||||
const approvalRequired = getConfigValue(value, ':account_approval_required') as boolean | undefined;
|
const approvalRequired = getConfigValue(value, ':account_approval_required') as boolean | undefined;
|
||||||
|
|
||||||
draft.registrations = registrationsOpen ?? draft.registrations;
|
draft.registrations = {
|
||||||
draft.approval_required = approvalRequired ?? draft.approval_required;
|
enabled: registrationsOpen ?? draft.registrations.enabled,
|
||||||
|
approval_required: approvalRequired ?? draft.registrations.approval_required,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (simplePolicy) {
|
if (simplePolicy) {
|
||||||
|
@ -76,9 +82,7 @@ const getHost = (instance: { uri: string }) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const persistInstance = (instance: { uri: string }) => {
|
const persistInstance = (instance: { uri: string }, host: string | null = getHost(instance)) => {
|
||||||
const host = getHost(instance);
|
|
||||||
|
|
||||||
if (host) {
|
if (host) {
|
||||||
KVStore.setItem(`instance:${host}`, instance).catch(console.error);
|
KVStore.setItem(`instance:${host}`, instance).catch(console.error);
|
||||||
}
|
}
|
||||||
|
@ -97,11 +101,13 @@ export default function instance(state = initialState, action: AnyAction) {
|
||||||
case PLEROMA_PRELOAD_IMPORT:
|
case PLEROMA_PRELOAD_IMPORT:
|
||||||
return preloadImport(state, action, '/api/v1/instance');
|
return preloadImport(state, action, '/api/v1/instance');
|
||||||
case rememberInstance.fulfilled.type:
|
case rememberInstance.fulfilled.type:
|
||||||
return importInstance(state, action.payload);
|
return importInstance(state, action.payload.instance);
|
||||||
case fetchInstance.fulfilled.type:
|
case fetchInstance.fulfilled.type:
|
||||||
|
case fetchInstanceV2.fulfilled.type:
|
||||||
persistInstance(action.payload);
|
persistInstance(action.payload);
|
||||||
return importInstance(state, action.payload);
|
return importInstance(state, action.payload.instance);
|
||||||
case fetchInstance.rejected.type:
|
case fetchInstance.rejected.type:
|
||||||
|
case fetchInstanceV2.rejected.type:
|
||||||
return handleInstanceFetchFail(state, action.error);
|
return handleInstanceFetchFail(state, action.error);
|
||||||
case ADMIN_CONFIG_UPDATE_REQUEST:
|
case ADMIN_CONFIG_UPDATE_REQUEST:
|
||||||
case ADMIN_CONFIG_UPDATE_SUCCESS:
|
case ADMIN_CONFIG_UPDATE_SUCCESS:
|
||||||
|
|
|
@ -53,6 +53,17 @@ const configurationSchema = coerceObject({
|
||||||
max_characters: z.number().optional().catch(undefined),
|
max_characters: z.number().optional().catch(undefined),
|
||||||
max_media_attachments: z.number().optional().catch(undefined),
|
max_media_attachments: z.number().optional().catch(undefined),
|
||||||
}),
|
}),
|
||||||
|
translation: coerceObject({
|
||||||
|
enabled: z.boolean().catch(false),
|
||||||
|
}),
|
||||||
|
urls: coerceObject({
|
||||||
|
streaming: z.string().url().optional().catch(undefined),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const contactSchema = coerceObject({
|
||||||
|
contact_account: accountSchema.optional().catch(undefined),
|
||||||
|
email: z.string().email().catch(''),
|
||||||
});
|
});
|
||||||
|
|
||||||
const nostrSchema = coerceObject({
|
const nostrSchema = coerceObject({
|
||||||
|
@ -65,6 +76,7 @@ const pleromaSchema = coerceObject({
|
||||||
account_activation_required: z.boolean().catch(false),
|
account_activation_required: z.boolean().catch(false),
|
||||||
birthday_min_age: z.number().catch(0),
|
birthday_min_age: z.number().catch(0),
|
||||||
birthday_required: z.boolean().catch(false),
|
birthday_required: z.boolean().catch(false),
|
||||||
|
description_limit: z.number().catch(1500),
|
||||||
features: z.string().array().catch([]),
|
features: z.string().array().catch([]),
|
||||||
federation: coerceObject({
|
federation: coerceObject({
|
||||||
enabled: z.boolean().catch(true), // Assume true unless explicitly false
|
enabled: z.boolean().catch(true), // Assume true unless explicitly false
|
||||||
|
@ -112,14 +124,20 @@ const pleromaPollLimitsSchema = coerceObject({
|
||||||
min_expiration: z.number().optional().catch(undefined),
|
min_expiration: z.number().optional().catch(undefined),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const registrations = coerceObject({
|
||||||
|
approval_required: z.boolean().catch(false),
|
||||||
|
enabled: z.boolean().catch(false),
|
||||||
|
message: z.string().optional().catch(undefined),
|
||||||
|
});
|
||||||
|
|
||||||
const statsSchema = coerceObject({
|
const statsSchema = coerceObject({
|
||||||
domain_count: z.number().catch(0),
|
domain_count: z.number().catch(0),
|
||||||
status_count: z.number().catch(0),
|
status_count: z.number().catch(0),
|
||||||
user_count: z.number().catch(0),
|
user_count: z.number().catch(0),
|
||||||
});
|
});
|
||||||
|
|
||||||
const urlsSchema = coerceObject({
|
const thumbnailSchema = coerceObject({
|
||||||
streaming_api: z.string().url().optional().catch(undefined),
|
url: z.string().catch(''),
|
||||||
});
|
});
|
||||||
|
|
||||||
const usageSchema = coerceObject({
|
const usageSchema = coerceObject({
|
||||||
|
@ -129,12 +147,10 @@ const usageSchema = coerceObject({
|
||||||
});
|
});
|
||||||
|
|
||||||
const instanceSchema = coerceObject({
|
const instanceSchema = coerceObject({
|
||||||
approval_required: z.boolean().catch(false),
|
|
||||||
configuration: configurationSchema,
|
configuration: configurationSchema,
|
||||||
contact_account: accountSchema.optional().catch(undefined),
|
contact: contactSchema,
|
||||||
description: z.string().catch(''),
|
description: z.string().catch(''),
|
||||||
description_limit: z.number().catch(1500),
|
domain: z.string().catch(''),
|
||||||
email: z.string().email().catch(''),
|
|
||||||
feature_quote: z.boolean().catch(false),
|
feature_quote: z.boolean().catch(false),
|
||||||
fedibird_capabilities: z.array(z.string()).catch([]),
|
fedibird_capabilities: z.array(z.string()).catch([]),
|
||||||
languages: z.string().array().catch([]),
|
languages: z.string().array().catch([]),
|
||||||
|
@ -143,13 +159,11 @@ const instanceSchema = coerceObject({
|
||||||
nostr: nostrSchema.optional().catch(undefined),
|
nostr: nostrSchema.optional().catch(undefined),
|
||||||
pleroma: pleromaSchema,
|
pleroma: pleromaSchema,
|
||||||
poll_limits: pleromaPollLimitsSchema,
|
poll_limits: pleromaPollLimitsSchema,
|
||||||
registrations: z.boolean().catch(false),
|
registrations: registrations,
|
||||||
rules: filteredArray(ruleSchema),
|
rules: filteredArray(ruleSchema),
|
||||||
short_description: z.string().catch(''),
|
|
||||||
stats: statsSchema,
|
stats: statsSchema,
|
||||||
thumbnail: z.string().catch(''),
|
thumbnail: thumbnailSchema,
|
||||||
title: z.string().catch(''),
|
title: z.string().catch(''),
|
||||||
urls: urlsSchema,
|
|
||||||
usage: usageSchema,
|
usage: usageSchema,
|
||||||
version: z.string().catch(''),
|
version: z.string().catch(''),
|
||||||
}).transform(({ max_media_attachments, max_toot_chars, poll_limits, ...instance }) => {
|
}).transform(({ max_media_attachments, max_toot_chars, poll_limits, ...instance }) => {
|
||||||
|
@ -182,6 +196,74 @@ const instanceSchema = coerceObject({
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const instanceV1ToV2 = coerceObject({
|
||||||
|
approval_required: z.boolean().catch(false),
|
||||||
|
configuration: configurationSchema,
|
||||||
|
contact_account: accountSchema.optional().catch(undefined),
|
||||||
|
description: z.string().catch(''),
|
||||||
|
description_limit: z.number().catch(1500),
|
||||||
|
email: z.string().email().catch(''),
|
||||||
|
feature_quote: z.boolean().catch(false),
|
||||||
|
fedibird_capabilities: z.array(z.string()).catch([]),
|
||||||
|
languages: z.string().array().catch([]),
|
||||||
|
max_media_attachments: z.number().optional().catch(undefined),
|
||||||
|
max_toot_chars: z.number().optional().catch(undefined),
|
||||||
|
nostr: nostrSchema.optional().catch(undefined),
|
||||||
|
pleroma: pleromaSchema,
|
||||||
|
poll_limits: pleromaPollLimitsSchema,
|
||||||
|
registrations: z.boolean().catch(false),
|
||||||
|
rules: filteredArray(ruleSchema),
|
||||||
|
short_description: z.string().catch(''),
|
||||||
|
stats: statsSchema,
|
||||||
|
thumbnail: z.string().catch(''),
|
||||||
|
title: z.string().catch(''),
|
||||||
|
urls: coerceObject({
|
||||||
|
streaming_api: z.string().url().optional().catch(undefined),
|
||||||
|
}),
|
||||||
|
usage: usageSchema,
|
||||||
|
version: z.string().catch(''),
|
||||||
|
}).transform(({
|
||||||
|
approval_required,
|
||||||
|
configuration,
|
||||||
|
contact_account,
|
||||||
|
description,
|
||||||
|
description_limit,
|
||||||
|
email,
|
||||||
|
pleroma,
|
||||||
|
registrations,
|
||||||
|
short_description,
|
||||||
|
thumbnail,
|
||||||
|
urls,
|
||||||
|
...instance
|
||||||
|
}) => {
|
||||||
|
return instanceSchema.parse({
|
||||||
|
...instance,
|
||||||
|
configuration: {
|
||||||
|
...configuration,
|
||||||
|
urls: {
|
||||||
|
streaming: urls.streaming_api,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
contact: {
|
||||||
|
account: contact_account,
|
||||||
|
email: email,
|
||||||
|
},
|
||||||
|
description: short_description || description,
|
||||||
|
pleroma: {
|
||||||
|
...pleroma,
|
||||||
|
metadata: {
|
||||||
|
...pleroma.metadata,
|
||||||
|
description_limit,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
registrations: {
|
||||||
|
approval_required: approval_required,
|
||||||
|
enabled: registrations,
|
||||||
|
},
|
||||||
|
thumbnail: { url: thumbnail },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
type Instance = z.infer<typeof instanceSchema>;
|
type Instance = z.infer<typeof instanceSchema>;
|
||||||
|
|
||||||
export { instanceSchema, Instance };
|
export { instanceSchema, instanceV1ToV2, Instance };
|
||||||
|
|
|
@ -20,7 +20,7 @@ export function connectStream(
|
||||||
callbacks: (dispatch: AppDispatch, getState: () => RootState) => ConnectStreamCallbacks,
|
callbacks: (dispatch: AppDispatch, getState: () => RootState) => ConnectStreamCallbacks,
|
||||||
) {
|
) {
|
||||||
return (dispatch: AppDispatch, getState: () => RootState) => {
|
return (dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const streamingAPIBaseURL = getState().instance.urls.streaming_api;
|
const streamingAPIBaseURL = getState().instance.configuration.urls.streaming;
|
||||||
const accessToken = getAccessToken(getState());
|
const accessToken = getAccessToken(getState());
|
||||||
const { onConnect, onDisconnect, onReceive } = callbacks(dispatch, getState);
|
const { onConnect, onDisconnect, onReceive } = callbacks(dispatch, getState);
|
||||||
|
|
||||||
|
|
|
@ -620,6 +620,16 @@ const getInstanceFeatures = (instance: Instance) => {
|
||||||
*/
|
*/
|
||||||
importData: v.software === PLEROMA && gte(v.version, '2.2.0'),
|
importData: v.software === PLEROMA && gte(v.version, '2.2.0'),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mastodon server information API v2.
|
||||||
|
* @see GET /api/v2/instance
|
||||||
|
* @see {@link https://docs.joinmastodon.org/methods/instance/#v2}
|
||||||
|
*/
|
||||||
|
instanceV2: any([
|
||||||
|
v.software === MASTODON && gte(v.compatVersion, '4.0.0'),
|
||||||
|
v.software === PLEROMA && v.build === REBASED && gte(v.version, '2.5.54'),
|
||||||
|
]),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Can create, view, and manage lists.
|
* Can create, view, and manage lists.
|
||||||
* @see {@link https://docs.joinmastodon.org/methods/lists/}
|
* @see {@link https://docs.joinmastodon.org/methods/lists/}
|
||||||
|
@ -926,7 +936,7 @@ const getInstanceFeatures = (instance: Instance) => {
|
||||||
* Can translate statuses.
|
* Can translate statuses.
|
||||||
* @see POST /api/v1/statuses/:id/translate
|
* @see POST /api/v1/statuses/:id/translate
|
||||||
*/
|
*/
|
||||||
translations: features.includes('translation'),
|
translations: features.includes('translation') || instance.configuration.translation.enabled,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trending statuses.
|
* Trending statuses.
|
||||||
|
@ -994,7 +1004,7 @@ export const parseVersion = (version: string): Backend => {
|
||||||
build: semver.build[0],
|
build: semver.build[0],
|
||||||
compatVersion: compat.version,
|
compatVersion: compat.version,
|
||||||
software: match[2] || MASTODON,
|
software: match[2] || MASTODON,
|
||||||
version: semver.version,
|
version: semver.version.split('-')[0],
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// If we can't parse the version, this is a new and exotic backend.
|
// If we can't parse the version, this is a new and exotic backend.
|
||||||
|
|
Loading…
Reference in New Issue