Improve compose page styles, propagate compose toast to other tabs

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak 2023-07-26 00:53:22 +02:00
parent b820781eff
commit 4c9201e8b4
6 changed files with 35 additions and 20 deletions

View file

@ -274,15 +274,18 @@ const directComposeById = (accountId: string) =>
dispatch(openModal('COMPOSE')); dispatch(openModal('COMPOSE'));
}; };
const handleComposeSubmit = (dispatch: AppDispatch, getState: () => RootState, composeId: string, data: APIEntity, status: string, edit?: boolean) => { const handleComposeSubmit = (dispatch: AppDispatch, getState: () => RootState, composeId: string, data: APIEntity, status: string, edit?: boolean, propagate?: boolean) => {
if (!dispatch || !getState) return; if (!dispatch || !getState) return;
dispatch(insertIntoTagHistory(composeId, data.tags || [], status)); dispatch(insertIntoTagHistory(composeId, data.tags || [], status));
dispatch(submitComposeSuccess(composeId, { ...data })); dispatch(submitComposeSuccess(composeId, { ...data }));
toast.success(edit ? messages.editSuccess : messages.success, { const toastMessage = edit ? messages.editSuccess : messages.success;
const toastOpts = {
actionLabel: messages.view, actionLabel: messages.view,
actionLink: `/@${data.account.acct}/posts/${data.id}`, actionLink: `/@${data.account.acct}/posts/${data.id}`,
}); };
if (propagate) toast.propagate('success', toastMessage, toastOpts);
else toast.success(toastMessage, toastOpts);
}; };
const needsDescriptions = (state: RootState, composeId: string) => { const needsDescriptions = (state: RootState, composeId: string) => {
@ -303,7 +306,7 @@ const validateSchedule = (state: RootState, composeId: string) => {
return schedule.getTime() > fiveMinutesFromNow.getTime(); return schedule.getTime() > fiveMinutesFromNow.getTime();
}; };
const submitCompose = (composeId: string, routerHistory?: History, force = false, onSubmit?: () => void) => const submitCompose = (composeId: string, routerHistory?: History, force = false, onSubmit?: () => void, propagate?: boolean) =>
(dispatch: AppDispatch, getState: () => RootState) => { (dispatch: AppDispatch, getState: () => RootState) => {
if (!isLoggedIn(getState)) return; if (!isLoggedIn(getState)) return;
const state = getState(); const state = getState();
@ -368,7 +371,7 @@ const submitCompose = (composeId: string, routerHistory?: History, force = false
if (!statusId && data.visibility === 'direct' && getState().conversations.mounted <= 0 && routerHistory) { if (!statusId && data.visibility === 'direct' && getState().conversations.mounted <= 0 && routerHistory) {
routerHistory.push('/messages'); routerHistory.push('/messages');
} }
handleComposeSubmit(dispatch, getState, composeId, data, status, !!statusId); handleComposeSubmit(dispatch, getState, composeId, data, status, !!statusId, propagate);
if (onSubmit) onSubmit(); if (onSubmit) onSubmit();
}).catch(function(error) { }).catch(function(error) {
dispatch(submitComposeFail(composeId, error)); dispatch(submitComposeFail(composeId, error));

View file

@ -30,6 +30,7 @@ interface IAutosuggesteTextarea {
onFocus: () => void onFocus: () => void
onBlur?: () => void onBlur?: () => void
condensed?: boolean condensed?: boolean
fullScreen?: boolean
children: React.ReactNode children: React.ReactNode
} }
@ -227,7 +228,7 @@ class AutosuggestTextarea extends ImmutablePureComponent<IAutosuggesteTextarea>
} }
render() { render() {
const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, children, condensed, id } = this.props; const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, children, condensed, id, fullScreen } = this.props;
const { suggestionsHidden } = this.state; const { suggestionsHidden } = this.state;
const style = { direction: 'ltr', minRows: 10 }; const style = { direction: 'ltr', minRows: 10 };
@ -237,7 +238,7 @@ class AutosuggestTextarea extends ImmutablePureComponent<IAutosuggesteTextarea>
} }
return [ return [
<div key='textarea'> <div key='textarea' className={clsx(fullScreen && 'grow')}>
<div className='relative'> <div className='relative'>
<label> <label>
<span style={{ display: 'none' }}>{placeholder}</span> <span style={{ display: 'none' }}>{placeholder}</span>
@ -247,6 +248,7 @@ class AutosuggestTextarea extends ImmutablePureComponent<IAutosuggesteTextarea>
className={clsx('w-full resize-none border-0 px-0 text-gray-800 transition-[min-height] placeholder:text-gray-600 focus:border-0 focus:shadow-none focus:ring-0 motion-reduce:transition-none dark:bg-transparent dark:text-white dark:placeholder:text-gray-600', { className={clsx('w-full resize-none border-0 px-0 text-gray-800 transition-[min-height] placeholder:text-gray-600 focus:border-0 focus:shadow-none focus:ring-0 motion-reduce:transition-none dark:bg-transparent dark:text-white dark:placeholder:text-gray-600', {
'min-h-[40px]': condensed, 'min-h-[40px]': condensed,
'min-h-[100px]': !condensed, 'min-h-[100px]': !condensed,
'h-full': fullScreen,
})} })}
id={id} id={id}
disabled={disabled} disabled={disabled}

View file

@ -195,11 +195,7 @@ const SoapboxMount = () => {
<GdprBanner /> <GdprBanner />
<div id='toaster'> <div id='toaster'>
<Toaster <Toaster position='top-right' />
position='top-right'
containerClassName='top-10'
containerStyle={{ top: 75 }}
/>
</div> </div>
</Route> </Route>
</Switch> </Switch>

View file

@ -66,9 +66,10 @@ interface IComposeForm<ID extends string> {
group?: string group?: string
extra?: React.ReactNode extra?: React.ReactNode
onSubmit?: () => void onSubmit?: () => void
fullScreen?: boolean
} }
const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickableAreaRef, event, group, extra, onSubmit }: IComposeForm<ID>) => { const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickableAreaRef, event, group, extra, onSubmit, fullScreen }: IComposeForm<ID>) => {
const history = useHistory(); const history = useHistory();
const intl = useIntl(); const intl = useIntl();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
@ -158,7 +159,7 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
return; return;
} }
dispatch(submitCompose(id, history, false, onSubmit)); dispatch(submitCompose(id, history, false, onSubmit, fullScreen));
}; };
const onSuggestionsClearRequested = () => { const onSuggestionsClearRequested = () => {
@ -275,7 +276,7 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
} }
return ( return (
<Stack className='w-full' space={4} ref={formRef} onClick={handleClick} element='form' onSubmit={handleSubmit}> <Stack className='w-full' grow={fullScreen} space={4} ref={formRef} onClick={handleClick} element='form' onSubmit={handleSubmit}>
{scheduledStatusCount > 0 && !event && !group && ( {scheduledStatusCount > 0 && !event && !group && (
<Warning <Warning
message={( message={(
@ -319,6 +320,7 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
autoFocus={shouldAutoFocus} autoFocus={shouldAutoFocus}
condensed={condensed} condensed={condensed}
id='compose-textarea' id='compose-textarea'
fullScreen
> >
{ {
!condensed && !condensed &&

View file

@ -8,7 +8,7 @@ import { fetchStatus } from 'soapbox/actions/statuses';
import { Stack } from 'soapbox/components/ui'; import { Stack } from 'soapbox/components/ui';
import ComposeForm from 'soapbox/features/compose/components/compose-form'; import ComposeForm from 'soapbox/features/compose/components/compose-form';
import { useAppDispatch, useCompose } from 'soapbox/hooks'; import { useAppDispatch, useCompose } from 'soapbox/hooks';
import { makeGetStatus } from 'soapbox/selectors'; import { makeGetStatus, selectOwnAccount } from 'soapbox/selectors';
import { getFeatures } from 'soapbox/utils/features'; import { getFeatures } from 'soapbox/utils/features';
const getStatus = makeGetStatus(); const getStatus = makeGetStatus();
@ -56,7 +56,7 @@ const ComposePage = () => {
type: COMPOSE_REPLY, type: COMPOSE_REPLY,
id: 'compose-modal', id: 'compose-modal',
status: status, status: status,
account: state.accounts.get(state.me)!, account: selectOwnAccount(state),
explicitAddressing, explicitAddressing,
}); });
})); }));
@ -72,7 +72,7 @@ const ComposePage = () => {
type: COMPOSE_QUOTE, type: COMPOSE_QUOTE,
id: 'compose-modal', id: 'compose-modal',
status: status, status: status,
account: state.accounts.get(state.me)!, account: selectOwnAccount(state),
explicitAddressing, explicitAddressing,
}); });
})); }));
@ -83,7 +83,7 @@ const ComposePage = () => {
<h3 className='grow-0 truncate text-lg font-bold leading-6 text-gray-900 dark:text-white'> <h3 className='grow-0 truncate text-lg font-bold leading-6 text-gray-900 dark:text-white'>
{heading} {heading}
</h3> </h3>
<ComposeForm id='compose-modal' onSubmit={() => window.close()} /> <ComposeForm id='compose-modal' onSubmit={() => window.close()} fullScreen />
</Stack> </Stack>
); );
}; };

