Convert instance to use zod
This commit is contained in:
@@ -4,10 +4,9 @@ import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
|
||||
import { updateConfig } from 'soapbox/actions/admin';
|
||||
import { RadioGroup, RadioItem } from 'soapbox/components/radio';
|
||||
import { useAppDispatch, useInstance } from 'soapbox/hooks';
|
||||
import { Instance } from 'soapbox/schemas';
|
||||
import toast from 'soapbox/toast';
|
||||
|
||||
import type { Instance } from 'soapbox/types/entities';
|
||||
|
||||
type RegistrationMode = 'open' | 'approval' | 'closed';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
||||
@@ -41,11 +41,13 @@ const Dashboard: React.FC = () => {
|
||||
|
||||
const v = parseVersion(instance.version);
|
||||
|
||||
const userCount = instance.stats.get('user_count');
|
||||
const statusCount = instance.stats.get('status_count');
|
||||
const domainCount = instance.stats.get('domain_count');
|
||||
const {
|
||||
user_count: userCount,
|
||||
status_count: statusCount,
|
||||
domain_count: domainCount,
|
||||
} = instance.stats;
|
||||
|
||||
const mau = instance.pleroma.getIn(['stats', 'mau']) as number | undefined;
|
||||
const mau = instance.pleroma.stats.mau;
|
||||
const retention = (userCount && mau) ? Math.round(mau / userCount * 100) : undefined;
|
||||
|
||||
if (!account) return null;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { List as ImmutableList } from 'immutable';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
@@ -13,9 +12,9 @@ interface IConsumersList {
|
||||
/** Displays OAuth consumers to log in with. */
|
||||
const ConsumersList: React.FC<IConsumersList> = () => {
|
||||
const instance = useInstance();
|
||||
const providers = ImmutableList<string>(instance.pleroma.get('oauth_consumer_strategies'));
|
||||
const providers = instance.pleroma.oauth_consumer_strategies;
|
||||
|
||||
if (providers.size > 0) {
|
||||
if (providers.length > 0) {
|
||||
return (
|
||||
<Card className='bg-gray-50 p-4 dark:bg-primary-800 sm:rounded-xl'>
|
||||
<Text size='xs' theme='muted'>
|
||||
|
||||
@@ -46,11 +46,11 @@ const RegistrationForm: React.FC<IRegistrationForm> = ({ inviteToken }) => {
|
||||
const instance = useInstance();
|
||||
|
||||
const locale = settings.get('locale');
|
||||
const needsConfirmation = !!instance.pleroma.getIn(['metadata', 'account_activation_required']);
|
||||
const needsConfirmation = instance.pleroma.metadata.account_activation_required;
|
||||
const needsApproval = instance.approval_required;
|
||||
const supportsEmailList = features.emailList;
|
||||
const supportsAccountLookup = features.accountLookup;
|
||||
const birthdayRequired = instance.pleroma.getIn(['metadata', 'birthday_required']);
|
||||
const birthdayRequired = instance.pleroma.metadata.birthday_required;
|
||||
|
||||
const [captchaLoading, setCaptchaLoading] = useState(true);
|
||||
const [submissionLoading, setSubmissionLoading] = useState(false);
|
||||
|
||||
@@ -3,8 +3,8 @@ import React from 'react';
|
||||
import { VirtuosoMockContext } from 'react-virtuoso';
|
||||
|
||||
import { ChatContext } from 'soapbox/contexts/chat-context';
|
||||
import { buildAccount } from 'soapbox/jest/factory';
|
||||
import { normalizeChatMessage, normalizeInstance } from 'soapbox/normalizers';
|
||||
import { buildAccount, buildInstance } from 'soapbox/jest/factory';
|
||||
import { normalizeChatMessage } from 'soapbox/normalizers';
|
||||
import { ChatMessage } from 'soapbox/types/entities';
|
||||
|
||||
import { __stub } from '../../../../api';
|
||||
@@ -70,7 +70,7 @@ Object.assign(navigator, {
|
||||
|
||||
const store = rootState
|
||||
.set('me', '1')
|
||||
.set('instance', normalizeInstance({ version: '3.4.1 (compatible; TruthSocial 1.0.0+unreleased)' }));
|
||||
.set('instance', buildInstance({ version: '3.4.1 (compatible; TruthSocial 1.0.0+unreleased)' }));
|
||||
|
||||
const renderComponentWithChatContext = () => render(
|
||||
<VirtuosoMockContext.Provider value={{ viewportHeight: 300, itemHeight: 100 }}>
|
||||
|
||||
@@ -76,8 +76,8 @@ const ChatComposer = React.forwardRef<HTMLTextAreaElement | null, IChatComposer>
|
||||
|
||||
const isBlocked = useAppSelector((state) => state.getIn(['relationships', chat?.account?.id, 'blocked_by']));
|
||||
const isBlocking = useAppSelector((state) => state.getIn(['relationships', chat?.account?.id, 'blocking']));
|
||||
const maxCharacterCount = useAppSelector((state) => state.instance.getIn(['configuration', 'chats', 'max_characters']) as number);
|
||||
const attachmentLimit = useAppSelector(state => state.instance.configuration.getIn(['chats', 'max_media_attachments']) as number);
|
||||
const maxCharacterCount = useAppSelector((state) => state.instance.configuration.chats.max_characters);
|
||||
const attachmentLimit = useAppSelector(state => state.instance.configuration.chats.max_media_attachments);
|
||||
|
||||
const [suggestions, setSuggestions] = useState<Suggestion>(initialSuggestionState);
|
||||
const isSuggestionsAvailable = suggestions.list.length > 0;
|
||||
|
||||
@@ -53,7 +53,7 @@ const Chat: React.FC<ChatInterface> = ({ chat, inputRef, className }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { createChatMessage, acceptChat } = useChatActions(chat.id);
|
||||
const attachmentLimit = useAppSelector(state => state.instance.configuration.getIn(['chats', 'max_media_attachments']) as number);
|
||||
const attachmentLimit = useAppSelector(state => state.instance.configuration.chats.max_media_attachments);
|
||||
|
||||
const [content, setContent] = useState<string>('');
|
||||
const [attachments, setAttachments] = useState<Attachment[]>([]);
|
||||
|
||||
@@ -77,7 +77,7 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
||||
|
||||
const compose = useCompose(id);
|
||||
const showSearch = useAppSelector((state) => state.search.submitted && !state.search.hidden);
|
||||
const maxTootChars = configuration.getIn(['statuses', 'max_characters']) as number;
|
||||
const maxTootChars = configuration.statuses.max_characters;
|
||||
const scheduledStatusCount = useAppSelector((state) => state.scheduled_statuses.size);
|
||||
const features = useFeatures();
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import { useAppDispatch, useCompose, useInstance } from 'soapbox/hooks';
|
||||
|
||||
import DurationSelector from './duration-selector';
|
||||
|
||||
import type { Map as ImmutableMap } from 'immutable';
|
||||
import type { AutoSuggestion } from 'soapbox/components/autosuggest-input';
|
||||
|
||||
const messages = defineMessages({
|
||||
@@ -115,13 +114,14 @@ const PollForm: React.FC<IPollForm> = ({ composeId }) => {
|
||||
|
||||
const compose = useCompose(composeId);
|
||||
|
||||
const pollLimits = configuration.get('polls') as ImmutableMap<string, number>;
|
||||
const options = compose.poll?.options;
|
||||
const expiresIn = compose.poll?.expires_in;
|
||||
const isMultiple = compose.poll?.multiple;
|
||||
|
||||
const maxOptions = pollLimits.get('max_options') as number;
|
||||
const maxOptionChars = pollLimits.get('max_characters_per_option') as number;
|
||||
const {
|
||||
max_options: maxOptions,
|
||||
max_characters_per_option: maxOptionChars,
|
||||
} = configuration.polls;
|
||||
|
||||
const onRemoveOption = (index: number) => dispatch(removePollOption(composeId, index));
|
||||
const onChangeOption = (index: number, title: string) => dispatch(changePollOption(composeId, index, title));
|
||||
|
||||
@@ -4,14 +4,12 @@ import { defineMessages, IntlShape, useIntl } from 'react-intl';
|
||||
import { IconButton } from 'soapbox/components/ui';
|
||||
import { useInstance } from 'soapbox/hooks';
|
||||
|
||||
import type { List as ImmutableList } from 'immutable';
|
||||
|
||||
const messages = defineMessages({
|
||||
upload: { id: 'upload_button.label', defaultMessage: 'Add media attachment' },
|
||||
});
|
||||
|
||||
export const onlyImages = (types: ImmutableList<string>) => {
|
||||
return Boolean(types && types.every(type => type.startsWith('image/')));
|
||||
export const onlyImages = (types: string[] | undefined): boolean => {
|
||||
return types?.every((type) => type.startsWith('image/')) ?? false;
|
||||
};
|
||||
|
||||
export interface IUploadButton {
|
||||
@@ -38,7 +36,7 @@ const UploadButton: React.FC<IUploadButton> = ({
|
||||
const { configuration } = useInstance();
|
||||
|
||||
const fileElement = useRef<HTMLInputElement>(null);
|
||||
const attachmentTypes = configuration.getIn(['media_attachments', 'supported_mime_types']) as ImmutableList<string>;
|
||||
const attachmentTypes = configuration.media_attachments.supported_mime_types;
|
||||
|
||||
const handleChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||
if (e.target.files?.length) {
|
||||
@@ -78,7 +76,7 @@ const UploadButton: React.FC<IUploadButton> = ({
|
||||
ref={fileElement}
|
||||
type='file'
|
||||
multiple
|
||||
accept={attachmentTypes && attachmentTypes.toArray().join(',')}
|
||||
accept={attachmentTypes?.join(',')}
|
||||
onChange={handleChange}
|
||||
disabled={disabled}
|
||||
className='hidden'
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useDraggedFiles } from 'soapbox/hooks';
|
||||
interface IMediaInput {
|
||||
className?: string
|
||||
src: string | undefined
|
||||
accept: string
|
||||
accept?: string
|
||||
onChange: (files: FileList | null) => void
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ const messages = defineMessages({
|
||||
|
||||
interface IMediaInput {
|
||||
src: string | undefined
|
||||
accept: string
|
||||
accept?: string
|
||||
onChange: (files: FileList | null) => void
|
||||
onClear?: () => void
|
||||
disabled?: boolean
|
||||
|
||||
@@ -25,7 +25,6 @@ import { isDefaultAvatar, isDefaultHeader } from 'soapbox/utils/accounts';
|
||||
import AvatarPicker from './components/avatar-picker';
|
||||
import HeaderPicker from './components/header-picker';
|
||||
|
||||
import type { List as ImmutableList } from 'immutable';
|
||||
import type { StreamfieldComponent } from 'soapbox/components/ui/streamfield/streamfield';
|
||||
import type { Account } from 'soapbox/schemas';
|
||||
|
||||
@@ -183,11 +182,12 @@ const EditProfile: React.FC = () => {
|
||||
|
||||
const { account } = useOwnAccount();
|
||||
const features = useFeatures();
|
||||
const maxFields = instance.pleroma.getIn(['metadata', 'fields_limits', 'max_fields'], 4) as number;
|
||||
const maxFields = instance.pleroma.metadata.fields_limits.max_fields;
|
||||
|
||||
const attachmentTypes = useAppSelector(
|
||||
state => state.instance.configuration.getIn(['media_attachments', 'supported_mime_types']) as ImmutableList<string>,
|
||||
)?.filter(type => type.startsWith('image/')).toArray().join(',');
|
||||
state => state.instance.configuration.media_attachments.supported_mime_types)
|
||||
?.filter(type => type.startsWith('image/'))
|
||||
.join(',');
|
||||
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
const [data, setData] = useState<AccountCredentials>({});
|
||||
|
||||
@@ -111,7 +111,7 @@ const InstanceRestrictions: React.FC<IInstanceRestrictions> = ({ remoteInstance
|
||||
if (!instance || !remoteInstance) return null;
|
||||
|
||||
const host = remoteInstance.get('host');
|
||||
const siteTitle = instance.get('title');
|
||||
const siteTitle = instance.title;
|
||||
|
||||
if (remoteInstance.getIn(['federation', 'reject']) === true) {
|
||||
return (
|
||||
|
||||
@@ -13,8 +13,6 @@ import HeaderPicker from '../edit-profile/components/header-picker';
|
||||
|
||||
import GroupTagsField from './components/group-tags-field';
|
||||
|
||||
import type { List as ImmutableList } from 'immutable';
|
||||
|
||||
const nonDefaultAvatar = (url: string | undefined) => url && isDefaultAvatar(url) ? undefined : url;
|
||||
const nonDefaultHeader = (url: string | undefined) => url && isDefaultHeader(url) ? undefined : url;
|
||||
|
||||
@@ -48,12 +46,12 @@ const EditGroup: React.FC<IEditGroup> = ({ params: { groupId } }) => {
|
||||
const displayName = useTextField(group?.display_name);
|
||||
const note = useTextField(group?.note_plain);
|
||||
|
||||
const maxName = Number(instance.configuration.getIn(['groups', 'max_characters_name']));
|
||||
const maxNote = Number(instance.configuration.getIn(['groups', 'max_characters_description']));
|
||||
const maxName = Number(instance.configuration.groups.max_characters_name);
|
||||
const maxNote = Number(instance.configuration.groups.max_characters_description);
|
||||
|
||||
const attachmentTypes = useAppSelector(
|
||||
state => state.instance.configuration.getIn(['media_attachments', 'supported_mime_types']) as ImmutableList<string>,
|
||||
)?.filter(type => type.startsWith('image/')).toArray().join(',');
|
||||
const attachmentTypes = useAppSelector(state => state.instance.configuration.media_attachments.supported_mime_types)
|
||||
?.filter((type) => type.startsWith('image/'))
|
||||
.join(',');
|
||||
|
||||
async function handleSubmit() {
|
||||
setIsSubmitting(true);
|
||||
|
||||
@@ -23,7 +23,7 @@ const Migration = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const instance = useInstance();
|
||||
|
||||
const cooldownPeriod = instance.pleroma.getIn(['metadata', 'migration_cooldown_period']) as number | undefined;
|
||||
const cooldownPeriod = instance.pleroma.metadata.migration_cooldown_period;
|
||||
|
||||
const [targetAccount, setTargetAccount] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
|
||||
@@ -5,8 +5,6 @@ import Icon from 'soapbox/components/icon';
|
||||
import { HStack, Text } from 'soapbox/components/ui';
|
||||
import { useAppSelector } from 'soapbox/hooks';
|
||||
|
||||
import type { List as ImmutableList } from 'immutable';
|
||||
|
||||
interface IUploadButton {
|
||||
disabled?: boolean
|
||||
onSelectFile: (files: FileList) => void
|
||||
@@ -14,7 +12,8 @@ interface IUploadButton {
|
||||
|
||||
const UploadButton: React.FC<IUploadButton> = ({ disabled, onSelectFile }) => {
|
||||
const fileElement = useRef<HTMLInputElement>(null);
|
||||
const attachmentTypes = useAppSelector(state => state.instance.configuration.getIn(['media_attachments', 'supported_mime_types']) as ImmutableList<string>)?.filter(type => type.startsWith('image/'));
|
||||
const attachmentTypes = useAppSelector(state => state.instance.configuration.media_attachments.supported_mime_types)
|
||||
?.filter((type) => type.startsWith('image/'));
|
||||
|
||||
const handleChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||
if (e.target.files?.length) {
|
||||
@@ -40,7 +39,7 @@ const UploadButton: React.FC<IUploadButton> = ({ disabled, onSelectFile }) => {
|
||||
<input
|
||||
ref={fileElement}
|
||||
type='file'
|
||||
accept={attachmentTypes && attachmentTypes.toArray().join(',')}
|
||||
accept={attachmentTypes?.join(',')}
|
||||
onChange={handleChange}
|
||||
disabled={disabled}
|
||||
className='hidden'
|
||||
|
||||
@@ -10,8 +10,6 @@ import { useAppSelector, useDebounce, useInstance } from 'soapbox/hooks';
|
||||
import { usePreview } from 'soapbox/hooks/forms';
|
||||
import resizeImage from 'soapbox/utils/resize-image';
|
||||
|
||||
import type { List as ImmutableList } from 'immutable';
|
||||
|
||||
const messages = defineMessages({
|
||||
groupNamePlaceholder: { id: 'manage_group.fields.name_placeholder', defaultMessage: 'Group Name' },
|
||||
groupDescriptionPlaceholder: { id: 'manage_group.fields.description_placeholder', defaultMessage: 'Description' },
|
||||
@@ -40,9 +38,9 @@ const DetailsStep: React.FC<IDetailsStep> = ({ params, onChange }) => {
|
||||
const avatarSrc = usePreview(params.avatar);
|
||||
const headerSrc = usePreview(params.header);
|
||||
|
||||
const attachmentTypes = useAppSelector(
|
||||
state => state.instance.configuration.getIn(['media_attachments', 'supported_mime_types']) as ImmutableList<string>,
|
||||
)?.filter(type => type.startsWith('image/')).toArray().join(',');
|
||||
const attachmentTypes = useAppSelector(state => state.instance.configuration.media_attachments.supported_mime_types)
|
||||
?.filter((type) => type.startsWith('image/'))
|
||||
.join(',');
|
||||
|
||||
const handleTextChange = (property: keyof CreateGroupParams): React.ChangeEventHandler<HTMLInputElement | HTMLTextAreaElement> => {
|
||||
return (e) => {
|
||||
@@ -107,7 +105,7 @@ const DetailsStep: React.FC<IDetailsStep> = ({ params, onChange }) => {
|
||||
placeholder={intl.formatMessage(messages.groupNamePlaceholder)}
|
||||
value={displayName}
|
||||
onChange={handleTextChange('display_name')}
|
||||
maxLength={Number(instance.configuration.getIn(['groups', 'max_characters_name']))}
|
||||
maxLength={Number(instance.configuration.groups.max_characters_name)}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
@@ -119,7 +117,7 @@ const DetailsStep: React.FC<IDetailsStep> = ({ params, onChange }) => {
|
||||
placeholder={intl.formatMessage(messages.groupDescriptionPlaceholder)}
|
||||
value={note}
|
||||
onChange={handleTextChange('note')}
|
||||
maxLength={Number(instance.configuration.getIn(['groups', 'max_characters_description']))}
|
||||
maxLength={Number(instance.configuration.groups.max_characters_description)}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user