@@ -13,6 +13,8 @@ import { Checkbox, Form, FormGroup, FormActions, Button, Input, Textarea, Select
|
||||
import CaptchaField from 'soapbox/features/auth-login/components/captcha';
|
||||
import { useAppDispatch, useSettings, useFeatures, useInstance } from 'soapbox/hooks';
|
||||
|
||||
import type { CreateAccountParams } from 'pl-api';
|
||||
|
||||
const messages = defineMessages({
|
||||
username: { id: 'registration.fields.username_placeholder', defaultMessage: 'Username' },
|
||||
username_hint: { id: 'registration.fields.username_hint', defaultMessage: 'Only letters, numbers, and underscores are allowed.' },
|
||||
@@ -53,7 +55,13 @@ const RegistrationForm: React.FC<IRegistrationForm> = ({ inviteToken }) => {
|
||||
|
||||
const [captchaLoading, setCaptchaLoading] = useState(true);
|
||||
const [submissionLoading, setSubmissionLoading] = useState(false);
|
||||
const [params, setParams] = useState(ImmutableMap<string, any>());
|
||||
const [params, setParams] = useState<CreateAccountParams>({
|
||||
username: '',
|
||||
email: '',
|
||||
password: '',
|
||||
agreement: false,
|
||||
locale: '',
|
||||
});
|
||||
const [captchaIdempotencyKey, setCaptchaIdempotencyKey] = useState(uuidv4());
|
||||
const [usernameUnavailable, setUsernameUnavailable] = useState(false);
|
||||
const [passwordConfirmation, setPasswordConfirmation] = useState('');
|
||||
@@ -61,39 +69,35 @@ const RegistrationForm: React.FC<IRegistrationForm> = ({ inviteToken }) => {
|
||||
|
||||
const controller = useRef(new AbortController());
|
||||
|
||||
const updateParams = (map: any) => {
|
||||
setParams(params.merge(ImmutableMap(map)));
|
||||
};
|
||||
|
||||
const onInputChange: React.ChangeEventHandler<HTMLInputElement | HTMLTextAreaElement> = e => {
|
||||
updateParams({ [e.target.name]: e.target.value });
|
||||
setParams(params => ({ ...params, [e.target.name]: e.target.value }));
|
||||
};
|
||||
|
||||
const onUsernameChange: React.ChangeEventHandler<HTMLInputElement> = e => {
|
||||
updateParams({ username: e.target.value });
|
||||
setParams(params => ({ ...params, username: e.target.value }));
|
||||
setUsernameUnavailable(false);
|
||||
controller.current.abort();
|
||||
controller.current = new AbortController();
|
||||
|
||||
const domain = params.get('domain');
|
||||
const domain = params.domain;
|
||||
usernameAvailable(e.target.value, domain ? domains!.find(({ id }) => id === domain)?.domain : undefined);
|
||||
};
|
||||
|
||||
const onDomainChange: React.ChangeEventHandler<HTMLSelectElement> = e => {
|
||||
updateParams({ domain: e.target.value || null });
|
||||
setParams(params => ({ ...params, domain: e.target.value || undefined }));
|
||||
setUsernameUnavailable(false);
|
||||
|
||||
controller.current.abort();
|
||||
controller.current = new AbortController();
|
||||
|
||||
const username = params.get('username');
|
||||
const username = params.username;
|
||||
if (username) {
|
||||
usernameAvailable(username, domains!.find(({ id }) => id === e.target.value)?.domain);
|
||||
}
|
||||
};
|
||||
|
||||
const onCheckboxChange: React.ChangeEventHandler<HTMLInputElement> = e => {
|
||||
updateParams({ [e.target.name]: e.target.checked });
|
||||
setParams(params => ({ ...params, [e.target.name]: e.target.checked }));
|
||||
};
|
||||
|
||||
const onPasswordChange: React.ChangeEventHandler<HTMLInputElement> = e => {
|
||||
@@ -106,7 +110,7 @@ const RegistrationForm: React.FC<IRegistrationForm> = ({ inviteToken }) => {
|
||||
};
|
||||
|
||||
const onPasswordConfirmChange: React.ChangeEventHandler<HTMLInputElement> = e => {
|
||||
const password = params.get('password', '');
|
||||
const password = params.password || '';
|
||||
const passwordConfirmation = e.target.value;
|
||||
setPasswordConfirmation(passwordConfirmation);
|
||||
|
||||
@@ -120,7 +124,7 @@ const RegistrationForm: React.FC<IRegistrationForm> = ({ inviteToken }) => {
|
||||
};
|
||||
|
||||
const onBirthdayChange = (birthday: string) => {
|
||||
updateParams({ birthday });
|
||||
setParams(params => ({ ...params, birthday }));
|
||||
};
|
||||
|
||||
const launchModal = () => {
|
||||
@@ -129,7 +133,7 @@ const RegistrationForm: React.FC<IRegistrationForm> = ({ inviteToken }) => {
|
||||
<FormattedMessage
|
||||
id='confirmations.register.needs_confirmation'
|
||||
defaultMessage='Please check your inbox at {email} for confirmation instructions. You will need to verify your email address to continue.'
|
||||
values={{ email: <strong>{params.get('email')}</strong> }}
|
||||
values={{ email: <strong>{params.email}</strong> }}
|
||||
/></p>}
|
||||
{needsApproval && <p>
|
||||
<FormattedMessage
|
||||
@@ -160,7 +164,7 @@ const RegistrationForm: React.FC<IRegistrationForm> = ({ inviteToken }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const passwordsMatch = () => params.get('password', '') === passwordConfirmation;
|
||||
const passwordsMatch = () => params.password === passwordConfirmation;
|
||||
|
||||
const usernameAvailable = useCallback(debounce((username, domain?: string) => {
|
||||
if (!supportsAccountLookup) return;
|
||||
@@ -186,19 +190,18 @@ const RegistrationForm: React.FC<IRegistrationForm> = ({ inviteToken }) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const normalParams = params.withMutations(params => {
|
||||
// Locale for confirmation email
|
||||
params.set('locale', locale);
|
||||
const normalParams = {
|
||||
...params,
|
||||
locale,
|
||||
};
|
||||
|
||||
// Pleroma invites
|
||||
if (inviteToken) {
|
||||
params.set('token', inviteToken);
|
||||
}
|
||||
});
|
||||
if (inviteToken) {
|
||||
params.token = inviteToken;
|
||||
}
|
||||
|
||||
setSubmissionLoading(true);
|
||||
|
||||
dispatch(register(normalParams.toJS()))
|
||||
dispatch(register(normalParams))
|
||||
.then(postRegisterAction)
|
||||
.catch(() => {
|
||||
setSubmissionLoading(false);
|
||||
@@ -212,10 +215,11 @@ const RegistrationForm: React.FC<IRegistrationForm> = ({ inviteToken }) => {
|
||||
|
||||
const onFetchCaptcha = (captcha: ImmutableMap<string, any>) => {
|
||||
setCaptchaLoading(false);
|
||||
updateParams({
|
||||
setParams(params => ({
|
||||
...params,
|
||||
captcha_token: captcha.get('token'),
|
||||
captcha_answer_data: captcha.get('answer_data'),
|
||||
});
|
||||
}));
|
||||
};
|
||||
|
||||
const onFetchCaptchaFail = () => {
|
||||
@@ -224,7 +228,7 @@ const RegistrationForm: React.FC<IRegistrationForm> = ({ inviteToken }) => {
|
||||
|
||||
const refreshCaptcha = () => {
|
||||
setCaptchaIdempotencyKey(uuidv4());
|
||||
updateParams({ captcha_solution: '' });
|
||||
setParams(params => ({ ...params, captcha_solution: '' }));
|
||||
};
|
||||
|
||||
const isLoading = captchaLoading || submissionLoading;
|
||||
@@ -247,7 +251,7 @@ const RegistrationForm: React.FC<IRegistrationForm> = ({ inviteToken }) => {
|
||||
pattern='^[a-zA-Z\d_-]+'
|
||||
icon={require('@tabler/icons/outline/at.svg')}
|
||||
onChange={onUsernameChange}
|
||||
value={params.get('username', '')}
|
||||
value={params.username}
|
||||
required
|
||||
/>
|
||||
</FormGroup>
|
||||
@@ -256,7 +260,7 @@ const RegistrationForm: React.FC<IRegistrationForm> = ({ inviteToken }) => {
|
||||
<FormGroup>
|
||||
<Select
|
||||
onChange={onDomainChange}
|
||||
value={params.get('domain')}
|
||||
value={params.domain}
|
||||
>
|
||||
{domains.map(({ id, domain }) => (
|
||||
<option key={id} value={id}>{domain}</option>
|
||||
@@ -273,7 +277,7 @@ const RegistrationForm: React.FC<IRegistrationForm> = ({ inviteToken }) => {
|
||||
autoCorrect='off'
|
||||
autoCapitalize='off'
|
||||
onChange={onInputChange}
|
||||
value={params.get('email', '')}
|
||||
value={params.email}
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -285,7 +289,7 @@ const RegistrationForm: React.FC<IRegistrationForm> = ({ inviteToken }) => {
|
||||
autoCorrect='off'
|
||||
autoCapitalize='off'
|
||||
onChange={onPasswordChange}
|
||||
value={params.get('password', '')}
|
||||
value={params.password}
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -308,7 +312,7 @@ const RegistrationForm: React.FC<IRegistrationForm> = ({ inviteToken }) => {
|
||||
|
||||
{birthdayRequired && (
|
||||
<BirthdayInput
|
||||
value={params.get('birthday')}
|
||||
value={params.birthday || ''}
|
||||
onChange={onBirthdayChange}
|
||||
required
|
||||
/>
|
||||
@@ -323,7 +327,7 @@ const RegistrationForm: React.FC<IRegistrationForm> = ({ inviteToken }) => {
|
||||
placeholder={intl.formatMessage(messages.reasonHint)}
|
||||
maxLength={500}
|
||||
onChange={onInputChange}
|
||||
value={params.get('reason', '')}
|
||||
value={params.reason || ''}
|
||||
autoGrow
|
||||
required
|
||||
/>
|
||||
@@ -337,7 +341,7 @@ const RegistrationForm: React.FC<IRegistrationForm> = ({ inviteToken }) => {
|
||||
onClick={onCaptchaClick}
|
||||
idempotencyKey={captchaIdempotencyKey}
|
||||
name='captcha_solution'
|
||||
value={params.get('captcha_solution', '')}
|
||||
value={params.captcha_solution || ''}
|
||||
/>
|
||||
|
||||
<FormGroup
|
||||
@@ -346,7 +350,7 @@ const RegistrationForm: React.FC<IRegistrationForm> = ({ inviteToken }) => {
|
||||
<Checkbox
|
||||
name='agreement'
|
||||
onChange={onCheckboxChange}
|
||||
checked={params.get('agreement', false)}
|
||||
checked={params.agreement}
|
||||
required
|
||||
/>
|
||||
</FormGroup>
|
||||
@@ -356,7 +360,7 @@ const RegistrationForm: React.FC<IRegistrationForm> = ({ inviteToken }) => {
|
||||
<Checkbox
|
||||
name='accepts_email_list'
|
||||
onChange={onCheckboxChange}
|
||||
checked={params.get('accepts_email_list', false)}
|
||||
checked={params.accepts_email_list}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
|
||||
@@ -9,8 +9,9 @@ import { Avatar, HStack, IconButton, Stack, Text } from 'soapbox/components/ui';
|
||||
import VerificationBadge from 'soapbox/components/verification-badge';
|
||||
import { useChatContext } from 'soapbox/contexts/chat-context';
|
||||
import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks';
|
||||
import { IChat, useChatActions } from 'soapbox/queries/chats';
|
||||
import { useChatActions } from 'soapbox/queries/chats';
|
||||
|
||||
import type { Chat } from 'pl-api';
|
||||
import type { Menu } from 'soapbox/components/dropdown-menu';
|
||||
|
||||
const messages = defineMessages({
|
||||
@@ -23,7 +24,7 @@ const messages = defineMessages({
|
||||
});
|
||||
|
||||
interface IChatListItemInterface {
|
||||
chat: IChat;
|
||||
chat: Chat;
|
||||
onClick: (chat: any) => void;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,11 @@ import { Components, Virtuoso, VirtuosoHandle } from 'react-virtuoso';
|
||||
import { Avatar, Button, Divider, Spinner, Stack, Text } from 'soapbox/components/ui';
|
||||
import PlaceholderChatMessage from 'soapbox/features/placeholder/components/placeholder-chat-message';
|
||||
import { useAppSelector } from 'soapbox/hooks';
|
||||
import { IChat, useChatActions, useChatMessages } from 'soapbox/queries/chats';
|
||||
import { useChatActions, useChatMessages } from 'soapbox/queries/chats';
|
||||
|
||||
import ChatMessage from './chat-message';
|
||||
|
||||
import type { Chat } from 'pl-api';
|
||||
import type { ChatMessage as ChatMessageEntity } from 'soapbox/types/entities';
|
||||
|
||||
const messages = defineMessages({
|
||||
@@ -21,7 +22,7 @@ const messages = defineMessages({
|
||||
|
||||
type TimeFormat = 'today' | 'date';
|
||||
|
||||
const timeChange = (prev: ChatMessageEntity, curr: ChatMessageEntity): TimeFormat | null => {
|
||||
const timeChange = (prev: Pick<ChatMessageEntity, 'created_at'>, curr: Pick<ChatMessageEntity, 'created_at'>): TimeFormat | null => {
|
||||
const prevDate = new Date(prev.created_at).getDate();
|
||||
const currDate = new Date(curr.created_at).getDate();
|
||||
const nowDate = new Date().getDate();
|
||||
@@ -57,7 +58,7 @@ const Scroller: Components['Scroller'] = React.forwardRef((props, ref) => {
|
||||
|
||||
interface IChatMessageList {
|
||||
/** Chat the messages are being rendered from. */
|
||||
chat: IChat;
|
||||
chat: Chat;
|
||||
}
|
||||
|
||||
/** Scrollable list of chat messages. */
|
||||
|
||||
@@ -11,11 +11,12 @@ import { HStack, Icon, Stack, Text } from 'soapbox/components/ui';
|
||||
import emojify from 'soapbox/features/emoji';
|
||||
import { MediaGallery } from 'soapbox/features/ui/util/async-components';
|
||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||
import { ChatKeys, IChat, useChatActions } from 'soapbox/queries/chats';
|
||||
import { ChatKeys, useChatActions } from 'soapbox/queries/chats';
|
||||
import { queryClient } from 'soapbox/queries/client';
|
||||
import { stripHTML } from 'soapbox/utils/html';
|
||||
import { onlyEmoji } from 'soapbox/utils/rich-content';
|
||||
|
||||
import type { Chat } from 'pl-api';
|
||||
import type { Menu as IMenu } from 'soapbox/components/dropdown-menu';
|
||||
import type { ChatMessage as ChatMessageEntity } from 'soapbox/types/entities';
|
||||
|
||||
@@ -43,7 +44,7 @@ const parseContent = (chatMessage: ChatMessageEntity) => {
|
||||
};
|
||||
|
||||
interface IChatMessage {
|
||||
chat: IChat;
|
||||
chat: Chat;
|
||||
chatMessage: ChatMessageEntity;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,10 +3,11 @@ import { defineMessages, useIntl } from 'react-intl';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { CardTitle, HStack, IconButton, Stack } from 'soapbox/components/ui';
|
||||
import { IChat } from 'soapbox/queries/chats';
|
||||
|
||||
import ChatList from '../../chat-list';
|
||||
|
||||
import type { Chat } from 'pl-api';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'column.chats', defaultMessage: 'Chats' },
|
||||
});
|
||||
@@ -15,7 +16,7 @@ const ChatPageSidebar = () => {
|
||||
const intl = useIntl();
|
||||
const history = useHistory();
|
||||
|
||||
const handleClickChat = (chat: IChat) => {
|
||||
const handleClickChat = (chat: Chat) => {
|
||||
history.push(`/chats/${chat.id}`);
|
||||
};
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { FormattedMessage } from 'react-intl';
|
||||
import { Stack } from 'soapbox/components/ui';
|
||||
import { ChatWidgetScreens, useChatContext } from 'soapbox/contexts/chat-context';
|
||||
import { useStatContext } from 'soapbox/contexts/stat-context';
|
||||
import { IChat, useChats } from 'soapbox/queries/chats';
|
||||
import { useChats } from 'soapbox/queries/chats';
|
||||
|
||||
import ChatList from '../chat-list';
|
||||
import ChatSearch from '../chat-search/chat-search';
|
||||
@@ -16,13 +16,15 @@ import { Pane } from '../ui';
|
||||
|
||||
import Blankslate from './blankslate';
|
||||
|
||||
import type { Chat } from 'pl-api';
|
||||
|
||||
const ChatPane = () => {
|
||||
const { unreadChatsCount } = useStatContext();
|
||||
|
||||
const { screen, changeScreen, isOpen, toggleChatPane } = useChatContext();
|
||||
const { chatsQuery: { data: chats, isLoading } } = useChats();
|
||||
|
||||
const handleClickChat = (nextChat: IChat) => {
|
||||
const handleClickChat = (nextChat: Chat) => {
|
||||
changeScreen(ChatWidgetScreens.CHAT, nextChat.id);
|
||||
};
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Avatar, HStack, Stack, Text } from 'soapbox/components/ui';
|
||||
import VerificationBadge from 'soapbox/components/verification-badge';
|
||||
import useAccountSearch from 'soapbox/queries/search';
|
||||
|
||||
import type { Account } from 'soapbox/types/entities';
|
||||
import type { Account } from 'pl-api';
|
||||
|
||||
interface IResults {
|
||||
accountSearchResult: ReturnType<typeof useAccountSearch>;
|
||||
|
||||
@@ -6,12 +6,13 @@ import { uploadMedia } from 'soapbox/actions/media';
|
||||
import { Stack } from 'soapbox/components/ui';
|
||||
import { useAppDispatch } from 'soapbox/hooks';
|
||||
import { normalizeAttachment } from 'soapbox/normalizers';
|
||||
import { IChat, useChatActions } from 'soapbox/queries/chats';
|
||||
import { useChatActions } from 'soapbox/queries/chats';
|
||||
import toast from 'soapbox/toast';
|
||||
|
||||
import ChatComposer from './chat-composer';
|
||||
import ChatMessageList from './chat-message-list';
|
||||
|
||||
import type { Chat as ChatEntity } from 'pl-api';
|
||||
import type { PlfeResponse } from 'soapbox/api';
|
||||
import type { Attachment } from 'soapbox/types/entities';
|
||||
|
||||
@@ -23,7 +24,7 @@ const messages = defineMessages({
|
||||
});
|
||||
|
||||
interface ChatInterface {
|
||||
chat: IChat;
|
||||
chat: ChatEntity;
|
||||
inputRef?: MutableRefObject<HTMLTextAreaElement | null>;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
||||
|
||||
const renderButtons = useCallback(() => (
|
||||
<HStack alignItems='center' space={2}>
|
||||
{features.media && <UploadButtonContainer composeId={id} />}
|
||||
<UploadButtonContainer composeId={id} />
|
||||
<EmojiPickerDropdown onPickEmoji={handleEmojiPick} condensed={shouldCondense} />
|
||||
{features.polls && <PollButton composeId={id} />}
|
||||
{features.scheduledStatuses && <ScheduleButton composeId={id} />}
|
||||
|
||||
@@ -20,7 +20,7 @@ const ReplyMentions: React.FC<IReplyMentions> = ({ composeId }) => {
|
||||
const status = useAppSelector<StatusEntity | null>(state => getStatus(state, { id: compose.in_reply_to! }));
|
||||
const to = compose.to;
|
||||
|
||||
if (!features.explicitAddressing || !status || !to) {
|
||||
if (!features.createStatusExplicitAddressing || !status || !to) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ const Settings = () => {
|
||||
<Preferences />
|
||||
</CardBody>
|
||||
|
||||
{(features.security || features.accountAliases) && (
|
||||
{(features.security || features.manageAccountAliases) && (
|
||||
<>
|
||||
<CardHeader>
|
||||
<CardTitle title={intl.formatMessage(messages.other)} />
|
||||
|
||||
@@ -2,15 +2,15 @@ import React, { useMemo, useState } from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { useCreateGroup, type CreateGroupParams } from 'soapbox/api/hooks';
|
||||
import { useCreateGroup } from 'soapbox/api/hooks';
|
||||
import { Modal, Stack } from 'soapbox/components/ui';
|
||||
import { type Group } from 'soapbox/schemas';
|
||||
import toast from 'soapbox/toast';
|
||||
|
||||
import ConfirmationStep from './steps/confirmation-step';
|
||||
import DetailsStep from './steps/details-step';
|
||||
import PrivacyStep from './steps/privacy-step';
|
||||
|
||||
import type { CreateGroupParams } from 'pl-api';
|
||||
import type { PlfeResponse } from 'soapbox/api';
|
||||
|
||||
const messages = defineMessages({
|
||||
@@ -22,7 +22,6 @@ const messages = defineMessages({
|
||||
enum Steps {
|
||||
ONE = 'ONE',
|
||||
TWO = 'TWO',
|
||||
THREE = 'THREE',
|
||||
}
|
||||
|
||||
interface ICreateGroupModal {
|
||||
@@ -34,7 +33,7 @@ const CreateGroupModal: React.FC<ICreateGroupModal> = ({ onClose }) => {
|
||||
|
||||
const [group, setGroup] = useState<Group | null>(null);
|
||||
const [params, setParams] = useState<CreateGroupParams>({
|
||||
group_visibility: 'everyone',
|
||||
display_name: '',
|
||||
});
|
||||
const [currentStep, setCurrentStep] = useState<Steps>(Steps.ONE);
|
||||
|
||||
@@ -46,24 +45,19 @@ const CreateGroupModal: React.FC<ICreateGroupModal> = ({ onClose }) => {
|
||||
|
||||
const confirmationText = useMemo(() => {
|
||||
switch (currentStep) {
|
||||
case Steps.THREE:
|
||||
return intl.formatMessage(messages.done);
|
||||
case Steps.TWO:
|
||||
return intl.formatMessage(messages.create);
|
||||
return intl.formatMessage(messages.done);
|
||||
default:
|
||||
return intl.formatMessage(messages.next);
|
||||
return intl.formatMessage(messages.create);
|
||||
}
|
||||
}, [currentStep]);
|
||||
|
||||
const handleNextStep = () => {
|
||||
switch (currentStep) {
|
||||
case Steps.ONE:
|
||||
setCurrentStep(Steps.TWO);
|
||||
break;
|
||||
case Steps.TWO:
|
||||
createGroup(params, {
|
||||
onSuccess(group) {
|
||||
setCurrentStep(Steps.THREE);
|
||||
setCurrentStep(Steps.TWO);
|
||||
setGroup(group);
|
||||
},
|
||||
onError(error: { response?: PlfeResponse }) {
|
||||
@@ -74,7 +68,7 @@ const CreateGroupModal: React.FC<ICreateGroupModal> = ({ onClose }) => {
|
||||
},
|
||||
});
|
||||
break;
|
||||
case Steps.THREE:
|
||||
case Steps.TWO:
|
||||
handleClose();
|
||||
break;
|
||||
default:
|
||||
@@ -85,26 +79,13 @@ const CreateGroupModal: React.FC<ICreateGroupModal> = ({ onClose }) => {
|
||||
const renderStep = () => {
|
||||
switch (currentStep) {
|
||||
case Steps.ONE:
|
||||
return <PrivacyStep params={params} onChange={setParams} />;
|
||||
case Steps.TWO:
|
||||
return <DetailsStep params={params} onChange={setParams} />;
|
||||
case Steps.THREE:
|
||||
case Steps.TWO:
|
||||
return <ConfirmationStep group={group} />;
|
||||
}
|
||||
};
|
||||
|
||||
const renderModalTitle = () => {
|
||||
switch (currentStep) {
|
||||
case Steps.ONE:
|
||||
return <FormattedMessage id='navigation_bar.create_group' defaultMessage='Create group' />;
|
||||
default:
|
||||
if (params.group_visibility === 'everyone') {
|
||||
return <FormattedMessage id='navigation_bar.create_group.public' defaultMessage='Create public group' />;
|
||||
} else {
|
||||
return <FormattedMessage id='navigation_bar.create_group.private' defaultMessage='Create private group' />;
|
||||
}
|
||||
}
|
||||
};
|
||||
const renderModalTitle = () => <FormattedMessage id='navigation_bar.create_group' defaultMessage='Create group' />;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import { CreateGroupParams } from 'soapbox/api/hooks';
|
||||
import { Form, FormGroup, Input, Textarea } from 'soapbox/components/ui';
|
||||
import AvatarPicker from 'soapbox/features/edit-profile/components/avatar-picker';
|
||||
import HeaderPicker from 'soapbox/features/edit-profile/components/header-picker';
|
||||
@@ -9,6 +8,8 @@ import { useAppSelector, useInstance } from 'soapbox/hooks';
|
||||
import { usePreview } from 'soapbox/hooks/forms';
|
||||
import resizeImage from 'soapbox/utils/resize-image';
|
||||
|
||||
import type { CreateGroupParams } from 'pl-api';
|
||||
|
||||
const messages = defineMessages({
|
||||
groupNamePlaceholder: { id: 'manage_group.fields.name_placeholder', defaultMessage: 'Group Name' },
|
||||
groupDescriptionPlaceholder: { id: 'manage_group.fields.description_placeholder', defaultMessage: 'Description' },
|
||||
@@ -17,7 +18,7 @@ const messages = defineMessages({
|
||||
|
||||
interface IDetailsStep {
|
||||
params: CreateGroupParams;
|
||||
onChange(params: CreateGroupParams): void;
|
||||
onChange: (params: CreateGroupParams) => void;
|
||||
}
|
||||
|
||||
const DetailsStep: React.FC<IDetailsStep> = ({ params, onChange }) => {
|
||||
@@ -52,7 +53,10 @@ const DetailsStep: React.FC<IDetailsStep> = ({ params, onChange }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleImageClear = (property: keyof CreateGroupParams) => () => onChange({ [property]: undefined });
|
||||
const handleImageClear = (property: keyof CreateGroupParams) => () => onChange({
|
||||
...params,
|
||||
[property]: undefined,
|
||||
});
|
||||
|
||||
return (
|
||||
<Form>
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { type CreateGroupParams } from 'soapbox/api/hooks';
|
||||
import List, { ListItem } from 'soapbox/components/list';
|
||||
import { Form, FormGroup, Stack, Text } from 'soapbox/components/ui';
|
||||
|
||||
interface IPrivacyStep {
|
||||
params: CreateGroupParams;
|
||||
onChange(params: CreateGroupParams): void;
|
||||
}
|
||||
|
||||
const PrivacyStep: React.FC<IPrivacyStep> = ({ params, onChange }) => {
|
||||
const visibility = params.group_visibility || 'everyone';
|
||||
|
||||
const onChangePrivacy = (group_visibility: CreateGroupParams['group_visibility']) => {
|
||||
onChange({ ...params, group_visibility });
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack className='mx-auto max-w-xs py-10' space={2}>
|
||||
<Text size='3xl' weight='bold' align='center'>
|
||||
<FormattedMessage id='manage_group.get_started' defaultMessage='Let’s get started!' />
|
||||
</Text>
|
||||
<Text theme='muted' align='center'>
|
||||
<FormattedMessage id='manage_group.tagline' defaultMessage='Groups connect you with others based on shared interests.' />
|
||||
</Text>
|
||||
</Stack>
|
||||
<Form>
|
||||
<FormGroup
|
||||
labelText={<FormattedMessage id='manage_group.privacy.label' defaultMessage='Privacy settings' />}
|
||||
>
|
||||
<List>
|
||||
<ListItem
|
||||
label={<Text weight='medium'><FormattedMessage id='manage_group.privacy.public.label' defaultMessage='Public' /></Text>}
|
||||
hint={<FormattedMessage id='manage_group.privacy.public.hint' defaultMessage='Discoverable. Anyone can join.' />}
|
||||
onSelect={() => onChangePrivacy('everyone')}
|
||||
isSelected={visibility === 'everyone'}
|
||||
/>
|
||||
|
||||
<ListItem
|
||||
label={<Text weight='medium'><FormattedMessage id='manage_group.privacy.private.label' defaultMessage='Private (Owner approval required)' /></Text>}
|
||||
hint={<FormattedMessage id='manage_group.privacy.private.hint' defaultMessage='Discoverable. Users can join after their request is approved.' />}
|
||||
onSelect={() => onChangePrivacy('members_only')}
|
||||
isSelected={visibility === 'members_only'}
|
||||
/>
|
||||
</List>
|
||||
</FormGroup>
|
||||
<Text size='sm' theme='muted' align='center'>
|
||||
<FormattedMessage id='manage_group.privacy.hint' defaultMessage='These settings cannot be changed later.' />
|
||||
</Text>
|
||||
</Form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export { PrivacyStep as default };
|
||||
Reference in New Issue
Block a user