[Glitch] Add new "quick boosting" setting
Port 6dad80eb8c9030af59c57f0369b078efee3e3a82 to glitch-soc Signed-off-by: Claire <claire.github-309c@sitedethib.com>
This commit is contained in:
parent
22e3c0e745
commit
460222e8e1
@ -1,5 +1,5 @@
|
|||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
import type { FC, KeyboardEvent, MouseEvent } from 'react';
|
import type { FC, KeyboardEvent, MouseEvent, MouseEventHandler } from 'react';
|
||||||
|
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
|
||||||
@ -8,6 +8,7 @@ import classNames from 'classnames';
|
|||||||
import { quoteComposeById } from '@/flavours/glitch/actions/compose_typed';
|
import { quoteComposeById } from '@/flavours/glitch/actions/compose_typed';
|
||||||
import { toggleReblog } from '@/flavours/glitch/actions/interactions';
|
import { toggleReblog } from '@/flavours/glitch/actions/interactions';
|
||||||
import { openModal } from '@/flavours/glitch/actions/modal';
|
import { openModal } from '@/flavours/glitch/actions/modal';
|
||||||
|
import { quickBoosting } from '@/flavours/glitch/initial_state';
|
||||||
import type { ActionMenuItem } from '@/flavours/glitch/models/dropdown_menu';
|
import type { ActionMenuItem } from '@/flavours/glitch/models/dropdown_menu';
|
||||||
import type { Status } from '@/flavours/glitch/models/status';
|
import type { Status } from '@/flavours/glitch/models/status';
|
||||||
import { useAppDispatch, useAppSelector } from '@/flavours/glitch/store';
|
import { useAppDispatch, useAppSelector } from '@/flavours/glitch/store';
|
||||||
@ -24,6 +25,55 @@ import {
|
|||||||
selectStatusState,
|
selectStatusState,
|
||||||
} from './boost_button_utils';
|
} from './boost_button_utils';
|
||||||
|
|
||||||
|
const StandaloneBoostButton: FC<ReblogButtonProps> = ({ status, counters }) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const statusState = useAppSelector((state) =>
|
||||||
|
selectStatusState(state, status),
|
||||||
|
);
|
||||||
|
const { title, meta, iconComponent, disabled } = useMemo(
|
||||||
|
() => boostItemState(statusState),
|
||||||
|
[statusState],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleClick: MouseEventHandler = useCallback(
|
||||||
|
(event) => {
|
||||||
|
if (statusState.isLoggedIn) {
|
||||||
|
dispatch(toggleReblog(status.get('id') as string, event.shiftKey));
|
||||||
|
} else {
|
||||||
|
dispatch(
|
||||||
|
openModal({
|
||||||
|
modalType: 'INTERACTION',
|
||||||
|
modalProps: {
|
||||||
|
accountId: status.getIn(['account', 'id']),
|
||||||
|
url: status.get('uri'),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[dispatch, status, statusState.isLoggedIn],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IconButton
|
||||||
|
disabled={disabled}
|
||||||
|
active={!!status.get('reblogged')}
|
||||||
|
title={intl.formatMessage(meta ?? title)}
|
||||||
|
icon='retweet'
|
||||||
|
iconComponent={iconComponent}
|
||||||
|
onClick={!disabled ? handleClick : undefined}
|
||||||
|
counter={
|
||||||
|
counters
|
||||||
|
? (status.get('reblogs_count') as number) +
|
||||||
|
(status.get('quotes_count') as number)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const renderMenuItem: RenderItemFn<ActionMenuItem> = (
|
const renderMenuItem: RenderItemFn<ActionMenuItem> = (
|
||||||
item,
|
item,
|
||||||
index,
|
index,
|
||||||
@ -46,7 +96,7 @@ interface ReblogButtonProps {
|
|||||||
|
|
||||||
type ActionMenuItemWithIcon = SomeRequired<ActionMenuItem, 'icon'>;
|
type ActionMenuItemWithIcon = SomeRequired<ActionMenuItem, 'icon'>;
|
||||||
|
|
||||||
export const BoostButton: FC<ReblogButtonProps> = ({ status, counters }) => {
|
const BoostOrQuoteMenu: FC<ReblogButtonProps> = ({ status, counters }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const statusState = useAppSelector((state) =>
|
const statusState = useAppSelector((state) =>
|
||||||
@ -188,3 +238,9 @@ const ReblogMenuItem: FC<ReblogMenuItemProps> = ({
|
|||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Switch between the standalone boost button or the
|
||||||
|
// "Boost or quote" menu based on the quickBoosting preference
|
||||||
|
export const BoostButton = quickBoosting
|
||||||
|
? StandaloneBoostButton
|
||||||
|
: BoostOrQuoteMenu;
|
||||||
|
|||||||
@ -22,12 +22,13 @@ import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend
|
|||||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||||
|
|
||||||
import { Dropdown } from 'flavours/glitch/components/dropdown_menu';
|
import { Dropdown } from 'flavours/glitch/components/dropdown_menu';
|
||||||
import { me } from '../../initial_state';
|
import { me, quickBoosting } from '../../initial_state';
|
||||||
|
|
||||||
import { IconButton } from '../icon_button';
|
import { IconButton } from '../icon_button';
|
||||||
import { RelativeTimestamp } from '../relative_timestamp';
|
import { RelativeTimestamp } from '../relative_timestamp';
|
||||||
import { BoostButton } from '../status/boost_button';
|
import { BoostButton } from '../status/boost_button';
|
||||||
import { RemoveQuoteHint } from './remove_quote_hint';
|
import { RemoveQuoteHint } from './remove_quote_hint';
|
||||||
|
import { quoteItemState, selectStatusState } from '../status/boost_button_utils';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
delete: { id: 'status.delete', defaultMessage: 'Delete' },
|
delete: { id: 'status.delete', defaultMessage: 'Delete' },
|
||||||
@ -68,6 +69,7 @@ const mapStateToProps = (state, { status }) => {
|
|||||||
const quotedStatusId = status.getIn(['quote', 'quoted_status']);
|
const quotedStatusId = status.getIn(['quote', 'quoted_status']);
|
||||||
return ({
|
return ({
|
||||||
quotedAccountId: quotedStatusId ? state.getIn(['statuses', quotedStatusId, 'account']) : null,
|
quotedAccountId: quotedStatusId ? state.getIn(['statuses', quotedStatusId, 'account']) : null,
|
||||||
|
statusQuoteState: selectStatusState(state, status),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -75,6 +77,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||||||
static propTypes = {
|
static propTypes = {
|
||||||
identity: identityContextPropShape,
|
identity: identityContextPropShape,
|
||||||
status: ImmutablePropTypes.map.isRequired,
|
status: ImmutablePropTypes.map.isRequired,
|
||||||
|
statusQuoteState: PropTypes.object,
|
||||||
quotedAccountId: PropTypes.string,
|
quotedAccountId: PropTypes.string,
|
||||||
contextType: PropTypes.string,
|
contextType: PropTypes.string,
|
||||||
onReply: PropTypes.func,
|
onReply: PropTypes.func,
|
||||||
@ -122,6 +125,10 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleQuoteClick = () => {
|
||||||
|
this.props.onQuote(this.props.status);
|
||||||
|
};
|
||||||
|
|
||||||
handleShareClick = () => {
|
handleShareClick = () => {
|
||||||
navigator.share({
|
navigator.share({
|
||||||
url: this.props.status.get('url'),
|
url: this.props.status.get('url'),
|
||||||
@ -214,7 +221,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { status, quotedAccountId, contextType, intl, withDismiss, withCounters, showReplyCount, scrollKey } = this.props;
|
const { status, statusQuoteState, quotedAccountId, contextType, intl, withDismiss, withCounters, showReplyCount, scrollKey } = this.props;
|
||||||
const { signedIn, permissions } = this.props.identity;
|
const { signedIn, permissions } = this.props.identity;
|
||||||
|
|
||||||
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
|
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
|
||||||
@ -239,6 +246,20 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||||||
menu.push({ text: intl.formatMessage(messages.share), action: this.handleShareClick });
|
menu.push({ text: intl.formatMessage(messages.share), action: this.handleShareClick });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (quickBoosting && signedIn) {
|
||||||
|
const quoteItem = quoteItemState(statusQuoteState);
|
||||||
|
menu.push(null);
|
||||||
|
menu.push({
|
||||||
|
text: intl.formatMessage(quoteItem.title),
|
||||||
|
description: quoteItem.meta
|
||||||
|
? intl.formatMessage(quoteItem.meta)
|
||||||
|
: undefined,
|
||||||
|
disabled: quoteItem.disabled,
|
||||||
|
action: this.handleQuoteClick,
|
||||||
|
});
|
||||||
|
menu.push(null);
|
||||||
|
}
|
||||||
|
|
||||||
if (publicStatus && !isRemote) {
|
if (publicStatus && !isRemote) {
|
||||||
menu.push({ text: intl.formatMessage(messages.embed), action: this.handleEmbed });
|
menu.push({ text: intl.formatMessage(messages.embed), action: this.handleEmbed });
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,8 +19,9 @@ import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend
|
|||||||
|
|
||||||
import { IconButton } from '../../../components/icon_button';
|
import { IconButton } from '../../../components/icon_button';
|
||||||
import { Dropdown } from 'flavours/glitch/components/dropdown_menu';
|
import { Dropdown } from 'flavours/glitch/components/dropdown_menu';
|
||||||
import { me } from '../../../initial_state';
|
import { me, quickBoosting } from '../../../initial_state';
|
||||||
import { BoostButton } from '@/flavours/glitch/components/status/boost_button';
|
import { BoostButton } from '@/flavours/glitch/components/status/boost_button';
|
||||||
|
import { quoteItemState, selectStatusState } from '@/flavours/glitch/components/status/boost_button_utils';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
delete: { id: 'status.delete', defaultMessage: 'Delete' },
|
delete: { id: 'status.delete', defaultMessage: 'Delete' },
|
||||||
@ -56,6 +57,7 @@ const mapStateToProps = (state, { status }) => {
|
|||||||
const quotedStatusId = status.getIn(['quote', 'quoted_status']);
|
const quotedStatusId = status.getIn(['quote', 'quoted_status']);
|
||||||
return ({
|
return ({
|
||||||
quotedAccountId: quotedStatusId ? state.getIn(['statuses', quotedStatusId, 'account']) : null,
|
quotedAccountId: quotedStatusId ? state.getIn(['statuses', quotedStatusId, 'account']) : null,
|
||||||
|
statusQuoteState: selectStatusState(state, status),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -63,6 +65,7 @@ class ActionBar extends PureComponent {
|
|||||||
static propTypes = {
|
static propTypes = {
|
||||||
identity: identityContextPropShape,
|
identity: identityContextPropShape,
|
||||||
status: ImmutablePropTypes.map.isRequired,
|
status: ImmutablePropTypes.map.isRequired,
|
||||||
|
statusQuoteState: PropTypes.object,
|
||||||
quotedAccountId: ImmutablePropTypes.string,
|
quotedAccountId: ImmutablePropTypes.string,
|
||||||
onReply: PropTypes.func.isRequired,
|
onReply: PropTypes.func.isRequired,
|
||||||
onReblog: PropTypes.func.isRequired,
|
onReblog: PropTypes.func.isRequired,
|
||||||
@ -107,6 +110,10 @@ class ActionBar extends PureComponent {
|
|||||||
this.props.onRevokeQuote(this.props.status);
|
this.props.onRevokeQuote(this.props.status);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleQuoteClick = () => {
|
||||||
|
this.props.onQuote(this.props.status);
|
||||||
|
};
|
||||||
|
|
||||||
handleQuotePolicyChange = () => {
|
handleQuotePolicyChange = () => {
|
||||||
this.props.onQuotePolicyChange(this.props.status);
|
this.props.onQuotePolicyChange(this.props.status);
|
||||||
};
|
};
|
||||||
@ -163,7 +170,7 @@ class ActionBar extends PureComponent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { status, quotedAccountId, intl } = this.props;
|
const { status, statusQuoteState, quotedAccountId, intl } = this.props;
|
||||||
const { signedIn, permissions } = this.props.identity;
|
const { signedIn, permissions } = this.props.identity;
|
||||||
|
|
||||||
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
|
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
|
||||||
@ -184,6 +191,20 @@ class ActionBar extends PureComponent {
|
|||||||
menu.push({ text: intl.formatMessage(messages.share), action: this.handleShare });
|
menu.push({ text: intl.formatMessage(messages.share), action: this.handleShare });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (quickBoosting && signedIn) {
|
||||||
|
const quoteItem = quoteItemState(statusQuoteState);
|
||||||
|
menu.push(null);
|
||||||
|
menu.push({
|
||||||
|
text: intl.formatMessage(quoteItem.title),
|
||||||
|
description: quoteItem.meta
|
||||||
|
? intl.formatMessage(quoteItem.meta)
|
||||||
|
: undefined,
|
||||||
|
disabled: quoteItem.disabled,
|
||||||
|
action: this.handleQuoteClick,
|
||||||
|
});
|
||||||
|
menu.push(null);
|
||||||
|
}
|
||||||
|
|
||||||
if (publicStatus && (signedIn || !isRemote)) {
|
if (publicStatus && (signedIn || !isRemote)) {
|
||||||
menu.push({ text: intl.formatMessage(messages.embed), action: this.handleEmbed });
|
menu.push({ text: intl.formatMessage(messages.embed), action: this.handleEmbed });
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@ interface InitialStateMeta {
|
|||||||
activity_api_enabled: boolean;
|
activity_api_enabled: boolean;
|
||||||
admin: string;
|
admin: string;
|
||||||
boost_modal?: boolean;
|
boost_modal?: boolean;
|
||||||
|
quick_boosting?: boolean;
|
||||||
favourite_modal?: boolean;
|
favourite_modal?: boolean;
|
||||||
crop_images: boolean;
|
crop_images: boolean;
|
||||||
delete_modal?: boolean;
|
delete_modal?: boolean;
|
||||||
@ -117,6 +118,7 @@ function getMeta<K extends keyof InitialStateMeta>(
|
|||||||
export const activityApiEnabled = getMeta('activity_api_enabled');
|
export const activityApiEnabled = getMeta('activity_api_enabled');
|
||||||
export const autoPlayGif = getMeta('auto_play_gif');
|
export const autoPlayGif = getMeta('auto_play_gif');
|
||||||
export const boostModal = getMeta('boost_modal');
|
export const boostModal = getMeta('boost_modal');
|
||||||
|
export const quickBoosting = getMeta('quick_boosting');
|
||||||
export const deleteModal = getMeta('delete_modal');
|
export const deleteModal = getMeta('delete_modal');
|
||||||
export const missingAltTextModal = getMeta('missing_alt_text_modal');
|
export const missingAltTextModal = getMeta('missing_alt_text_modal');
|
||||||
export const disableSwiping = getMeta('disable_swiping');
|
export const disableSwiping = getMeta('disable_swiping');
|
||||||
|
|||||||
@ -225,6 +225,10 @@ code {
|
|||||||
list-style: disc;
|
list-style: disc;
|
||||||
margin-inline-start: 18px;
|
margin-inline-start: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
vertical-align: -3px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ul.hint {
|
ul.hint {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user