From bfd40fa3733bfbf6cf9889c4529fa5aa95242b4b Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 29 Mar 2023 14:51:29 -0500 Subject: [PATCH 1/9] Boilerplate Edit Group page by copying code from the modal --- app/soapbox/features/group/edit-group.tsx | 185 ++++++++++++++++++ app/soapbox/features/group/manage-group.tsx | 7 +- app/soapbox/features/ui/index.tsx | 2 + .../features/ui/util/async-components.ts | 4 + 4 files changed, 193 insertions(+), 5 deletions(-) create mode 100644 app/soapbox/features/group/edit-group.tsx diff --git a/app/soapbox/features/group/edit-group.tsx b/app/soapbox/features/group/edit-group.tsx new file mode 100644 index 000000000..1b6f62fc4 --- /dev/null +++ b/app/soapbox/features/group/edit-group.tsx @@ -0,0 +1,185 @@ +import clsx from 'clsx'; +import React, { useEffect, useState } from 'react'; +import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; + +import { + changeGroupEditorTitle, + changeGroupEditorDescription, + changeGroupEditorMedia, +} from 'soapbox/actions/groups'; +import Icon from 'soapbox/components/icon'; +import { Avatar, Column, Form, FormGroup, HStack, Input, Text, Textarea } from 'soapbox/components/ui'; +import { useAppDispatch, useAppSelector, useInstance } from 'soapbox/hooks'; +import { isDefaultAvatar, isDefaultHeader } from 'soapbox/utils/accounts'; +import resizeImage from 'soapbox/utils/resize-image'; + +import type { List as ImmutableList } from 'immutable'; + +interface IMediaInput { + src: string | null + accept: string + onChange: React.ChangeEventHandler + disabled: boolean +} + +const messages = defineMessages({ + heading: { id: 'navigation_bar.edit_group', defaultMessage: 'Edit Group' }, + groupNamePlaceholder: { id: 'manage_group.fields.name_placeholder', defaultMessage: 'Group Name' }, + groupDescriptionPlaceholder: { id: 'manage_group.fields.description_placeholder', defaultMessage: 'Description' }, +}); + +const HeaderPicker: React.FC = ({ src, onChange, accept, disabled }) => { + return ( + + ); +}; + +const AvatarPicker: React.FC = ({ src, onChange, accept, disabled }) => { + return ( + + ); +}; + +const EditGroup = () => { + const intl = useIntl(); + const dispatch = useAppDispatch(); + const instance = useInstance(); + + const groupId = useAppSelector((state) => state.group_editor.groupId); + const isUploading = useAppSelector((state) => state.group_editor.isUploading); + const name = useAppSelector((state) => state.group_editor.displayName); + const description = useAppSelector((state) => state.group_editor.note); + + const [avatarSrc, setAvatarSrc] = useState(null); + const [headerSrc, setHeaderSrc] = useState(null); + + const attachmentTypes = useAppSelector( + state => state.instance.configuration.getIn(['media_attachments', 'supported_mime_types']) as ImmutableList, + )?.filter(type => type.startsWith('image/')).toArray().join(','); + + const onChangeName: React.ChangeEventHandler = ({ target }) => { + dispatch(changeGroupEditorTitle(target.value)); + }; + + const onChangeDescription: React.ChangeEventHandler = ({ target }) => { + dispatch(changeGroupEditorDescription(target.value)); + }; + + const handleFileChange: React.ChangeEventHandler = e => { + const rawFile = e.target.files?.item(0); + + if (!rawFile) return; + + if (e.target.name === 'avatar') { + resizeImage(rawFile, 400 * 400).then(file => { + dispatch(changeGroupEditorMedia('avatar', file)); + setAvatarSrc(URL.createObjectURL(file)); + }).catch(console.error); + } else { + resizeImage(rawFile, 1920 * 1080).then(file => { + dispatch(changeGroupEditorMedia('header', file)); + setHeaderSrc(URL.createObjectURL(file)); + }).catch(console.error); + } + }; + + useEffect(() => { + if (!groupId) return; + + dispatch((_, getState) => { + const group = getState().groups.items.get(groupId); + if (!group) return; + if (group.avatar && !isDefaultAvatar(group.avatar)) setAvatarSrc(group.avatar); + if (group.header && !isDefaultHeader(group.header)) setHeaderSrc(group.header); + }); + }, [groupId]); + + return ( + +
+
+ + +
+ } + > + + + } + > +