mirror of
https://github.com/mastodon/mastodon.git
synced 2026-09-13 17:25:51 -05:00
Composer redesign: Add compose buttons (#40012)
This commit is contained in:
@@ -81,3 +81,7 @@
|
||||
border: none;
|
||||
width: calc(100% - (2 * var(--space-sm)));
|
||||
}
|
||||
|
||||
.popoverMenu {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ export const DropdownPopover = <As extends React.ElementType>({
|
||||
matchReferenceWidth,
|
||||
closeOnClickOutside,
|
||||
children,
|
||||
className,
|
||||
...props
|
||||
}: DropdownProps<As> & Omit<PopoverProps, 'children'>) => {
|
||||
const popoverProps = {
|
||||
@@ -56,7 +57,14 @@ export const DropdownPopover = <As extends React.ElementType>({
|
||||
return (
|
||||
<Popover {...popoverProps}>
|
||||
{({ props: popoverChildProps }) => (
|
||||
<Dropdown {...props} {...popoverChildProps}>
|
||||
<Dropdown
|
||||
{...props}
|
||||
{...popoverChildProps}
|
||||
className={classNames(
|
||||
className,
|
||||
props.maxWidth && classes.popoverMenu,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</Dropdown>
|
||||
)}
|
||||
|
||||
@@ -37,10 +37,9 @@ const ComposeMediaAttachments: React.FC = () => {
|
||||
|
||||
if (totalAttachments === 1) {
|
||||
return (
|
||||
<ComposeUpload
|
||||
id={attachments.at(0)?.id}
|
||||
className={classes.mediaSingle}
|
||||
/>
|
||||
<div className={classes.mediaSingle}>
|
||||
<ComposeUpload id={attachments.at(0)?.id} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,30 +61,32 @@ export const ComposeFooter: React.FC<{ onEmojiPick: OnEmojiPick }> = ({
|
||||
/>
|
||||
</IconButton>
|
||||
|
||||
<span className={classes.counter}>
|
||||
<FormattedMessage
|
||||
id='compose.counter'
|
||||
defaultMessage='{current, number}/{max, number}'
|
||||
values={{ current, max }}
|
||||
/>
|
||||
</span>
|
||||
|
||||
<Button
|
||||
color='neutral'
|
||||
type='submit'
|
||||
disabled={!canSubmit}
|
||||
loading={isSubmitting}
|
||||
>
|
||||
{type !== 'message' && (
|
||||
<FormattedMessage id='compose.publish' defaultMessage='Publish' />
|
||||
)}
|
||||
{type === 'message' && (
|
||||
<div className={classes.flexGrowWrap}>
|
||||
<span className={classes.counter}>
|
||||
<FormattedMessage
|
||||
id='compose.message.publish'
|
||||
defaultMessage='Send'
|
||||
id='compose.counter'
|
||||
defaultMessage='{current, number}/{max, number}'
|
||||
values={{ current, max }}
|
||||
/>
|
||||
)}
|
||||
</Button>
|
||||
</span>
|
||||
|
||||
<Button
|
||||
color='neutral'
|
||||
type='submit'
|
||||
disabled={!canSubmit}
|
||||
loading={isSubmitting}
|
||||
>
|
||||
{type !== 'message' && (
|
||||
<FormattedMessage id='compose.publish' defaultMessage='Publish' />
|
||||
)}
|
||||
{type === 'message' && (
|
||||
<FormattedMessage
|
||||
id='compose.message.publish'
|
||||
defaultMessage='Send'
|
||||
/>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import { XIcon } from '@phosphor-icons/react';
|
||||
import { ArrowsOutSimpleIcon, MinusIcon, XIcon } from '@phosphor-icons/react';
|
||||
|
||||
import { IconButton } from '@/mastodon/components/button/redesign';
|
||||
import { createAppSelector, useAppSelector } from '@/mastodon/store';
|
||||
import {
|
||||
hideComposer,
|
||||
minimizeComposerToggle,
|
||||
selectIsMinimized,
|
||||
} from '@/mastodon/reducers/slices/composer';
|
||||
import {
|
||||
createAppSelector,
|
||||
useAppDispatch,
|
||||
useAppSelector,
|
||||
} from '@/mastodon/store';
|
||||
|
||||
import { selectComposeType } from './selectors';
|
||||
import classes from './styles.module.scss';
|
||||
@@ -42,14 +53,48 @@ const selectComposeFormTitle = createAppSelector(
|
||||
},
|
||||
);
|
||||
|
||||
export const ComposeFormHeader: React.FC<{ id?: string }> = ({ id }) => {
|
||||
export const ComposeFormHeader: React.FC<{
|
||||
id?: string;
|
||||
noMinimize?: boolean;
|
||||
}> = ({ id, noMinimize }) => {
|
||||
const intl = useIntl();
|
||||
const titleMessage = useAppSelector(selectComposeFormTitle);
|
||||
const isMinimized = useAppSelector(selectIsMinimized);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const onClose = useCallback(() => {
|
||||
dispatch(hideComposer());
|
||||
}, [dispatch]);
|
||||
const onMinimize = useCallback(() => {
|
||||
dispatch(minimizeComposerToggle());
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<header className={classes.header}>
|
||||
<h2 id={id}>{intl.formatMessage(titleMessage)}</h2>
|
||||
<IconButton icon={XIcon} variant='ghost' size='sm'>
|
||||
|
||||
{!noMinimize && (
|
||||
<IconButton
|
||||
size='sm'
|
||||
variant='ghost'
|
||||
icon={isMinimized ? ArrowsOutSimpleIcon : MinusIcon}
|
||||
onClick={onMinimize}
|
||||
>
|
||||
{isMinimized ? (
|
||||
<FormattedMessage
|
||||
id='compose.expand'
|
||||
defaultMessage='Show composer'
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id='compose.minimize'
|
||||
defaultMessage='Minimize composer'
|
||||
/>
|
||||
)}
|
||||
</IconButton>
|
||||
)}
|
||||
|
||||
<IconButton icon={XIcon} variant='ghost' size='sm' onClick={onClose}>
|
||||
<FormattedMessage id='lightbox.close' defaultMessage='Close' />
|
||||
</IconButton>
|
||||
</header>
|
||||
|
||||
@@ -3,6 +3,8 @@ import { useCallback, useEffect, useId, useRef } from 'react';
|
||||
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { LockSimpleOpenIcon } from '@phosphor-icons/react';
|
||||
import type { TextareaAutosizeProps } from 'react-textarea-autosize';
|
||||
|
||||
@@ -57,11 +59,15 @@ const messages = defineMessages({
|
||||
|
||||
interface RedesignComposeFormProps {
|
||||
autoFocus?: boolean;
|
||||
className?: string;
|
||||
noMinimize?: boolean;
|
||||
redirectOnSuccess?: boolean;
|
||||
}
|
||||
|
||||
export const RedesignComposeForm: React.FC<RedesignComposeFormProps> = ({
|
||||
autoFocus,
|
||||
className,
|
||||
noMinimize,
|
||||
redirectOnSuccess,
|
||||
}) => {
|
||||
const {
|
||||
@@ -90,21 +96,24 @@ export const RedesignComposeForm: React.FC<RedesignComposeFormProps> = ({
|
||||
role='dialog'
|
||||
onSubmit={onSubmit}
|
||||
aria-labelledby={titleId}
|
||||
className={classes.root}
|
||||
className={classNames(className, classes.root)}
|
||||
>
|
||||
<ComposeFormHeader id={titleId} />
|
||||
<div className={classes.toolbar}>
|
||||
{type !== 'message' && <ComposeVisibility />}
|
||||
<ComposeFormHeader id={titleId} noMinimize={noMinimize} />
|
||||
|
||||
{type === 'message' && (
|
||||
<p className={classes.toolbarMessage}>
|
||||
<Icon id='lock-open' icon={LockSimpleOpenIcon} />
|
||||
<FormattedMessage
|
||||
id='compose.message.notice'
|
||||
defaultMessage='Messages are not end-to-end encrypted'
|
||||
/>
|
||||
</p>
|
||||
)}
|
||||
<div className={classes.toolbar}>
|
||||
<div className={classes.flexGrowWrap}>
|
||||
{type !== 'message' && <ComposeVisibility />}
|
||||
|
||||
{type === 'message' && (
|
||||
<p className={classes.toolbarMessage}>
|
||||
<Icon id='lock-open' icon={LockSimpleOpenIcon} />
|
||||
<FormattedMessage
|
||||
id='compose.message.notice'
|
||||
defaultMessage='Messages are not end-to-end encrypted'
|
||||
/>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ToggleField
|
||||
label={intl.formatMessage(messages.sensitive)}
|
||||
|
||||
@@ -2,12 +2,11 @@ import { length } from 'stringz';
|
||||
|
||||
import type { ApiMediaAttachmentJSON } from '@/mastodon/api_types/media_attachments';
|
||||
import type { StatusVisibility } from '@/mastodon/models/status';
|
||||
import type { ComposeType } from '@/mastodon/reducers/slices/composer';
|
||||
import { createAppSelector } from '@/mastodon/store';
|
||||
|
||||
import { countableText } from '../util/counter';
|
||||
|
||||
export type ComposeType = 'post' | 'message' | 'reply';
|
||||
|
||||
export const selectComposePrivacy = createAppSelector(
|
||||
[
|
||||
(state) => state.compose.get('privacy') as StatusVisibility | null,
|
||||
|
||||
@@ -14,11 +14,12 @@
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
h2 {
|
||||
@include mixins.type-heading-sm;
|
||||
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,21 +27,32 @@
|
||||
display: flex;
|
||||
gap: var(--space-xs);
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
label {
|
||||
font-weight: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbarGrow {
|
||||
margin-right: auto;
|
||||
.flexGrowWrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-xs);
|
||||
flex-grow: 1;
|
||||
|
||||
@media (width < 310px) {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
|
||||
.footer & {
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbarMessage {
|
||||
display: flex;
|
||||
gap: var(--space-2xs);
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
color: var(--color-text-secondary);
|
||||
|
||||
svg {
|
||||
@@ -60,17 +72,29 @@
|
||||
|
||||
border: none;
|
||||
width: 100%;
|
||||
outline: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.textarea {
|
||||
flex-grow: 1;
|
||||
|
||||
> textarea {
|
||||
@include mixins.type-body-lg;
|
||||
|
||||
border: none;
|
||||
width: 100%;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-xs);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.counter {
|
||||
margin-left: auto;
|
||||
font-family: var(--font-monospace);
|
||||
color: var(--color-text-tertiary);
|
||||
}
|
||||
@@ -116,10 +140,16 @@
|
||||
// Attachments
|
||||
|
||||
.mediaSingle {
|
||||
min-width: 120px;
|
||||
max-height: var(--max-media-height-large);
|
||||
width: var(--width);
|
||||
height: var(--height);
|
||||
overflow: auto;
|
||||
|
||||
.mediaUpload {
|
||||
aspect-ratio: var(--width) / var(--height);
|
||||
width: var(--width);
|
||||
min-width: 120px;
|
||||
max-width: 100%;
|
||||
height: var(--height);
|
||||
max-height: var(--max-media-height-large);
|
||||
}
|
||||
}
|
||||
|
||||
.mediaGrid {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
@use '@/styles/mastodon/variables';
|
||||
|
||||
.button {
|
||||
position: fixed;
|
||||
bottom: var(--space-md);
|
||||
right: var(--space-md);
|
||||
}
|
||||
|
||||
.composer,
|
||||
.composerMinimized {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
margin: var(--space-md);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.composer {
|
||||
width: min(560px, calc(100vw - var(--space-md) * 2));
|
||||
min-height: 520px;
|
||||
max-height: 80vh;
|
||||
}
|
||||
|
||||
.composerMinimized {
|
||||
width: min(320px, calc(100vw - var(--space-md) * 2));
|
||||
padding: var(--space-md);
|
||||
}
|
||||
|
||||
@media (width < #{variables.$mobile-menu-breakpoint}) {
|
||||
.composer,
|
||||
.composerMinimized {
|
||||
bottom: var(--mobile-bottom-nav-height);
|
||||
}
|
||||
}
|
||||
116
app/javascript/mastodon/features/compose/redesign/trigger.tsx
Normal file
116
app/javascript/mastodon/features/compose/redesign/trigger.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
/* eslint-disable jsx-a11y/no-autofocus */
|
||||
import type React from 'react';
|
||||
import { lazy, Suspense, useCallback, useState } from 'react';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import {
|
||||
ChatCircleIcon,
|
||||
NewspaperIcon,
|
||||
PenNibIcon,
|
||||
} from '@phosphor-icons/react';
|
||||
|
||||
import { IconButton } from '@/mastodon/components/button/redesign';
|
||||
import { CircularProgress } from '@/mastodon/components/circular_progress';
|
||||
import {
|
||||
Dropdown,
|
||||
DropdownItemButton,
|
||||
DropdownPopover,
|
||||
} from '@/mastodon/components/dropdown/redesign';
|
||||
import { useToggle } from '@/mastodon/hooks/useToggle';
|
||||
import { openNewComposer } from '@/mastodon/reducers/slices/composer';
|
||||
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
|
||||
import { isRedesignEnabled } from '@/mastodon/utils/environment';
|
||||
|
||||
import { ComposeFormHeader } from './header';
|
||||
import classes from './trigger.module.scss';
|
||||
|
||||
const ComposeLazyForm = lazy(() =>
|
||||
import('./index').then(({ RedesignComposeForm }) => ({
|
||||
default: RedesignComposeForm,
|
||||
})),
|
||||
);
|
||||
|
||||
export const ComposeRedesignButton: React.FC = () => {
|
||||
const [ref, setRef] = useState<HTMLButtonElement | null>(null);
|
||||
const [menuOpen, { onFalse: onMenuClose, onToggle: onMenuToggle }] =
|
||||
useToggle();
|
||||
const displayState = useAppSelector((state) => state.composer.displayState);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const handleComposerOpen: React.MouseEventHandler<HTMLButtonElement> =
|
||||
useCallback(
|
||||
(event) => {
|
||||
const {
|
||||
currentTarget: { name },
|
||||
} = event;
|
||||
if (name === 'post' || name === 'message') {
|
||||
dispatch(openNewComposer({ type: name }));
|
||||
onMenuClose();
|
||||
}
|
||||
},
|
||||
[dispatch, onMenuClose],
|
||||
);
|
||||
|
||||
if (!isRedesignEnabled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (displayState === 'minimized') {
|
||||
return (
|
||||
<Dropdown className={classes.composerMinimized} elevation={2}>
|
||||
<ComposeFormHeader />
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
|
||||
if (displayState === 'showing') {
|
||||
return (
|
||||
<Suspense fallback={<CircularProgress strokeWidth={2} size={50} />}>
|
||||
<ComposeLazyForm autoFocus className={classes.composer} />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton
|
||||
icon={PenNibIcon}
|
||||
color='neutral'
|
||||
ref={setRef}
|
||||
onClick={onMenuToggle}
|
||||
className={classes.button}
|
||||
size='lg'
|
||||
>
|
||||
<FormattedMessage
|
||||
id='compose.new'
|
||||
defaultMessage='Write a new post or messsage'
|
||||
/>
|
||||
</IconButton>
|
||||
|
||||
<DropdownPopover
|
||||
isOpen={menuOpen}
|
||||
maxWidth={180}
|
||||
reference={ref}
|
||||
onClose={onMenuClose}
|
||||
placement='top-end'
|
||||
>
|
||||
<DropdownItemButton
|
||||
name='post'
|
||||
onClick={handleComposerOpen}
|
||||
leadingIcon={NewspaperIcon}
|
||||
>
|
||||
<FormattedMessage id='compose.new.post' defaultMessage='Post' />
|
||||
</DropdownItemButton>
|
||||
|
||||
<DropdownItemButton
|
||||
name='message'
|
||||
onClick={handleComposerOpen}
|
||||
leadingIcon={ChatCircleIcon}
|
||||
>
|
||||
<FormattedMessage id='compose.new.message' defaultMessage='Message' />
|
||||
</DropdownItemButton>
|
||||
</DropdownPopover>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -47,12 +47,7 @@ export const ComposeVisibility: React.FC = () => {
|
||||
defaultMessage='To: {button}'
|
||||
values={{
|
||||
button: (
|
||||
<Button
|
||||
className={classes.toolbarGrow}
|
||||
size='sm'
|
||||
onClick={onToggle}
|
||||
ref={setTrigger}
|
||||
>
|
||||
<Button size='sm' onClick={onToggle} ref={setTrigger}>
|
||||
{privacy !== 'private' && (
|
||||
<FormattedMessage
|
||||
id='privacy.public.short'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
import { lazy, PureComponent, Suspense } from 'react';
|
||||
|
||||
import { defineMessages } from 'react-intl';
|
||||
|
||||
@@ -23,6 +23,7 @@ import { PictureInPicture } from 'mastodon/features/picture_in_picture';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { layoutFromWindow } from 'mastodon/is_mobile';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
import { isRedesignEnabled } from '@/mastodon/utils/environment';
|
||||
import { checkAnnualReport } from '@/mastodon/reducers/slices/annual_report';
|
||||
|
||||
import { uploadCompose, resetCompose, changeComposeSpoilerness } from '../../actions/compose';
|
||||
@@ -269,9 +270,13 @@ class SwitchingColumnsArea extends PureComponent {
|
||||
</ColumnsContextProvider>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const LazyRedesignComposeButton = lazy(
|
||||
() => import('@/mastodon/features/compose/redesign/trigger')
|
||||
.then(({ ComposeRedesignButton }) => ({ default: ComposeRedesignButton }))
|
||||
);
|
||||
|
||||
class UI extends PureComponent {
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
@@ -665,6 +670,12 @@ class UI extends PureComponent {
|
||||
<LoadingBarContainer className='loading-bar' />
|
||||
<ModalContainer />
|
||||
<UploadArea active={draggingOver} onClose={this.closeUploadModal} />
|
||||
|
||||
{isRedesignEnabled() && (
|
||||
<Suspense>
|
||||
<LazyRedesignComposeButton />
|
||||
</Suspense>
|
||||
)}
|
||||
</div>
|
||||
</Hotkeys>
|
||||
);
|
||||
|
||||
@@ -493,11 +493,16 @@
|
||||
"compose.counter": "{current, number}/{max, number}",
|
||||
"compose.discoverable": "Discoverable in public feeds & search results",
|
||||
"compose.error.blank_post": "Post can't be blank.",
|
||||
"compose.expand": "Show composer",
|
||||
"compose.language.change": "Change language",
|
||||
"compose.language.search": "Search languages...",
|
||||
"compose.message.notice": "Messages are not end-to-end encrypted",
|
||||
"compose.message.placeholder": "Add your recipients and your message.",
|
||||
"compose.message.publish": "Send",
|
||||
"compose.minimize": "Minimize composer",
|
||||
"compose.new": "Write a new post or messsage",
|
||||
"compose.new.message": "Message",
|
||||
"compose.new.post": "Post",
|
||||
"compose.poll.delete": "Delete poll",
|
||||
"compose.poll.duration": "Duration: {button}",
|
||||
"compose.poll.multiple": "Allow multiple selections",
|
||||
|
||||
@@ -116,6 +116,10 @@ function statusToTextMentions(state, status) {
|
||||
return set.union(status.get('mentions').filterNot(mention => mention.get('id') === me).map(mention => `@${mention.get('acct')} `)).join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {typeof initialState} state
|
||||
* @returns {typeof initialState}
|
||||
*/
|
||||
function clearAll(state) {
|
||||
return state.withMutations(map => {
|
||||
map.set('id', null);
|
||||
|
||||
85
app/javascript/mastodon/reducers/slices/composer.ts
Normal file
85
app/javascript/mastodon/reducers/slices/composer.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
import {
|
||||
directCompose,
|
||||
replyComposeById,
|
||||
resetCompose,
|
||||
} from '@/mastodon/actions/compose';
|
||||
import { changeComposeVisibility } from '@/mastodon/actions/compose_typed';
|
||||
import {
|
||||
createAppSelector,
|
||||
createAppThunk,
|
||||
} from '@/mastodon/store/typed_functions';
|
||||
|
||||
type DisplayState = 'hidden' | 'showing' | 'minimized';
|
||||
|
||||
export type ComposeType = 'post' | 'message' | 'reply';
|
||||
|
||||
interface ComposerState {
|
||||
displayState: DisplayState;
|
||||
}
|
||||
|
||||
const initialState: ComposerState = {
|
||||
displayState: 'hidden',
|
||||
};
|
||||
|
||||
const composerSlice = createSlice({
|
||||
name: 'composer',
|
||||
initialState,
|
||||
reducers: {
|
||||
showComposer(state) {
|
||||
state.displayState = 'showing';
|
||||
},
|
||||
minimizeComposerToggle(state) {
|
||||
state.displayState =
|
||||
state.displayState === 'showing' ? 'minimized' : 'showing';
|
||||
},
|
||||
hideComposer(state) {
|
||||
state.displayState = 'hidden';
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const composer = composerSlice.reducer;
|
||||
export const { minimizeComposerToggle } = composerSlice.actions;
|
||||
|
||||
interface ComposeNewPost {
|
||||
type?: 'post';
|
||||
}
|
||||
interface ComposeNewReply {
|
||||
type: 'reply';
|
||||
toStatusId: string;
|
||||
}
|
||||
interface ComposeNewMessage {
|
||||
type: 'message';
|
||||
toAccountId?: string;
|
||||
}
|
||||
type ComposeNewPayload = ComposeNewPost | ComposeNewReply | ComposeNewMessage;
|
||||
|
||||
export const openNewComposer = createAppThunk(
|
||||
(payload: ComposeNewPayload, { dispatch, getState }) => {
|
||||
dispatch(resetCompose());
|
||||
if (payload.type === 'message') {
|
||||
const account =
|
||||
!!payload.toAccountId && getState().accounts.get(payload.toAccountId);
|
||||
if (account) {
|
||||
dispatch(directCompose(account));
|
||||
} else {
|
||||
dispatch(changeComposeVisibility('direct'));
|
||||
}
|
||||
} else if (payload.type === 'reply') {
|
||||
dispatch(replyComposeById(payload.toStatusId));
|
||||
}
|
||||
dispatch(composerSlice.actions.showComposer());
|
||||
},
|
||||
);
|
||||
|
||||
export const hideComposer = createAppThunk((_arg, { dispatch }) => {
|
||||
dispatch(composerSlice.actions.hideComposer());
|
||||
dispatch(resetCompose());
|
||||
});
|
||||
|
||||
export const selectIsMinimized = createAppSelector(
|
||||
[(state) => state.composer.displayState],
|
||||
(displayState) => displayState === 'minimized',
|
||||
);
|
||||
@@ -1,11 +1,13 @@
|
||||
import { annualReport } from './annual_report';
|
||||
import { collections } from './collections';
|
||||
import { composer } from './composer';
|
||||
import { emojis } from './emojis';
|
||||
import { profileEdit } from './profile_edit';
|
||||
|
||||
export const sliceReducers = {
|
||||
annualReport,
|
||||
collections,
|
||||
composer,
|
||||
emojis,
|
||||
profileEdit,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user