From 472092201584360950048bc512aa181fcc5046fc Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 26 Apr 2023 12:45:39 -0500 Subject: [PATCH 1/7] ComposeButton: style group compose button --- .../features/ui/components/compose-button.tsx | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/app/soapbox/features/ui/components/compose-button.tsx b/app/soapbox/features/ui/components/compose-button.tsx index 155a7194e..dc414647b 100644 --- a/app/soapbox/features/ui/components/compose-button.tsx +++ b/app/soapbox/features/ui/components/compose-button.tsx @@ -1,11 +1,23 @@ import React from 'react'; import { FormattedMessage } from 'react-intl'; +import { useLocation, useRouteMatch } from 'react-router-dom'; import { openModal } from 'soapbox/actions/modals'; -import { Button } from 'soapbox/components/ui'; +import { Avatar, Button, HStack } from 'soapbox/components/ui'; import { useAppDispatch } from 'soapbox/hooks'; +import { useGroupLookup } from 'soapbox/hooks/api/groups/useGroupLookup'; const ComposeButton = () => { + const location = useLocation(); + + if (location.pathname.startsWith('/group/')) { + return ; + } + + return ; +}; + +const HomeComposeButton = () => { const dispatch = useAppDispatch(); const onOpenCompose = () => dispatch(openModal('COMPOSE')); @@ -22,4 +34,32 @@ const ComposeButton = () => { ); }; +const GroupComposeButton = () => { + const dispatch = useAppDispatch(); + const match = useRouteMatch<{ groupSlug: string }>('/group/:groupSlug'); + const { entity: group } = useGroupLookup(match?.params.groupSlug || ''); + + const onOpenCompose = () => dispatch(openModal('COMPOSE')); + + if (group) { + return ( + + ); + } + + return null; +}; + export default ComposeButton; From f9b595406063daefedf3f64c49c11df100193db5 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 26 Apr 2023 15:07:19 -0500 Subject: [PATCH 2/7] ComposeModal: allow passing a composeId --- app/soapbox/actions/compose.ts | 10 ++++++++++ app/soapbox/features/ui/components/compose-button.tsx | 7 ++++++- .../features/ui/components/modals/compose-modal.tsx | 7 +++---- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/app/soapbox/actions/compose.ts b/app/soapbox/actions/compose.ts index 995f37c15..61303d15b 100644 --- a/app/soapbox/actions/compose.ts +++ b/app/soapbox/actions/compose.ts @@ -22,6 +22,7 @@ import { createStatus } from './statuses'; import type { AutoSuggestion } from 'soapbox/components/autosuggest-input'; import type { Emoji } from 'soapbox/features/emoji'; +import type { Group } from 'soapbox/schemas'; import type { AppDispatch, RootState } from 'soapbox/store'; import type { Account, APIEntity, Status, Tag } from 'soapbox/types/entities'; import type { History } from 'soapbox/types/history'; @@ -168,6 +169,14 @@ const cancelQuoteCompose = () => ({ id: 'compose-modal', }); +const groupComposeModal = (group: Group) => + (dispatch: AppDispatch, getState: () => RootState) => { + const composeId = `group:${group.id}`; + + dispatch(groupCompose(composeId, group.id)); + dispatch(openModal('COMPOSE', { composeId })); + }; + const resetCompose = (composeId = 'compose-modal') => ({ type: COMPOSE_RESET, id: composeId, @@ -829,6 +838,7 @@ export { uploadComposeFail, undoUploadCompose, groupCompose, + groupComposeModal, setGroupTimelineVisible, clearComposeSuggestions, fetchComposeSuggestions, diff --git a/app/soapbox/features/ui/components/compose-button.tsx b/app/soapbox/features/ui/components/compose-button.tsx index dc414647b..e16e2713c 100644 --- a/app/soapbox/features/ui/components/compose-button.tsx +++ b/app/soapbox/features/ui/components/compose-button.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { FormattedMessage } from 'react-intl'; import { useLocation, useRouteMatch } from 'react-router-dom'; +import { groupComposeModal } from 'soapbox/actions/compose'; import { openModal } from 'soapbox/actions/modals'; import { Avatar, Button, HStack } from 'soapbox/components/ui'; import { useAppDispatch } from 'soapbox/hooks'; @@ -39,7 +40,11 @@ const GroupComposeButton = () => { const match = useRouteMatch<{ groupSlug: string }>('/group/:groupSlug'); const { entity: group } = useGroupLookup(match?.params.groupSlug || ''); - const onOpenCompose = () => dispatch(openModal('COMPOSE')); + const onOpenCompose = () => { + if (group) { + dispatch(groupComposeModal(group)); + } + }; if (group) { return ( diff --git a/app/soapbox/features/ui/components/modals/compose-modal.tsx b/app/soapbox/features/ui/components/modals/compose-modal.tsx index 72a06fb7c..079bedc42 100644 --- a/app/soapbox/features/ui/components/modals/compose-modal.tsx +++ b/app/soapbox/features/ui/components/modals/compose-modal.tsx @@ -18,14 +18,13 @@ const messages = defineMessages({ interface IComposeModal { onClose: (type?: string) => void + composeId?: string } -const ComposeModal: React.FC = ({ onClose }) => { +const ComposeModal: React.FC = ({ onClose, composeId = 'compose-modal' }) => { const intl = useIntl(); const dispatch = useAppDispatch(); const node = useRef(null); - - const composeId = 'compose-modal'; const compose = useCompose(composeId); const { id: statusId, privacy, in_reply_to: inReplyTo, quote } = compose!; @@ -79,7 +78,7 @@ const ComposeModal: React.FC = ({ onClose }) => { 'ring-2 ring-offset-2 ring-primary-600': isDraggedOver, })} > - + ); }; From a193108ef8efabe98948861f4b10842986212b19 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 26 Apr 2023 15:14:54 -0500 Subject: [PATCH 3/7] ComposeModal: become group-aware --- app/soapbox/features/ui/components/modals/compose-modal.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/soapbox/features/ui/components/modals/compose-modal.tsx b/app/soapbox/features/ui/components/modals/compose-modal.tsx index 079bedc42..f29df9042 100644 --- a/app/soapbox/features/ui/components/modals/compose-modal.tsx +++ b/app/soapbox/features/ui/components/modals/compose-modal.tsx @@ -27,7 +27,7 @@ const ComposeModal: React.FC = ({ onClose, composeId = 'compose-m const node = useRef(null); const compose = useCompose(composeId); - const { id: statusId, privacy, in_reply_to: inReplyTo, quote } = compose!; + const { id: statusId, privacy, in_reply_to: inReplyTo, quote, group_id: groupId } = compose!; const { isDragging, isDraggedOver } = useDraggedFiles(node, (files) => { dispatch(uploadCompose(composeId, files, intl)); @@ -59,6 +59,10 @@ const ComposeModal: React.FC = ({ onClose, composeId = 'compose-m return ; } else if (privacy === 'direct') { return ; + } else if (inReplyTo && groupId) { + return ; + } else if (groupId) { + return ; } else if (inReplyTo) { return ; } else if (quote) { From 506cad3b706b6cc98afb5ce68d41a460012a54bc Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 26 Apr 2023 15:33:34 -0500 Subject: [PATCH 4/7] FloatingActionButton: contextual group support --- .../features/ui/components/compose-button.tsx | 40 +++++++------- .../ui/components/floating-action-button.tsx | 53 +++++++++++++++++-- 2 files changed, 66 insertions(+), 27 deletions(-) diff --git a/app/soapbox/features/ui/components/compose-button.tsx b/app/soapbox/features/ui/components/compose-button.tsx index e16e2713c..a2944f699 100644 --- a/app/soapbox/features/ui/components/compose-button.tsx +++ b/app/soapbox/features/ui/components/compose-button.tsx @@ -40,31 +40,27 @@ const GroupComposeButton = () => { const match = useRouteMatch<{ groupSlug: string }>('/group/:groupSlug'); const { entity: group } = useGroupLookup(match?.params.groupSlug || ''); + if (!group) return null; + const onOpenCompose = () => { - if (group) { - dispatch(groupComposeModal(group)); - } + dispatch(groupComposeModal(group)); }; - if (group) { - return ( - - ); - } - - return null; + return ( + + ); }; export default ComposeButton; diff --git a/app/soapbox/features/ui/components/floating-action-button.tsx b/app/soapbox/features/ui/components/floating-action-button.tsx index 8800cf444..0e972ef40 100644 --- a/app/soapbox/features/ui/components/floating-action-button.tsx +++ b/app/soapbox/features/ui/components/floating-action-button.tsx @@ -1,20 +1,30 @@ import clsx from 'clsx'; import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; +import { useLocation, useRouteMatch } from 'react-router-dom'; +import { groupComposeModal } from 'soapbox/actions/compose'; import { openModal } from 'soapbox/actions/modals'; -import { Icon } from 'soapbox/components/ui'; +import { Avatar, HStack, Icon } from 'soapbox/components/ui'; import { useAppDispatch } from 'soapbox/hooks'; +import { useGroupLookup } from 'soapbox/hooks/api/groups/useGroupLookup'; const messages = defineMessages({ publish: { id: 'compose_form.publish', defaultMessage: 'Publish' }, }); -interface IFloatingActionButton { -} - /** FloatingActionButton (aka FAB), a composer button that floats in the corner on mobile. */ -const FloatingActionButton: React.FC = () => { +const FloatingActionButton: React.FC = () => { + const location = useLocation(); + + if (location.pathname.startsWith('/group/')) { + return ; + } + + return ; +}; + +const HomeFAB: React.FC = () => { const intl = useIntl(); const dispatch = useAppDispatch(); @@ -39,4 +49,37 @@ const FloatingActionButton: React.FC = () => { ); }; +const GroupFAB: React.FC = () => { + const intl = useIntl(); + const dispatch = useAppDispatch(); + + const match = useRouteMatch<{ groupSlug: string }>('/group/:groupSlug'); + const { entity: group } = useGroupLookup(match?.params.groupSlug || ''); + + if (!group) return null; + + const handleOpenComposeModal = () => { + dispatch(groupComposeModal(group)); + }; + + return ( + + ); +}; + export default FloatingActionButton; From 8ee5727cd896ac8f76c6134f6f690a6d869f97bf Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 26 Apr 2023 15:53:06 -0500 Subject: [PATCH 5/7] ComposeModal: add share to group toggle --- .../ui/components/modals/compose-modal.tsx | 49 +++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/app/soapbox/features/ui/components/modals/compose-modal.tsx b/app/soapbox/features/ui/components/modals/compose-modal.tsx index f29df9042..358a6d648 100644 --- a/app/soapbox/features/ui/components/modals/compose-modal.tsx +++ b/app/soapbox/features/ui/components/modals/compose-modal.tsx @@ -2,11 +2,12 @@ import clsx from 'clsx'; import React, { useRef } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; -import { cancelReplyCompose, uploadCompose } from 'soapbox/actions/compose'; +import { cancelReplyCompose, setGroupTimelineVisible, uploadCompose } from 'soapbox/actions/compose'; import { openModal, closeModal } from 'soapbox/actions/modals'; import { checkComposeContent } from 'soapbox/components/modal-root'; -import { Modal } from 'soapbox/components/ui'; -import { useAppDispatch, useCompose, useDraggedFiles } from 'soapbox/hooks'; +import { HStack, Modal, Text, Toggle } from 'soapbox/components/ui'; +import { useAppDispatch, useAppSelector, useCompose, useDraggedFiles } from 'soapbox/hooks'; +import { useGroup } from 'soapbox/hooks/api'; import ComposeForm from '../../../compose/components/compose-form'; @@ -82,9 +83,49 @@ const ComposeModal: React.FC = ({ onClose, composeId = 'compose-m 'ring-2 ring-offset-2 ring-primary-600': isDraggedOver, })} > - + } + /> ); }; +interface IComposeFormGroupToggle { + composeId: string + groupId: string | null +} + +const ComposeFormGroupToggle: React.FC = ({ composeId, groupId }) => { + const dispatch = useAppDispatch(); + const { group } = useGroup(groupId || '', false); + + const groupTimelineVisible = useAppSelector((state) => !!state.compose.get(composeId)?.group_timeline_visible); + + const handleToggleChange = () => { + dispatch(setGroupTimelineVisible(composeId, !groupTimelineVisible)); + }; + + const labelId = `group-timeline-visible+${composeId}`; + + if (!group) return null; + if (group.locked) return null; + + return ( + + + + + ); +}; + export default ComposeModal; From 44300539d53307556725e8f1bbd8a2990b7b7dd0 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 26 Apr 2023 16:41:20 -0500 Subject: [PATCH 6/7] yarn i18n --- app/soapbox/locales/en.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/soapbox/locales/en.json b/app/soapbox/locales/en.json index 3f6c24526..8c378c652 100644 --- a/app/soapbox/locales/en.json +++ b/app/soapbox/locales/en.json @@ -1039,6 +1039,7 @@ "navbar.login.username.placeholder": "Email or username", "navigation.chats": "Chats", "navigation.compose": "Compose", + "navigation.compose_group": "Compose to Group", "navigation.dashboard": "Dashboard", "navigation.developers": "Developers", "navigation.direct_messages": "Messages", @@ -1052,6 +1053,8 @@ "navigation_bar.compose_direct": "Direct message", "navigation_bar.compose_edit": "Edit post", "navigation_bar.compose_event": "Manage event", + "navigation_bar.compose_group": "Compose to group", + "navigation_bar.compose_group_reply": "Reply to group post", "navigation_bar.compose_quote": "Quote post", "navigation_bar.compose_reply": "Reply to post", "navigation_bar.create_event": "Create new event", From a1b9d8a682a6f8216c185429b8fdce6c8fc86214 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 27 Apr 2023 13:20:53 -0500 Subject: [PATCH 7/7] Fix ComposeButton tests --- .../features/ui/components/__tests__/compose-button.test.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/soapbox/features/ui/components/__tests__/compose-button.test.tsx b/app/soapbox/features/ui/components/__tests__/compose-button.test.tsx index c98b7aa50..11d9bdea1 100644 --- a/app/soapbox/features/ui/components/__tests__/compose-button.test.tsx +++ b/app/soapbox/features/ui/components/__tests__/compose-button.test.tsx @@ -3,6 +3,7 @@ import React from 'react'; import { IntlProvider } from 'react-intl'; import { Provider } from 'react-redux'; import '@testing-library/jest-dom'; +import { MemoryRouter } from 'react-router-dom'; import { MODAL_OPEN } from 'soapbox/actions/modals'; import { mockStore, rootState } from 'soapbox/jest/test-helpers'; @@ -14,7 +15,9 @@ const renderComposeButton = () => { render( - + + + , );