View file

@ -17,6 +17,13 @@ interface IToastOptions {
summary?: string summary?: string
} }
let bc: BroadcastChannel;
if (BroadcastChannel) {
bc = new BroadcastChannel('toast');
bc.onmessage = ({ data }) => createToast(data.type, data.message, data.opts);
(window as any).bc = bc;
}
const DEFAULT_DURATION = 4000; const DEFAULT_DURATION = 4000;
const createToast = (type: ToastType, message: ToastText, opts?: IToastOptions) => { const createToast = (type: ToastType, message: ToastText, opts?: IToastOptions) => {
@ -39,6 +46,10 @@ function error(message: ToastText, opts?: IToastOptions) {
createToast('error', message, opts); createToast('error', message, opts);
} }
const propagate = (type: ToastType, message: ToastText, opts?: IToastOptions) => {
bc?.postMessage({ type, message, opts });
};
const messages = defineMessages({ const messages = defineMessages({
unexpectedMessage: { id: 'alert.unexpected.message', defaultMessage: 'An unexpected error occurred.' }, unexpectedMessage: { id: 'alert.unexpected.message', defaultMessage: 'An unexpected error occurred.' },
}); });
@ -79,5 +90,6 @@ export default {
info, info,
success, success,
error, error,
propagate,
showAlertForError, showAlertForError,
}; };