Merge branch 'hoverstatus' into 'develop'
Status reply hover See merge request soapbox-pub/soapbox-fe!1550
This commit is contained in:
commit
e5d6b4fd7b
17 changed files with 367 additions and 24 deletions
27
app/soapbox/actions/status-hover-card.ts
Normal file
27
app/soapbox/actions/status-hover-card.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
const STATUS_HOVER_CARD_OPEN = 'STATUS_HOVER_CARD_OPEN';
|
||||
const STATUS_HOVER_CARD_UPDATE = 'STATUS_HOVER_CARD_UPDATE';
|
||||
const STATUS_HOVER_CARD_CLOSE = 'STATUS_HOVER_CARD_CLOSE';
|
||||
|
||||
const openStatusHoverCard = (ref: React.MutableRefObject<HTMLDivElement>, statusId: string) => ({
|
||||
type: STATUS_HOVER_CARD_OPEN,
|
||||
ref,
|
||||
statusId,
|
||||
});
|
||||
|
||||
const updateStatusHoverCard = () => ({
|
||||
type: STATUS_HOVER_CARD_UPDATE,
|
||||
});
|
||||
|
||||
const closeStatusHoverCard = (force = false) => ({
|
||||
type: STATUS_HOVER_CARD_CLOSE,
|
||||
force,
|
||||
});
|
||||
|
||||
export {
|
||||
STATUS_HOVER_CARD_OPEN,
|
||||
STATUS_HOVER_CARD_UPDATE,
|
||||
STATUS_HOVER_CARD_CLOSE,
|
||||
openStatusHoverCard,
|
||||
updateStatusHoverCard,
|
||||
closeStatusHoverCard,
|
||||
};
|
57
app/soapbox/components/hover-status-wrapper.tsx
Normal file
57
app/soapbox/components/hover-status-wrapper.tsx
Normal file
|
@ -0,0 +1,57 @@
|
|||
import classNames from 'classnames';
|
||||
import { debounce } from 'lodash';
|
||||
import React, { useRef } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import {
|
||||
openStatusHoverCard,
|
||||
closeStatusHoverCard,
|
||||
} from 'soapbox/actions/status-hover-card';
|
||||
import { isMobile } from 'soapbox/is_mobile';
|
||||
|
||||
const showStatusHoverCard = debounce((dispatch, ref, statusId) => {
|
||||
dispatch(openStatusHoverCard(ref, statusId));
|
||||
}, 300);
|
||||
|
||||
interface IHoverStatusWrapper {
|
||||
statusId: any,
|
||||
inline: boolean,
|
||||
className?: string,
|
||||
}
|
||||
|
||||
/** Makes a status hover card appear when the wrapped element is hovered. */
|
||||
export const HoverStatusWrapper: React.FC<IHoverStatusWrapper> = ({ statusId, children, inline = false, className }) => {
|
||||
const dispatch = useDispatch();
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const Elem: keyof JSX.IntrinsicElements = inline ? 'span' : 'div';
|
||||
|
||||
const handleMouseEnter = () => {
|
||||
if (!isMobile(window.innerWidth)) {
|
||||
showStatusHoverCard(dispatch, ref, statusId);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
showStatusHoverCard.cancel();
|
||||
setTimeout(() => dispatch(closeStatusHoverCard()), 200);
|
||||
};
|
||||
|
||||
const handleClick = () => {
|
||||
showStatusHoverCard.cancel();
|
||||
dispatch(closeStatusHoverCard(true));
|
||||
};
|
||||
|
||||
return (
|
||||
<Elem
|
||||
ref={ref}
|
||||
className={classNames('hover-status-wrapper', className)}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{children}
|
||||
</Elem>
|
||||
);
|
||||
};
|
||||
|
||||
export { HoverStatusWrapper as default, showStatusHoverCard };
|
102
app/soapbox/components/status-hover-card.tsx
Normal file
102
app/soapbox/components/status-hover-card.tsx
Normal file
|
@ -0,0 +1,102 @@
|
|||
import classNames from 'classnames';
|
||||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import { usePopper } from 'react-popper';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
closeStatusHoverCard,
|
||||
updateStatusHoverCard,
|
||||
} from 'soapbox/actions/status-hover-card';
|
||||
import { fetchStatus } from 'soapbox/actions/statuses';
|
||||
import StatusContainer from 'soapbox/containers/status_container';
|
||||
import { useAppSelector, useAppDispatch } from 'soapbox/hooks';
|
||||
|
||||
import { showStatusHoverCard } from './hover-status-wrapper';
|
||||
import { Card, CardBody } from './ui';
|
||||
|
||||
interface IStatusHoverCard {
|
||||
visible: boolean,
|
||||
}
|
||||
|
||||
/** Popup status preview that appears when hovering reply to */
|
||||
export const StatusHoverCard: React.FC<IStatusHoverCard> = ({ visible = true }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const history = useHistory();
|
||||
|
||||
const [popperElement, setPopperElement] = useState<HTMLElement | null>(null);
|
||||
|
||||
const statusId: string | undefined = useAppSelector(state => state.status_hover_card.statusId || undefined);
|
||||
const status = useAppSelector(state => state.statuses.get(statusId!));
|
||||
const targetRef = useAppSelector(state => state.status_hover_card.ref?.current);
|
||||
|
||||
useEffect(() => {
|
||||
if (statusId && !status) {
|
||||
dispatch(fetchStatus(statusId));
|
||||
}
|
||||
}, [statusId, status]);
|
||||
|
||||
useEffect(() => {
|
||||
const unlisten = history.listen(() => {
|
||||
showStatusHoverCard.cancel();
|
||||
dispatch(closeStatusHoverCard());
|
||||
});
|
||||
|
||||
return () => {
|
||||
unlisten();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const { styles, attributes } = usePopper(targetRef, popperElement, {
|
||||
placement: 'top',
|
||||
});
|
||||
|
||||
const handleMouseEnter = useCallback((): React.MouseEventHandler => {
|
||||
return () => {
|
||||
dispatch(updateStatusHoverCard());
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleMouseLeave = useCallback((): React.MouseEventHandler => {
|
||||
return () => {
|
||||
dispatch(closeStatusHoverCard(true));
|
||||
};
|
||||
}, []);
|
||||
|
||||
if (!statusId) return null;
|
||||
|
||||
const renderStatus = (statusId: string) => {
|
||||
return (
|
||||
// @ts-ignore
|
||||
<StatusContainer
|
||||
key={statusId}
|
||||
id={statusId}
|
||||
hoverable={false}
|
||||
hideActionBar
|
||||
muted
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames({
|
||||
'absolute transition-opacity w-[500px] z-50 top-0 left-0': true,
|
||||
'opacity-100': visible,
|
||||
'opacity-0 pointer-events-none': !visible,
|
||||
})}
|
||||
ref={setPopperElement}
|
||||
style={styles.popper}
|
||||
{...attributes.popper}
|
||||
onMouseEnter={handleMouseEnter()}
|
||||
onMouseLeave={handleMouseLeave()}
|
||||
>
|
||||
<Card className='relative'>
|
||||
<CardBody>
|
||||
{renderStatus(statusId)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StatusHoverCard;
|
|
@ -3,6 +3,7 @@ import { FormattedList, FormattedMessage } from 'react-intl';
|
|||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { openModal } from 'soapbox/actions/modals';
|
||||
import HoverStatusWrapper from 'soapbox/components/hover-status-wrapper';
|
||||
import HoverRefWrapper from 'soapbox/components/hover_ref_wrapper';
|
||||
import { useAppDispatch } from 'soapbox/hooks';
|
||||
|
||||
|
@ -10,9 +11,10 @@ import type { Account, Status } from 'soapbox/types/entities';
|
|||
|
||||
interface IStatusReplyMentions {
|
||||
status: Status,
|
||||
hoverable?: boolean,
|
||||
}
|
||||
|
||||
const StatusReplyMentions: React.FC<IStatusReplyMentions> = ({ status }) => {
|
||||
const StatusReplyMentions: React.FC<IStatusReplyMentions> = ({ status, hoverable = true }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleOpenMentionsModal: React.MouseEventHandler<HTMLSpanElement> = (e) => {
|
||||
|
@ -46,11 +48,21 @@ const StatusReplyMentions: React.FC<IStatusReplyMentions> = ({ status }) => {
|
|||
}
|
||||
|
||||
// The typical case with a reply-to and a list of mentions.
|
||||
const accounts = to.slice(0, 2).map(account => (
|
||||
<HoverRefWrapper key={account.id} accountId={account.id} inline>
|
||||
const accounts = to.slice(0, 2).map(account => {
|
||||
const link = (
|
||||
<Link to={`/@${account.acct}`} className='reply-mentions__account'>@{account.username}</Link>
|
||||
</HoverRefWrapper>
|
||||
)).toArray();
|
||||
);
|
||||
|
||||
if (hoverable) {
|
||||
return (
|
||||
<HoverRefWrapper key={account.id} accountId={account.id} inline>
|
||||
{link}
|
||||
</HoverRefWrapper>
|
||||
);
|
||||
} else {
|
||||
return link;
|
||||
}
|
||||
}).toArray();
|
||||
|
||||
if (to.size > 2) {
|
||||
accounts.push(
|
||||
|
@ -64,9 +76,26 @@ const StatusReplyMentions: React.FC<IStatusReplyMentions> = ({ status }) => {
|
|||
<div className='reply-mentions'>
|
||||
<FormattedMessage
|
||||
id='reply_mentions.reply'
|
||||
defaultMessage='Replying to {accounts}'
|
||||
defaultMessage='<hover>Replying to</hover> {accounts}'
|
||||
values={{
|
||||
accounts: <FormattedList type='conjunction' value={accounts} />,
|
||||
hover: (children: React.ReactNode) => {
|
||||
if (hoverable) {
|
||||
return (
|
||||
<HoverStatusWrapper statusId={status.in_reply_to_id} inline>
|
||||
<span
|
||||
key='hoverstatus'
|
||||
className='hover:underline cursor-pointer'
|
||||
role='presentation'
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
</HoverStatusWrapper>
|
||||
);
|
||||
} else {
|
||||
return children;
|
||||
}
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -93,6 +93,8 @@ interface IStatus extends RouteComponentProps {
|
|||
history: History,
|
||||
featured?: boolean,
|
||||
withDismiss?: boolean,
|
||||
hideActionBar?: boolean,
|
||||
hoverable?: boolean,
|
||||
}
|
||||
|
||||
interface IStatusState {
|
||||
|
@ -105,6 +107,7 @@ class Status extends ImmutablePureComponent<IStatus, IStatusState> {
|
|||
|
||||
static defaultProps = {
|
||||
focusable: true,
|
||||
hoverable: true,
|
||||
};
|
||||
|
||||
didShowCard = false;
|
||||
|
@ -480,6 +483,7 @@ class Status extends ImmutablePureComponent<IStatus, IStatusState> {
|
|||
action={reblogElement}
|
||||
hideActions={!reblogElement}
|
||||
showEdit={!!status.edited_at}
|
||||
showProfileHoverCard={this.props.hoverable}
|
||||
/>
|
||||
</HStack>
|
||||
</div>
|
||||
|
@ -491,7 +495,10 @@ class Status extends ImmutablePureComponent<IStatus, IStatusState> {
|
|||
</div>
|
||||
)}
|
||||
|
||||
<StatusReplyMentions status={this._properStatus()} />
|
||||
<StatusReplyMentions
|
||||
status={this._properStatus()}
|
||||
hoverable={this.props.hoverable}
|
||||
/>
|
||||
|
||||
<StatusContent
|
||||
status={status}
|
||||
|
@ -512,14 +519,16 @@ class Status extends ImmutablePureComponent<IStatus, IStatusState> {
|
|||
{poll}
|
||||
{quote}
|
||||
|
||||
<StatusActionBar
|
||||
status={status}
|
||||
// @ts-ignore what?
|
||||
account={account}
|
||||
emojiSelectorFocused={this.state.emojiSelectorFocused}
|
||||
handleEmojiSelectorUnfocus={this.handleEmojiSelectorUnfocus}
|
||||
{...other}
|
||||
/>
|
||||
{!this.props.hideActionBar && (
|
||||
<StatusActionBar
|
||||
status={status}
|
||||
// @ts-ignore what?
|
||||
account={account}
|
||||
emojiSelectorFocused={this.state.emojiSelectorFocused}
|
||||
handleEmojiSelectorUnfocus={this.handleEmojiSelectorUnfocus}
|
||||
{...other}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -102,6 +102,7 @@ import {
|
|||
SidebarMenu,
|
||||
UploadArea,
|
||||
ProfileHoverCard,
|
||||
StatusHoverCard,
|
||||
Share,
|
||||
NewStatus,
|
||||
IntentionalError,
|
||||
|
@ -698,6 +699,10 @@ const UI: React.FC = ({ children }) => {
|
|||
<BundleContainer fetchComponent={ProfileHoverCard}>
|
||||
{Component => <Component />}
|
||||
</BundleContainer>
|
||||
|
||||
<BundleContainer fetchComponent={StatusHoverCard}>
|
||||
{Component => <Component />}
|
||||
</BundleContainer>
|
||||
</div>
|
||||
</div>
|
||||
</HotKeys>
|
||||
|
|
|
@ -406,6 +406,10 @@ export function ProfileHoverCard() {
|
|||
return import(/* webpackChunkName: "features/ui" */'soapbox/components/profile-hover-card');
|
||||
}
|
||||
|
||||
export function StatusHoverCard() {
|
||||
return import(/* webpackChunkName: "features/ui" */'soapbox/components/status-hover-card');
|
||||
}
|
||||
|
||||
export function CryptoDonate() {
|
||||
return import(/* webpackChunkName: "features/crypto_donate" */'../../crypto_donate');
|
||||
}
|
||||
|
|
|
@ -860,7 +860,7 @@
|
|||
"reply_mentions.account.add": "Add to mentions",
|
||||
"reply_mentions.account.remove": "Remove from mentions",
|
||||
"reply_mentions.more": "{count, plural, one {einen weiteren Nutzer} other {# weitere Nutzer}}",
|
||||
"reply_mentions.reply": "Antwort an {accounts}",
|
||||
"reply_mentions.reply": "<hover>Antwort an</hover> {accounts}",
|
||||
"reply_mentions.reply_empty": "Antwort auf einen Beitrag",
|
||||
"report.block": "{target} blockieren.",
|
||||
"report.block_hint": "Soll dieses Konto zusammen mit der Meldung auch gleich blockiert werden?",
|
||||
|
|
|
@ -1001,7 +1001,7 @@
|
|||
"id": "reply_mentions.reply_empty"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Replying to {accounts}{more}",
|
||||
"defaultMessage": "<hover>Replying to</hover> {accounts}{more}",
|
||||
"id": "reply_mentions.reply"
|
||||
},
|
||||
{
|
||||
|
@ -2470,7 +2470,7 @@
|
|||
"id": "reply_mentions.reply_empty"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Replying to {accounts}{more}",
|
||||
"defaultMessage": "<hover>Replying to</hover> {accounts}{more}",
|
||||
"id": "reply_mentions.reply"
|
||||
},
|
||||
{
|
||||
|
@ -5609,7 +5609,7 @@
|
|||
"id": "reply_indicator.cancel"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Replying to {accounts}{more}",
|
||||
"defaultMessage": "<hover>Replying to</hover> {accounts}{more}",
|
||||
"id": "reply_mentions.reply"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -860,7 +860,7 @@
|
|||
"reply_mentions.account.add": "𐑨𐑛 𐑑 𐑥𐑧𐑯𐑖𐑩𐑯𐑟",
|
||||
"reply_mentions.account.remove": "𐑮𐑦𐑥𐑵𐑝 𐑓𐑮𐑪𐑥 𐑥𐑧𐑯𐑖𐑩𐑯𐑟",
|
||||
"reply_mentions.more": "{count} 𐑥𐑹",
|
||||
"reply_mentions.reply": "𐑮𐑦𐑐𐑤𐑲𐑦𐑙 𐑑 {accounts}",
|
||||
"reply_mentions.reply": "<hover>𐑮𐑦𐑐𐑤𐑲𐑦𐑙 𐑑</hover> {accounts}",
|
||||
"reply_mentions.reply_empty": "𐑮𐑦𐑐𐑤𐑲𐑦𐑙 𐑑 𐑐𐑴𐑕𐑑",
|
||||
"report.block": "𐑚𐑤𐑪𐑒 {target}",
|
||||
"report.block_hint": "𐑛𐑵 𐑿 𐑷𐑤𐑕𐑴 𐑢𐑪𐑯𐑑 𐑑 𐑚𐑤𐑪𐑒 𐑞𐑦𐑕 𐑩𐑒𐑬𐑯𐑑?",
|
||||
|
|
|
@ -860,7 +860,7 @@
|
|||
"reply_mentions.account.add": "הוסף לאזכורים",
|
||||
"reply_mentions.account.remove": "הסר מהאזכורים",
|
||||
"reply_mentions.more": "{count} עוד",
|
||||
"reply_mentions.reply": "משיב ל-{accounts}",
|
||||
"reply_mentions.reply": "<hover>משיב ל-</hover>{accounts}",
|
||||
"reply_mentions.reply_empty": "משיב לפוסט",
|
||||
"report.block": "חסום {target}",
|
||||
"report.block_hint": "האם גם אתה רוצה לחסום את החשבון הזה?",
|
||||
|
|
|
@ -789,7 +789,7 @@
|
|||
"reply_mentions.account.add": "Bæta við í tilvísanirnar",
|
||||
"reply_mentions.account.remove": "Fjarlægja úr tilvísunum",
|
||||
"reply_mentions.more": "{count} fleirum",
|
||||
"reply_mentions.reply": "Að svara {accounts}",
|
||||
"reply_mentions.reply": "<hover>Að svara</hover> {accounts}",
|
||||
"reply_mentions.reply_empty": "Að svara færslu",
|
||||
"report.block": "Loka á {target}",
|
||||
"report.block_hint": "Viltu líka loka á þennan reikning?",
|
||||
|
|
|
@ -860,7 +860,7 @@
|
|||
"reply_mentions.account.add": "Add to mentions",
|
||||
"reply_mentions.account.remove": "Remove from mentions",
|
||||
"reply_mentions.more": "ancora {count}",
|
||||
"reply_mentions.reply": "Risponde a {accounts}",
|
||||
"reply_mentions.reply": "<hover>Risponde a</hover> {accounts}",
|
||||
"reply_mentions.reply_empty": "Rispondendo al contenuto",
|
||||
"report.block": "Blocca {target}",
|
||||
"report.block_hint": "Vuoi anche bloccare questa persona?",
|
||||
|
|
|
@ -913,7 +913,7 @@
|
|||
"reply_mentions.account.add": "Dodaj do wspomnianych",
|
||||
"reply_mentions.account.remove": "Usuń z wspomnianych",
|
||||
"reply_mentions.more": "{count} więcej",
|
||||
"reply_mentions.reply": "W odpowiedzi do {accounts}",
|
||||
"reply_mentions.reply": "<hover>W odpowiedzi do</hover> {accounts}",
|
||||
"reply_mentions.reply_empty": "W odpowiedzi na wpis",
|
||||
"report.block": "Zablokuj {target}",
|
||||
"report.block_hint": "Czy chcesz też zablokować to konto?",
|
||||
|
|
72
app/soapbox/reducers/__tests__/status-hover-card.test.tsx
Normal file
72
app/soapbox/reducers/__tests__/status-hover-card.test.tsx
Normal file
|
@ -0,0 +1,72 @@
|
|||
import {
|
||||
STATUS_HOVER_CARD_OPEN,
|
||||
STATUS_HOVER_CARD_CLOSE,
|
||||
STATUS_HOVER_CARD_UPDATE,
|
||||
} from 'soapbox/actions/status-hover-card';
|
||||
|
||||
import reducer, { ReducerRecord } from '../status-hover-card';
|
||||
|
||||
describe(STATUS_HOVER_CARD_OPEN, () => {
|
||||
it('sets the ref and statusId', () => {
|
||||
const ref = { current: document.createElement('div') };
|
||||
|
||||
const action = {
|
||||
type: STATUS_HOVER_CARD_OPEN,
|
||||
ref,
|
||||
statusId: '1234',
|
||||
};
|
||||
|
||||
const result = reducer(undefined, action);
|
||||
expect(result.ref).toBe(ref);
|
||||
expect(result.statusId).toBe('1234');
|
||||
});
|
||||
});
|
||||
|
||||
describe(STATUS_HOVER_CARD_CLOSE, () => {
|
||||
it('flushes the state', () => {
|
||||
const state = ReducerRecord({
|
||||
ref: { current: document.createElement('div') },
|
||||
statusId: '1234',
|
||||
});
|
||||
|
||||
const action = { type: STATUS_HOVER_CARD_CLOSE };
|
||||
|
||||
const result = reducer(state, action);
|
||||
expect(result.ref).toBe(null);
|
||||
expect(result.statusId).toBe('');
|
||||
});
|
||||
|
||||
it('leaves the state alone if hovered', () => {
|
||||
const state = ReducerRecord({
|
||||
ref: { current: document.createElement('div') },
|
||||
statusId: '1234',
|
||||
hovered: true,
|
||||
});
|
||||
|
||||
const action = { type: STATUS_HOVER_CARD_CLOSE };
|
||||
const result = reducer(state, action);
|
||||
expect(result).toEqual(state);
|
||||
});
|
||||
|
||||
it('action.force flushes the state even if hovered', () => {
|
||||
const state = ReducerRecord({
|
||||
ref: { current: document.createElement('div') },
|
||||
statusId: '1234',
|
||||
hovered: true,
|
||||
});
|
||||
|
||||
const action = { type: STATUS_HOVER_CARD_CLOSE, force: true };
|
||||
const result = reducer(state, action);
|
||||
expect(result.ref).toBe(null);
|
||||
expect(result.statusId).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe(STATUS_HOVER_CARD_UPDATE, () => {
|
||||
it('sets hovered', () => {
|
||||
const state = ReducerRecord();
|
||||
const action = { type: STATUS_HOVER_CARD_UPDATE };
|
||||
const result = reducer(state, action);
|
||||
expect(result.hovered).toBe(true);
|
||||
});
|
||||
});
|
|
@ -53,6 +53,7 @@ import security from './security';
|
|||
import settings from './settings';
|
||||
import sidebar from './sidebar';
|
||||
import soapbox from './soapbox';
|
||||
import status_hover_card from './status-hover-card';
|
||||
import status_lists from './status_lists';
|
||||
import statuses from './statuses';
|
||||
import suggestions from './suggestions';
|
||||
|
@ -108,6 +109,7 @@ const reducers = {
|
|||
chat_messages,
|
||||
chat_message_lists,
|
||||
profile_hover_card,
|
||||
status_hover_card,
|
||||
backups,
|
||||
admin_log,
|
||||
security,
|
||||
|
|
36
app/soapbox/reducers/status-hover-card.ts
Normal file
36
app/soapbox/reducers/status-hover-card.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
import { Record as ImmutableRecord } from 'immutable';
|
||||
|
||||
import {
|
||||
STATUS_HOVER_CARD_OPEN,
|
||||
STATUS_HOVER_CARD_CLOSE,
|
||||
STATUS_HOVER_CARD_UPDATE,
|
||||
} from 'soapbox/actions/status-hover-card';
|
||||
|
||||
import type { AnyAction } from 'redux';
|
||||
|
||||
export const ReducerRecord = ImmutableRecord({
|
||||
ref: null as React.MutableRefObject<HTMLDivElement> | null,
|
||||
statusId: '',
|
||||
hovered: false,
|
||||
});
|
||||
|
||||
type State = ReturnType<typeof ReducerRecord>;
|
||||
|
||||
export default function statusHoverCard(state: State = ReducerRecord(), action: AnyAction) {
|
||||
switch (action.type) {
|
||||
case STATUS_HOVER_CARD_OPEN:
|
||||
return state.withMutations((state) => {
|
||||
state.set('ref', action.ref);
|
||||
state.set('statusId', action.statusId);
|
||||
});
|
||||
case STATUS_HOVER_CARD_UPDATE:
|
||||
return state.set('hovered', true);
|
||||
case STATUS_HOVER_CARD_CLOSE:
|
||||
if (state.hovered === true && !action.force)
|
||||
return state;
|
||||
else
|
||||
return ReducerRecord();
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue