From 1ace723c3e9e7843682f45a5aff415705890348c Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 9 Sep 2020 19:21:27 -0500 Subject: [PATCH 01/14] Hovercard: add react-popper --- package.json | 2 ++ yarn.lock | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/package.json b/package.json index e79c4356c..1c160f1f3 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "@babel/preset-react": "^7.0.0", "@babel/runtime": "^7.3.4", "@clusterws/cws": "^0.16.0", + "@popperjs/core": "^2.4.4", "array-includes": "^3.0.3", "autoprefixer": "^9.5.1", "axios": "^0.19.0", @@ -114,6 +115,7 @@ "react-motion": "^0.5.2", "react-notification": "^6.8.4", "react-overlays": "^0.8.3", + "react-popper": "^2.2.3", "react-redux": "^6.0.1", "react-redux-loading-bar": "^4.5.0", "react-router-dom": "^4.1.1", diff --git a/yarn.lock b/yarn.lock index 561257434..9c95574c0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1482,6 +1482,11 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" +"@popperjs/core@^2.4.4": + version "2.4.4" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.4.4.tgz#11d5db19bd178936ec89cd84519c4de439574398" + integrity sha512-1oO6+dN5kdIA3sKPZhRGJTfGVP4SWV6KqlMOwry4J3HfyD68sl/3KmG7DeYUzvN+RbhXDnv/D8vNNB8168tAMg== + "@sinonjs/commons@^1.7.0": version "1.8.0" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.0.tgz#c8d68821a854c555bba172f3b06959a0039b236d" @@ -9488,6 +9493,11 @@ react-fast-compare@^2.0.4: resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9" integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw== +react-fast-compare@^3.0.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" + integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA== + react-helmet@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-6.0.0.tgz#fcb93ebaca3ba562a686eb2f1f9d46093d83b5f8" @@ -9615,6 +9625,14 @@ react-overlays@^0.8.3: react-transition-group "^2.2.0" warning "^3.0.0" +react-popper@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-2.2.3.tgz#33d425fa6975d4bd54d9acd64897a89d904b9d97" + integrity sha512-mOEiMNT1249js0jJvkrOjyHsGvqcJd3aGW/agkiMoZk3bZ1fXN1wQszIQSjHIai48fE67+zwF8Cs+C4fWqlfjw== + dependencies: + react-fast-compare "^3.0.1" + warning "^4.0.2" + react-redux-loading-bar@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/react-redux-loading-bar/-/react-redux-loading-bar-4.5.0.tgz#96538d0ba041463d810e213fb54eadbce9628266" @@ -11874,6 +11892,13 @@ warning@^4.0.1: dependencies: loose-envify "^1.0.0" +warning@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" + integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== + dependencies: + loose-envify "^1.0.0" + watchpack@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" From 0c4eae5f10656738dd448f8947b44df6d70e0ca6 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 10 Sep 2020 19:09:27 -0500 Subject: [PATCH 02/14] Hovercard: basic Redux+Popper prototype --- app/soapbox/actions/profile_hover_card.js | 9 +++ app/soapbox/components/profile_hover_card.js | 76 +++++++++++++++++++ app/soapbox/components/status.js | 15 ++-- app/soapbox/containers/status_container.js | 5 ++ app/soapbox/features/ui/index.js | 2 + app/soapbox/reducers/index.js | 2 + app/soapbox/reducers/profile_hover_card.js | 16 ++++ app/styles/components/profile_hover_card.scss | 9 +-- 8 files changed, 121 insertions(+), 13 deletions(-) create mode 100644 app/soapbox/actions/profile_hover_card.js create mode 100644 app/soapbox/components/profile_hover_card.js create mode 100644 app/soapbox/reducers/profile_hover_card.js diff --git a/app/soapbox/actions/profile_hover_card.js b/app/soapbox/actions/profile_hover_card.js new file mode 100644 index 000000000..7014858ae --- /dev/null +++ b/app/soapbox/actions/profile_hover_card.js @@ -0,0 +1,9 @@ +export const PROFILE_HOVER_CARD_OPEN = 'PROFILE_HOVER_CARD_OPEN'; + +export function openProfileHoverCard(ref, accountId) { + return { + type: PROFILE_HOVER_CARD_OPEN, + ref, + accountId, + }; +} diff --git a/app/soapbox/components/profile_hover_card.js b/app/soapbox/components/profile_hover_card.js new file mode 100644 index 000000000..c6319d38b --- /dev/null +++ b/app/soapbox/components/profile_hover_card.js @@ -0,0 +1,76 @@ +import React, { useEffect, useState } from 'react'; +import PropTypes from 'prop-types'; +import { useSelector, useDispatch } from 'react-redux'; +import { makeGetAccount } from 'soapbox/selectors'; +import { injectIntl, FormattedMessage } from 'react-intl'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import UserPanel from 'soapbox/features/ui/components/user_panel'; +import ActionButton from 'soapbox/features/ui/components/action_button'; +import { isAdmin, isModerator } from 'soapbox/utils/accounts'; +import Badge from 'soapbox/components/badge'; +import classNames from 'classnames'; +import { fetchRelationships } from 'soapbox/actions/accounts'; +import { usePopper } from 'react-popper'; + +const getAccount = makeGetAccount(); + +const getBadges = (account) => { + let badges = []; + if (isAdmin(account)) badges.push(); + if (isModerator(account)) badges.push(); + if (account.getIn(['patron', 'is_patron'])) badges.push(); + return badges; +}; + +export const ProfileHoverCard = ({ visible }) => { + const dispatch = useDispatch(); + + const [popperElement, setPopperElement] = useState(null); + + const accountId = useSelector(state => state.getIn(['profile_hover_card', 'accountId'])); + const account = useSelector(state => accountId && getAccount(state, accountId)); + const targetRef = useSelector(state => state.getIn(['profile_hover_card', 'ref'])); + const badges = account ? getBadges(account) : []; + + useEffect(() => { + if (accountId) dispatch(fetchRelationships([accountId])); + }, [dispatch, accountId]); + + const { styles, attributes } = usePopper(targetRef, popperElement); + + if (!account) return null; + const accountBio = { __html: account.get('note_emojified') }; + const followedBy = account.getIn(['relationship', 'followed_by']); + + return ( +
+
+ {followedBy && + + + } +
+ + {badges.length > 0 && +
+ {badges} +
} + {account.getIn(['source', 'note'], '').length > 0 && +
} +
+
+ ); +}; + +ProfileHoverCard.propTypes = { + visible: PropTypes.bool, + accountId: PropTypes.string, + account: ImmutablePropTypes.map, + intl: PropTypes.object.isRequired, +}; + +ProfileHoverCard.defaultProps = { + visible: true, +}; + +export default injectIntl(ProfileHoverCard); diff --git a/app/soapbox/components/status.js b/app/soapbox/components/status.js index 8acb4e7f5..d0d182b7d 100644 --- a/app/soapbox/components/status.js +++ b/app/soapbox/components/status.js @@ -18,7 +18,6 @@ import classNames from 'classnames'; import Icon from 'soapbox/components/icon'; import PollContainer from 'soapbox/containers/poll_container'; import { NavLink } from 'react-router-dom'; -import ProfileHoverCardContainer from '../features/profile_hover_card/profile_hover_card_container'; import { isMobile } from '../../../app/soapbox/is_mobile'; import { debounce } from 'lodash'; import { getDomain } from 'soapbox/utils/accounts'; @@ -82,6 +81,7 @@ class Status extends ImmutablePureComponent { onEmbed: PropTypes.func, onHeightChange: PropTypes.func, onToggleHidden: PropTypes.func, + onShowProfileCard: PropTypes.func, muted: PropTypes.bool, hidden: PropTypes.bool, unread: PropTypes.bool, @@ -257,7 +257,8 @@ class Status extends ImmutablePureComponent { } showProfileCard = debounce(() => { - this.setState({ profileCardVisible: true }); + const { onShowProfileCard, status } = this.props; + onShowProfileCard(this.profileNode, status.getIn(['account', 'id'])); }, 1200); handleProfileHover = e => { @@ -283,6 +284,10 @@ class Status extends ImmutablePureComponent { this.node = c; } + setProfileRef = c => { + this.profileNode = c; + } + render() { let media = null; let poll = null; @@ -457,7 +462,6 @@ class Status extends ImmutablePureComponent { }; const statusUrl = `/@${status.getIn(['account', 'acct'])}/posts/${status.get('id')}`; - const { profileCardVisible } = this.state; const favicon = status.getIn(['account', 'pleroma', 'favicon']); const domain = getDomain(status.get('account')); @@ -478,7 +482,7 @@ class Status extends ImmutablePureComponent {
} -
+
{statusAvatar} @@ -486,9 +490,6 @@ class Status extends ImmutablePureComponent { - { profileCardVisible && - - }
diff --git a/app/soapbox/containers/status_container.js b/app/soapbox/containers/status_container.js index 02dc76c21..d2ae6f7e6 100644 --- a/app/soapbox/containers/status_container.js +++ b/app/soapbox/containers/status_container.js @@ -35,6 +35,7 @@ import { groupRemoveStatus, } from '../actions/groups'; import { getSettings } from '../actions/settings'; +import { openProfileHoverCard } from 'soapbox/actions/profile_hover_card'; const messages = defineMessages({ deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, @@ -206,6 +207,10 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ dispatch(groupRemoveStatus(groupId, statusId)); }, + onShowProfileCard(ref, accountId) { + dispatch(openProfileHoverCard(ref, accountId)); + }, + }); export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Status)); diff --git a/app/soapbox/features/ui/index.js b/app/soapbox/features/ui/index.js index ff80086ee..3f37ffe40 100644 --- a/app/soapbox/features/ui/index.js +++ b/app/soapbox/features/ui/index.js @@ -38,6 +38,7 @@ import { Redirect } from 'react-router-dom'; import Icon from 'soapbox/components/icon'; import { isStaff } from 'soapbox/utils/accounts'; import ChatPanes from 'soapbox/features/chats/components/chat_panes'; +import ProfileHoverCard from 'soapbox/components/profile_hover_card'; import { Status, @@ -650,6 +651,7 @@ class UI extends React.PureComponent { {me && } {me && !mobile && } +
); diff --git a/app/soapbox/reducers/index.js b/app/soapbox/reducers/index.js index 2caec469a..6f4abdafe 100644 --- a/app/soapbox/reducers/index.js +++ b/app/soapbox/reducers/index.js @@ -46,6 +46,7 @@ import admin from './admin'; import chats from './chats'; import chat_messages from './chat_messages'; import chat_message_lists from './chat_message_lists'; +import profile_hover_card from './profile_hover_card'; const reducers = { dropdown_menu, @@ -95,6 +96,7 @@ const reducers = { chats, chat_messages, chat_message_lists, + profile_hover_card, }; export default combineReducers(reducers); diff --git a/app/soapbox/reducers/profile_hover_card.js b/app/soapbox/reducers/profile_hover_card.js new file mode 100644 index 000000000..907142656 --- /dev/null +++ b/app/soapbox/reducers/profile_hover_card.js @@ -0,0 +1,16 @@ +import { PROFILE_HOVER_CARD_OPEN } from 'soapbox/actions/profile_hover_card'; +import { Map as ImmutableMap } from 'immutable'; + +const initialState = ImmutableMap(); + +export default function profileHoverCard(state = initialState, action) { + switch(action.type) { + case PROFILE_HOVER_CARD_OPEN: + return ImmutableMap({ + ref: action.ref, + accountId: action.accountId, + }); + default: + return state; + } +} diff --git a/app/styles/components/profile_hover_card.scss b/app/styles/components/profile_hover_card.scss index decb68711..6d2ead6e1 100644 --- a/app/styles/components/profile_hover_card.scss +++ b/app/styles/components/profile_hover_card.scss @@ -15,7 +15,8 @@ transition-duration: 0.2s; width: 320px; z-index: 200; - left: -10px; + top: 0; + left: 0; padding: 20px; margin-bottom: 10px; @@ -24,10 +25,6 @@ pointer-events: all; } - @media(min-width: 750px) { - left: -100px; - } - .profile-hover-card__container { @include standard-panel; position: relative; @@ -114,7 +111,7 @@ .detailed-status { .profile-hover-card { top: 0; - left: 60px; + left: 0; } } From 4630a5cd04c95683c65be080839dbe794ecdd8e9 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 10 Sep 2020 20:18:04 -0500 Subject: [PATCH 03/14] Hovercard: mouseout clears the card --- app/soapbox/actions/profile_hover_card.js | 7 +++++++ app/soapbox/components/status.js | 13 +++++++------ app/soapbox/containers/status_container.js | 11 +++++++++-- .../features/status/components/detailed_status.js | 6 +++--- app/soapbox/reducers/profile_hover_card.js | 7 ++++++- app/styles/components/profile_hover_card.scss | 1 - 6 files changed, 32 insertions(+), 13 deletions(-) diff --git a/app/soapbox/actions/profile_hover_card.js b/app/soapbox/actions/profile_hover_card.js index 7014858ae..83b81d984 100644 --- a/app/soapbox/actions/profile_hover_card.js +++ b/app/soapbox/actions/profile_hover_card.js @@ -1,4 +1,5 @@ export const PROFILE_HOVER_CARD_OPEN = 'PROFILE_HOVER_CARD_OPEN'; +export const PROFILE_HOVER_CARD_CLEAR = 'PROFILE_HOVER_CARD_CLEAR'; export function openProfileHoverCard(ref, accountId) { return { @@ -7,3 +8,9 @@ export function openProfileHoverCard(ref, accountId) { accountId, }; } + +export function clearProfileHoverCard() { + return { + type: PROFILE_HOVER_CARD_CLEAR, + }; +} diff --git a/app/soapbox/components/status.js b/app/soapbox/components/status.js index d0d182b7d..e81ed9ccb 100644 --- a/app/soapbox/components/status.js +++ b/app/soapbox/components/status.js @@ -81,7 +81,7 @@ class Status extends ImmutablePureComponent { onEmbed: PropTypes.func, onHeightChange: PropTypes.func, onToggleHidden: PropTypes.func, - onShowProfileCard: PropTypes.func, + onShowHoverProfileCard: PropTypes.func, muted: PropTypes.bool, hidden: PropTypes.bool, unread: PropTypes.bool, @@ -256,17 +256,18 @@ class Status extends ImmutablePureComponent { this.handleToggleMediaVisibility(); } - showProfileCard = debounce(() => { - const { onShowProfileCard, status } = this.props; - onShowProfileCard(this.profileNode, status.getIn(['account', 'id'])); + showProfileHoverCard = debounce(() => { + const { onShowProfileHoverCard, status } = this.props; + onShowProfileHoverCard(this.profileNode, status.getIn(['account', 'id'])); }, 1200); handleProfileHover = e => { - if (!isMobile(window.innerWidth)) this.showProfileCard(); + if (!isMobile(window.innerWidth)) this.showProfileHoverCard(); } handleProfileLeave = e => { - this.showProfileCard.cancel(); + this.showProfileHoverCard.cancel(); + this.props.onClearProfileHoverCard(); this.setState({ profileCardVisible: false }); } diff --git a/app/soapbox/containers/status_container.js b/app/soapbox/containers/status_container.js index d2ae6f7e6..015256169 100644 --- a/app/soapbox/containers/status_container.js +++ b/app/soapbox/containers/status_container.js @@ -35,7 +35,10 @@ import { groupRemoveStatus, } from '../actions/groups'; import { getSettings } from '../actions/settings'; -import { openProfileHoverCard } from 'soapbox/actions/profile_hover_card'; +import { + openProfileHoverCard, + clearProfileHoverCard, +} from 'soapbox/actions/profile_hover_card'; const messages = defineMessages({ deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, @@ -207,10 +210,14 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ dispatch(groupRemoveStatus(groupId, statusId)); }, - onShowProfileCard(ref, accountId) { + onShowProfileHoverCard(ref, accountId) { dispatch(openProfileHoverCard(ref, accountId)); }, + onClearProfileHoverCard() { + dispatch(clearProfileHoverCard()); + }, + }); export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Status)); diff --git a/app/soapbox/features/status/components/detailed_status.js b/app/soapbox/features/status/components/detailed_status.js index d58bd7e72..ca3a90b88 100644 --- a/app/soapbox/features/status/components/detailed_status.js +++ b/app/soapbox/features/status/components/detailed_status.js @@ -86,16 +86,16 @@ export default class DetailedStatus extends ImmutablePureComponent { window.open(href, 'soapbox-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes'); } - showProfileCard = debounce(() => { + showProfileHoverCard = debounce(() => { this.setState({ profileCardVisible: true }); }, 1200); handleProfileHover = e => { - if (!isMobile(window.innerWidth)) this.showProfileCard(); + if (!isMobile(window.innerWidth)) this.showProfileHoverCard(); } handleProfileLeave = e => { - this.showProfileCard.cancel(); + this.showProfileHoverCard.cancel(); this.setState({ profileCardVisible: false }); } diff --git a/app/soapbox/reducers/profile_hover_card.js b/app/soapbox/reducers/profile_hover_card.js index 907142656..09e3a92fc 100644 --- a/app/soapbox/reducers/profile_hover_card.js +++ b/app/soapbox/reducers/profile_hover_card.js @@ -1,4 +1,7 @@ -import { PROFILE_HOVER_CARD_OPEN } from 'soapbox/actions/profile_hover_card'; +import { + PROFILE_HOVER_CARD_OPEN, + PROFILE_HOVER_CARD_CLEAR, +} from 'soapbox/actions/profile_hover_card'; import { Map as ImmutableMap } from 'immutable'; const initialState = ImmutableMap(); @@ -10,6 +13,8 @@ export default function profileHoverCard(state = initialState, action) { ref: action.ref, accountId: action.accountId, }); + case PROFILE_HOVER_CARD_CLEAR: + return ImmutableMap(); default: return state; } diff --git a/app/styles/components/profile_hover_card.scss b/app/styles/components/profile_hover_card.scss index 6d2ead6e1..ded79b5f5 100644 --- a/app/styles/components/profile_hover_card.scss +++ b/app/styles/components/profile_hover_card.scss @@ -17,7 +17,6 @@ z-index: 200; top: 0; left: 0; - padding: 20px; margin-bottom: 10px; &--visible { From d8ea37bf5e6650e174c7c23c3a27c6904c31558f Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 10 Sep 2020 22:08:17 -0500 Subject: [PATCH 04/14] Hovercard: clear when card is unhovered --- app/soapbox/components/profile_hover_card.js | 20 ++++++++++++++++--- app/soapbox/components/status.js | 2 -- app/styles/components/profile_hover_card.scss | 6 ++++++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/app/soapbox/components/profile_hover_card.js b/app/soapbox/components/profile_hover_card.js index c6319d38b..c62a36422 100644 --- a/app/soapbox/components/profile_hover_card.js +++ b/app/soapbox/components/profile_hover_card.js @@ -11,6 +11,7 @@ import Badge from 'soapbox/components/badge'; import classNames from 'classnames'; import { fetchRelationships } from 'soapbox/actions/accounts'; import { usePopper } from 'react-popper'; +import { clearProfileHoverCard } from 'soapbox/actions/profile_hover_card'; const getAccount = makeGetAccount(); @@ -22,13 +23,19 @@ const getBadges = (account) => { return badges; }; +const handleMouseLeave = (dispatch) => { + return e => { + dispatch(clearProfileHoverCard()); + }; +}; + export const ProfileHoverCard = ({ visible }) => { const dispatch = useDispatch(); const [popperElement, setPopperElement] = useState(null); const accountId = useSelector(state => state.getIn(['profile_hover_card', 'accountId'])); - const account = useSelector(state => accountId && getAccount(state, accountId)); + const account = useSelector(state => accountId && getAccount(state, accountId)); const targetRef = useSelector(state => state.getIn(['profile_hover_card', 'ref'])); const badges = account ? getBadges(account) : []; @@ -36,14 +43,21 @@ export const ProfileHoverCard = ({ visible }) => { if (accountId) dispatch(fetchRelationships([accountId])); }, [dispatch, accountId]); - const { styles, attributes } = usePopper(targetRef, popperElement); + const { styles, attributes } = usePopper(targetRef, popperElement, { + modifiers: [{ + name: 'offset', + options: { + offset: [-100, 0], + }, + }], + }); if (!account) return null; const accountBio = { __html: account.get('note_emojified') }; const followedBy = account.getIn(['relationship', 'followed_by']); return ( -
+
{followedBy && diff --git a/app/soapbox/components/status.js b/app/soapbox/components/status.js index e81ed9ccb..275603bcc 100644 --- a/app/soapbox/components/status.js +++ b/app/soapbox/components/status.js @@ -108,7 +108,6 @@ class Status extends ImmutablePureComponent { state = { showMedia: defaultMediaVisibility(this.props.status, this.props.displayMedia), statusId: undefined, - profileCardVisible: false, }; // Track height changes we know about to compensate scrolling @@ -268,7 +267,6 @@ class Status extends ImmutablePureComponent { handleProfileLeave = e => { this.showProfileHoverCard.cancel(); this.props.onClearProfileHoverCard(); - this.setState({ profileCardVisible: false }); } _properStatus() { diff --git a/app/styles/components/profile_hover_card.scss b/app/styles/components/profile_hover_card.scss index ded79b5f5..47a5242a2 100644 --- a/app/styles/components/profile_hover_card.scss +++ b/app/styles/components/profile_hover_card.scss @@ -125,3 +125,9 @@ display: block; } } + +/* Hide the popper when the reference is hidden */ +#popper[data-popper-reference-hidden] { + visibility: hidden; + pointer-events: none; +} From 9692c7539a9492538544a552612b35549c01a137 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 11 Sep 2020 10:05:52 -0500 Subject: [PATCH 05/14] Hovercard: appear and disappear properly --- app/soapbox/actions/profile_hover_card.js | 14 +++++++++++--- app/soapbox/components/profile_hover_card.js | 15 ++++++++++++--- app/soapbox/containers/status_container.js | 4 ++-- app/soapbox/reducers/profile_hover_card.js | 12 +++++++++--- 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/app/soapbox/actions/profile_hover_card.js b/app/soapbox/actions/profile_hover_card.js index 83b81d984..90543148d 100644 --- a/app/soapbox/actions/profile_hover_card.js +++ b/app/soapbox/actions/profile_hover_card.js @@ -1,5 +1,6 @@ export const PROFILE_HOVER_CARD_OPEN = 'PROFILE_HOVER_CARD_OPEN'; -export const PROFILE_HOVER_CARD_CLEAR = 'PROFILE_HOVER_CARD_CLEAR'; +export const PROFILE_HOVER_CARD_UPDATE = 'PROFILE_HOVER_CARD_UPDATE'; +export const PROFILE_HOVER_CARD_CLOSE = 'PROFILE_HOVER_CARD_CLOSE'; export function openProfileHoverCard(ref, accountId) { return { @@ -9,8 +10,15 @@ export function openProfileHoverCard(ref, accountId) { }; } -export function clearProfileHoverCard() { +export function updateProfileHoverCard() { return { - type: PROFILE_HOVER_CARD_CLEAR, + type: PROFILE_HOVER_CARD_UPDATE, + }; +} + +export function closeProfileHoverCard(force = false) { + return { + type: PROFILE_HOVER_CARD_CLOSE, + force, }; } diff --git a/app/soapbox/components/profile_hover_card.js b/app/soapbox/components/profile_hover_card.js index c62a36422..ae3f8bc3a 100644 --- a/app/soapbox/components/profile_hover_card.js +++ b/app/soapbox/components/profile_hover_card.js @@ -11,7 +11,10 @@ import Badge from 'soapbox/components/badge'; import classNames from 'classnames'; import { fetchRelationships } from 'soapbox/actions/accounts'; import { usePopper } from 'react-popper'; -import { clearProfileHoverCard } from 'soapbox/actions/profile_hover_card'; +import { + closeProfileHoverCard, + updateProfileHoverCard, +} from 'soapbox/actions/profile_hover_card'; const getAccount = makeGetAccount(); @@ -23,9 +26,15 @@ const getBadges = (account) => { return badges; }; +const handleMouseEnter = (dispatch) => { + return e => { + dispatch(updateProfileHoverCard()); + }; +}; + const handleMouseLeave = (dispatch) => { return e => { - dispatch(clearProfileHoverCard()); + dispatch(closeProfileHoverCard(true)); }; }; @@ -57,7 +66,7 @@ export const ProfileHoverCard = ({ visible }) => { const followedBy = account.getIn(['relationship', 'followed_by']); return ( -
+
{followedBy && diff --git a/app/soapbox/containers/status_container.js b/app/soapbox/containers/status_container.js index 015256169..fb976a82d 100644 --- a/app/soapbox/containers/status_container.js +++ b/app/soapbox/containers/status_container.js @@ -37,7 +37,7 @@ import { import { getSettings } from '../actions/settings'; import { openProfileHoverCard, - clearProfileHoverCard, + closeProfileHoverCard, } from 'soapbox/actions/profile_hover_card'; const messages = defineMessages({ @@ -215,7 +215,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ }, onClearProfileHoverCard() { - dispatch(clearProfileHoverCard()); + setTimeout(() => dispatch(closeProfileHoverCard()), 300); }, }); diff --git a/app/soapbox/reducers/profile_hover_card.js b/app/soapbox/reducers/profile_hover_card.js index 09e3a92fc..5776a3e87 100644 --- a/app/soapbox/reducers/profile_hover_card.js +++ b/app/soapbox/reducers/profile_hover_card.js @@ -1,6 +1,7 @@ import { PROFILE_HOVER_CARD_OPEN, - PROFILE_HOVER_CARD_CLEAR, + PROFILE_HOVER_CARD_CLOSE, + PROFILE_HOVER_CARD_UPDATE, } from 'soapbox/actions/profile_hover_card'; import { Map as ImmutableMap } from 'immutable'; @@ -13,8 +14,13 @@ export default function profileHoverCard(state = initialState, action) { ref: action.ref, accountId: action.accountId, }); - case PROFILE_HOVER_CARD_CLEAR: - return ImmutableMap(); + case PROFILE_HOVER_CARD_UPDATE: + return state.set('hovered', true); + case PROFILE_HOVER_CARD_CLOSE: + if (state.get('hovered') === true && !action.force) + return state; + else + return ImmutableMap(); default: return state; } From 1d90950e59c51ffc06067daca18c81ca1848e6f0 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 11 Sep 2020 11:37:05 -0500 Subject: [PATCH 06/14] Hovercard: refactor with HoverRefWrapper --- app/soapbox/components/hover_ref_wrapper.js | 50 +++++++++++++++++++ app/soapbox/components/profile_hover_card.js | 2 +- app/soapbox/components/status.js | 39 ++++----------- app/soapbox/containers/status_container.js | 12 ----- app/styles/components/profile_hover_card.scss | 1 - 5 files changed, 62 insertions(+), 42 deletions(-) create mode 100644 app/soapbox/components/hover_ref_wrapper.js diff --git a/app/soapbox/components/hover_ref_wrapper.js b/app/soapbox/components/hover_ref_wrapper.js new file mode 100644 index 000000000..64d62ba5c --- /dev/null +++ b/app/soapbox/components/hover_ref_wrapper.js @@ -0,0 +1,50 @@ +import React, { useRef } from 'react'; +import PropTypes from 'prop-types'; +import { + openProfileHoverCard, + closeProfileHoverCard, +} from 'soapbox/actions/profile_hover_card'; +import { useDispatch } from 'react-redux'; +import { debounce } from 'lodash'; +import { isMobile } from 'soapbox/is_mobile'; + +const showProfileHoverCard = debounce((dispatch, ref, accountId) => { + dispatch(openProfileHoverCard(ref, accountId)); +}, 1200); + +const handleMouseEnter = (dispatch, ref, accountId) => { + return e => { + if (!isMobile(window.innerWidth)) + showProfileHoverCard(dispatch, ref, accountId); + }; +}; + +const handleMouseLeave = (dispatch) => { + return e => { + showProfileHoverCard.cancel(); + setTimeout(() => dispatch(closeProfileHoverCard()), 300); + }; +}; + +export const HoverRefWrapper = ({ accountId, children }) => { + const dispatch = useDispatch(); + const ref = useRef(); + + return ( +
+ {children} +
+ ); +}; + +HoverRefWrapper.propTypes = { + accountId: PropTypes.string, + children: PropTypes.node, +}; + +export default HoverRefWrapper; diff --git a/app/soapbox/components/profile_hover_card.js b/app/soapbox/components/profile_hover_card.js index ae3f8bc3a..b545b87bc 100644 --- a/app/soapbox/components/profile_hover_card.js +++ b/app/soapbox/components/profile_hover_card.js @@ -45,7 +45,7 @@ export const ProfileHoverCard = ({ visible }) => { const accountId = useSelector(state => state.getIn(['profile_hover_card', 'accountId'])); const account = useSelector(state => accountId && getAccount(state, accountId)); - const targetRef = useSelector(state => state.getIn(['profile_hover_card', 'ref'])); + const targetRef = useSelector(state => state.getIn(['profile_hover_card', 'ref', 'current'])); const badges = account ? getBadges(account) : []; useEffect(() => { diff --git a/app/soapbox/components/status.js b/app/soapbox/components/status.js index 275603bcc..e52dfdc5c 100644 --- a/app/soapbox/components/status.js +++ b/app/soapbox/components/status.js @@ -18,9 +18,8 @@ import classNames from 'classnames'; import Icon from 'soapbox/components/icon'; import PollContainer from 'soapbox/containers/poll_container'; import { NavLink } from 'react-router-dom'; -import { isMobile } from '../../../app/soapbox/is_mobile'; -import { debounce } from 'lodash'; import { getDomain } from 'soapbox/utils/accounts'; +import HoverRefWrapper from 'soapbox/components/hover_ref_wrapper'; // We use the component (and not the container) since we do not want // to use the progress bar to show download progress @@ -255,20 +254,6 @@ class Status extends ImmutablePureComponent { this.handleToggleMediaVisibility(); } - showProfileHoverCard = debounce(() => { - const { onShowProfileHoverCard, status } = this.props; - onShowProfileHoverCard(this.profileNode, status.getIn(['account', 'id'])); - }, 1200); - - handleProfileHover = e => { - if (!isMobile(window.innerWidth)) this.showProfileHoverCard(); - } - - handleProfileLeave = e => { - this.showProfileHoverCard.cancel(); - this.props.onClearProfileHoverCard(); - } - _properStatus() { const { status } = this.props; @@ -283,10 +268,6 @@ class Status extends ImmutablePureComponent { this.node = c; } - setProfileRef = c => { - this.profileNode = c; - } - render() { let media = null; let poll = null; @@ -481,15 +462,17 @@ class Status extends ImmutablePureComponent {
} -
-
- - {statusAvatar} + +
+
+ + {statusAvatar} +
+ + +
- - - -
+
{!group && status.get('group') && ( diff --git a/app/soapbox/containers/status_container.js b/app/soapbox/containers/status_container.js index fb976a82d..02dc76c21 100644 --- a/app/soapbox/containers/status_container.js +++ b/app/soapbox/containers/status_container.js @@ -35,10 +35,6 @@ import { groupRemoveStatus, } from '../actions/groups'; import { getSettings } from '../actions/settings'; -import { - openProfileHoverCard, - closeProfileHoverCard, -} from 'soapbox/actions/profile_hover_card'; const messages = defineMessages({ deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, @@ -210,14 +206,6 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ dispatch(groupRemoveStatus(groupId, statusId)); }, - onShowProfileHoverCard(ref, accountId) { - dispatch(openProfileHoverCard(ref, accountId)); - }, - - onClearProfileHoverCard() { - setTimeout(() => dispatch(closeProfileHoverCard()), 300); - }, - }); export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Status)); diff --git a/app/styles/components/profile_hover_card.scss b/app/styles/components/profile_hover_card.scss index 47a5242a2..e89998901 100644 --- a/app/styles/components/profile_hover_card.scss +++ b/app/styles/components/profile_hover_card.scss @@ -17,7 +17,6 @@ z-index: 200; top: 0; left: 0; - margin-bottom: 10px; &--visible { opacity: 1; From 24bf880e0b5d952dda5613dc70fd22bf050539c2 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 11 Sep 2020 11:42:02 -0500 Subject: [PATCH 07/14] Hovercard: refactor detailed status --- .../status/components/detailed_status.js | 46 ++++++------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/app/soapbox/features/status/components/detailed_status.js b/app/soapbox/features/status/components/detailed_status.js index ca3a90b88..e323873a3 100644 --- a/app/soapbox/features/status/components/detailed_status.js +++ b/app/soapbox/features/status/components/detailed_status.js @@ -16,10 +16,8 @@ import classNames from 'classnames'; import Icon from 'soapbox/components/icon'; import PollContainer from 'soapbox/containers/poll_container'; import { StatusInteractionBar } from './status_interaction_bar'; -import ProfileHoverCardContainer from 'soapbox/features/profile_hover_card/profile_hover_card_container'; -import { isMobile } from 'soapbox/is_mobile'; -import { debounce } from 'lodash'; import { getDomain } from 'soapbox/utils/accounts'; +import HoverRefWrapper from 'soapbox/components/hover_ref_wrapper'; export default class DetailedStatus extends ImmutablePureComponent { @@ -42,7 +40,6 @@ export default class DetailedStatus extends ImmutablePureComponent { state = { height: null, - profileCardVisible: false, }; handleOpenVideo = (media, startTime) => { @@ -86,24 +83,10 @@ export default class DetailedStatus extends ImmutablePureComponent { window.open(href, 'soapbox-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes'); } - showProfileHoverCard = debounce(() => { - this.setState({ profileCardVisible: true }); - }, 1200); - - handleProfileHover = e => { - if (!isMobile(window.innerWidth)) this.showProfileHoverCard(); - } - - handleProfileLeave = e => { - this.showProfileHoverCard.cancel(); - this.setState({ profileCardVisible: false }); - } - render() { const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status; const outerStyle = { boxSizing: 'border-box' }; const { compact } = this.props; - const { profileCardVisible } = this.state; const favicon = status.getIn(['account', 'pleroma', 'favicon']); const domain = getDomain(status.get('account')); @@ -181,21 +164,20 @@ export default class DetailedStatus extends ImmutablePureComponent { return (
-
-
- -
- -
-
- - - + +
+
+ +
+ +
+
+ + + +
- { profileCardVisible && - - } -
+ {status.get('group') && (
From 72659f27c14b422e1351bc326055dd169bab8fcb Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 11 Sep 2020 12:17:32 -0500 Subject: [PATCH 08/14] Hovercard: attach to each element for better positioning --- app/soapbox/components/display_name.js | 5 +++- app/soapbox/components/hover_ref_wrapper.js | 12 ++++++--- app/soapbox/components/profile_hover_card.js | 9 +------ app/soapbox/components/status.js | 21 ++++++++------- .../status/components/detailed_status.js | 26 ++++++++++--------- app/styles/components/profile_hover_card.scss | 12 --------- 6 files changed, 39 insertions(+), 46 deletions(-) diff --git a/app/soapbox/components/display_name.js b/app/soapbox/components/display_name.js index 7ab8b9e60..7d848a902 100644 --- a/app/soapbox/components/display_name.js +++ b/app/soapbox/components/display_name.js @@ -4,6 +4,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import VerificationBadge from './verification_badge'; import { acctFull } from '../utils/accounts'; import { List as ImmutableList } from 'immutable'; +import HoverRefWrapper from 'soapbox/components/hover_ref_wrapper'; export default class DisplayName extends React.PureComponent { @@ -42,7 +43,9 @@ export default class DisplayName extends React.PureComponent { return ( - {displayName} + + {displayName} + {suffix} {children} diff --git a/app/soapbox/components/hover_ref_wrapper.js b/app/soapbox/components/hover_ref_wrapper.js index 64d62ba5c..fe6ac2bed 100644 --- a/app/soapbox/components/hover_ref_wrapper.js +++ b/app/soapbox/components/hover_ref_wrapper.js @@ -26,25 +26,31 @@ const handleMouseLeave = (dispatch) => { }; }; -export const HoverRefWrapper = ({ accountId, children }) => { +export const HoverRefWrapper = ({ accountId, children, inline }) => { const dispatch = useDispatch(); const ref = useRef(); + const Elem = inline ? 'span' : 'div'; return ( -
{children} -
+ ); }; HoverRefWrapper.propTypes = { accountId: PropTypes.string, children: PropTypes.node, + inline: PropTypes.bool, +}; + +HoverRefWrapper.defaultProps = { + inline: false, }; export default HoverRefWrapper; diff --git a/app/soapbox/components/profile_hover_card.js b/app/soapbox/components/profile_hover_card.js index b545b87bc..8d234aa4b 100644 --- a/app/soapbox/components/profile_hover_card.js +++ b/app/soapbox/components/profile_hover_card.js @@ -52,14 +52,7 @@ export const ProfileHoverCard = ({ visible }) => { if (accountId) dispatch(fetchRelationships([accountId])); }, [dispatch, accountId]); - const { styles, attributes } = usePopper(targetRef, popperElement, { - modifiers: [{ - name: 'offset', - options: { - offset: [-100, 0], - }, - }], - }); + const { styles, attributes } = usePopper(targetRef, popperElement); if (!account) return null; const accountBio = { __html: account.get('note_emojified') }; diff --git a/app/soapbox/components/status.js b/app/soapbox/components/status.js index e52dfdc5c..9e8a2d684 100644 --- a/app/soapbox/components/status.js +++ b/app/soapbox/components/status.js @@ -462,17 +462,18 @@ class Status extends ImmutablePureComponent {
} - -
-
- - {statusAvatar} -
- - - +
+
+ + + {statusAvatar} + +
- + + + +
{!group && status.get('group') && ( diff --git a/app/soapbox/features/status/components/detailed_status.js b/app/soapbox/features/status/components/detailed_status.js index e323873a3..131de49ec 100644 --- a/app/soapbox/features/status/components/detailed_status.js +++ b/app/soapbox/features/status/components/detailed_status.js @@ -164,20 +164,22 @@ export default class DetailedStatus extends ImmutablePureComponent { return (
- -
-
- -
+
+
+ +
+ -
-
- - - -
+ +
+ + + + + +
- +
{status.get('group') && (
diff --git a/app/styles/components/profile_hover_card.scss b/app/styles/components/profile_hover_card.scss index e89998901..b3fb4fc31 100644 --- a/app/styles/components/profile_hover_card.scss +++ b/app/styles/components/profile_hover_card.scss @@ -113,18 +113,6 @@ } } -/* Prevent floating avatars from intercepting with current card */ -.status, -.detailed-status { - .floating-link { - display: none; - } - - &:hover .floating-link { - display: block; - } -} - /* Hide the popper when the reference is hidden */ #popper[data-popper-reference-hidden] { visibility: hidden; From 9ac6f3aaabe05df9654c51e12553a5d18d65675d Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 11 Sep 2020 12:24:43 -0500 Subject: [PATCH 09/14] Restore old border-radius behavior, fixes #266 --- app/styles/application.scss | 1 - app/styles/chats.scss | 1 - app/styles/components/columns.scss | 1 - app/styles/components/detailed-status.scss | 1 - app/styles/components/drawer.scss | 2 +- app/styles/loading.scss | 1 - app/styles/overflow_hacks.scss | 37 ---------------------- 7 files changed, 1 insertion(+), 43 deletions(-) delete mode 100644 app/styles/overflow_hacks.scss diff --git a/app/styles/application.scss b/app/styles/application.scss index 96eb4dcf4..fdce329ad 100644 --- a/app/styles/application.scss +++ b/app/styles/application.scss @@ -27,7 +27,6 @@ @import 'dyslexic'; @import 'demetricator'; @import 'pro'; -@import 'overflow_hacks'; @import 'chats'; // COMPONENTS diff --git a/app/styles/chats.scss b/app/styles/chats.scss index 29c2f0c4f..3e2fc2e24 100644 --- a/app/styles/chats.scss +++ b/app/styles/chats.scss @@ -322,7 +322,6 @@ display: flex; align-items: center; background: var(--accent-color--faint); - border-radius: 10px 10px 0 0; .column-back-button { background: transparent; diff --git a/app/styles/components/columns.scss b/app/styles/components/columns.scss index d8034e497..10e46e441 100644 --- a/app/styles/components/columns.scss +++ b/app/styles/components/columns.scss @@ -212,7 +212,6 @@ font-size: 16px; line-height: inherit; border: 0; - border-radius: 10px 10px 0 0; text-align: unset; padding: 15px; margin: 0; diff --git a/app/styles/components/detailed-status.scss b/app/styles/components/detailed-status.scss index d53b426ee..ceab5899c 100644 --- a/app/styles/components/detailed-status.scss +++ b/app/styles/components/detailed-status.scss @@ -62,7 +62,6 @@ border-bottom: 1px solid var(--brand-color--faint); display: flex; flex-direction: row; - border-radius: 0 0 10px 10px; } .detailed-status__link { diff --git a/app/styles/components/drawer.scss b/app/styles/components/drawer.scss index 39e936158..7dc5a1a10 100644 --- a/app/styles/components/drawer.scss +++ b/app/styles/components/drawer.scss @@ -20,7 +20,7 @@ .column, .drawer { flex: 1 1 100%; - overflow: visible; + overflow: hidden; } .drawer__pager { diff --git a/app/styles/loading.scss b/app/styles/loading.scss index 88705782e..42a3a0c1f 100644 --- a/app/styles/loading.scss +++ b/app/styles/loading.scss @@ -188,7 +188,6 @@ align-items: center; justify-content: center; padding: 20px; - border-radius: 0 0 10px 10px; & > div { width: 100%; diff --git a/app/styles/overflow_hacks.scss b/app/styles/overflow_hacks.scss deleted file mode 100644 index ef72b6863..000000000 --- a/app/styles/overflow_hacks.scss +++ /dev/null @@ -1,37 +0,0 @@ -// This is a file dedicated to fixing the css we broke by introducing the hover -// card and `overflow:visible` on drawer.scss line 23. If we ever figure out how -// to pop the hover card out while keeping `overflow:hidden`, feel free to delete -// this entire file. - -button.column-header__button.active { - border-radius: 0 10px 0 0; -} - -.column-back-button.column-back-button--slim-button { - border-radius: 0 10px 0 0; -} - -.detailed-status__wrapper .detailed-status__action-bar { - border-radius: 0 0 10px 10px; -} - -.slist .item-list .column-link { - background-color: transparent; - border-top: 1px solid var(--brand-color--med); -} - -.focusable { - &:focus { - border-radius: 0 0 10px 10px; - } -} - -.load-more:hover { - border-radius: 0 0 10px 10px; -} - -// this still looks like shit but at least it's better than it overflowing - -.empty-column-indicator { - border-radius: 0 0 10px 10px; -} From 7494a0fd00b66062dc78e21bf5185e0e5f2046bb Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 11 Sep 2020 12:30:16 -0500 Subject: [PATCH 10/14] Hovercard: delete legacy code --- .../profile_hover_card_container.js | 79 ------------------- 1 file changed, 79 deletions(-) delete mode 100644 app/soapbox/features/profile_hover_card/profile_hover_card_container.js diff --git a/app/soapbox/features/profile_hover_card/profile_hover_card_container.js b/app/soapbox/features/profile_hover_card/profile_hover_card_container.js deleted file mode 100644 index d4f706212..000000000 --- a/app/soapbox/features/profile_hover_card/profile_hover_card_container.js +++ /dev/null @@ -1,79 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { makeGetAccount } from '../../selectors'; -import { injectIntl, FormattedMessage } from 'react-intl'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import ImmutablePureComponent from 'react-immutable-pure-component'; -import UserPanel from '../ui/components/user_panel'; -import ActionButton from '../ui/components/action_button'; -import { isAdmin, isModerator } from 'soapbox/utils/accounts'; -import Badge from 'soapbox/components/badge'; -import classNames from 'classnames'; -import { fetchRelationships } from 'soapbox/actions/accounts'; - -const getAccount = makeGetAccount(); - -const mapStateToProps = (state, { accountId }) => { - return { - account: getAccount(state, accountId), - }; -}; - -export default @connect(mapStateToProps) -@injectIntl -class ProfileHoverCardContainer extends ImmutablePureComponent { - - static propTypes = { - visible: PropTypes.bool, - accountId: PropTypes.string, - account: ImmutablePropTypes.map, - intl: PropTypes.object.isRequired, - dispatch: PropTypes.func.isRequired, - } - - static defaultProps = { - visible: true, - } - - getBadges = () => { - const { account } = this.props; - let badges = []; - if (isAdmin(account)) badges.push(); - if (isModerator(account)) badges.push(); - if (account.getIn(['patron', 'is_patron'])) badges.push(); - return badges; - } - - componentDidMount() { - this.props.dispatch(fetchRelationships([this.props.accountId])); - } - - render() { - const { visible, accountId, account } = this.props; - if (!accountId) return null; - const accountBio = { __html: account.get('note_emojified') }; - const followedBy = account.getIn(['relationship', 'followed_by']); - const badges = this.getBadges(); - - return ( -
-
- {followedBy && - - - } -
- - {badges.length > 0 && -
- {badges} -
} - {account.getIn(['source', 'note'], '').length > 0 && -
} -
-
- ); - } - -}; From 77339056418bfd446d3b0a1a3671d824faf2d688 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 11 Sep 2020 12:38:42 -0500 Subject: [PATCH 11/14] Hovercard: update DisplayName snapshot --- .../__snapshots__/display_name-test.js.snap | 24 ++++++++++++------- .../components/__tests__/display_name-test.js | 4 ++-- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/app/soapbox/components/__tests__/__snapshots__/display_name-test.js.snap b/app/soapbox/components/__tests__/__snapshots__/display_name-test.js.snap index 59789099f..d26a406a2 100644 --- a/app/soapbox/components/__tests__/__snapshots__/display_name-test.js.snap +++ b/app/soapbox/components/__tests__/__snapshots__/display_name-test.js.snap @@ -4,16 +4,22 @@ exports[` renders display name + account name 1`] = ` - - Foo

", + + + Foo

", + } } - } - /> -
+ /> +
+
diff --git a/app/soapbox/components/__tests__/display_name-test.js b/app/soapbox/components/__tests__/display_name-test.js index 0d040c4cd..f626f94ca 100644 --- a/app/soapbox/components/__tests__/display_name-test.js +++ b/app/soapbox/components/__tests__/display_name-test.js @@ -1,7 +1,7 @@ import React from 'react'; -import renderer from 'react-test-renderer'; import { fromJS } from 'immutable'; import DisplayName from '../display_name'; +import { createComponent } from 'soapbox/test_helpers'; describe('', () => { it('renders display name + account name', () => { @@ -10,7 +10,7 @@ describe('', () => { acct: 'bar@baz', display_name_html: '

Foo

', }); - const component = renderer.create(); + const component = createComponent(); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); From d6a39b3e0959dc40b21ebd0bf5775d172914b073 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 11 Sep 2020 13:04:31 -0500 Subject: [PATCH 12/14] Hovercard: put hovercard on chat window --- .../features/chats/components/chat_window.js | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/app/soapbox/features/chats/components/chat_window.js b/app/soapbox/features/chats/components/chat_window.js index acc5c2e8e..bb1a6346b 100644 --- a/app/soapbox/features/chats/components/chat_window.js +++ b/app/soapbox/features/chats/components/chat_window.js @@ -14,6 +14,7 @@ import { } from 'soapbox/actions/chats'; import ChatBox from './chat_box'; import { shortNumberFormat } from 'soapbox/utils/numbers'; +import HoverRefWrapper from 'soapbox/components/hover_ref_wrapper'; const mapStateToProps = (state, { pane }) => ({ me: state.get('me'), @@ -79,13 +80,24 @@ class ChatWindow extends ImmutablePureComponent { const right = (285 * (idx + 1)) + 20; const unreadCount = chat.get('unread'); + const unreadIcon = ( + + {shortNumberFormat(unreadCount)} + + ); + + const avatar = ( + + + + + + ); + return (
- {unreadCount > 0 - ? {shortNumberFormat(unreadCount)} - : - } + {unreadCount > 0 ? unreadIcon : avatar } From ec491add236b044ad0429e2f5b8a122e0be2f468 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 11 Sep 2020 13:04:45 -0500 Subject: [PATCH 13/14] UserPanel: only show stats when they're available --- app/soapbox/features/ui/components/user_panel.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/soapbox/features/ui/components/user_panel.js b/app/soapbox/features/ui/components/user_panel.js index 1ba4e3efc..411b9b70b 100644 --- a/app/soapbox/features/ui/components/user_panel.js +++ b/app/soapbox/features/ui/components/user_panel.js @@ -56,26 +56,26 @@ class UserPanel extends ImmutablePureComponent {
-
+ {account.get('statuses_count') &&
{shortNumberFormat(account.get('statuses_count'))} -
+
} -
+ {account.get('followers_count') &&
{shortNumberFormat(account.get('followers_count'))} -
+
} -
+ {account.get('following_count') &&
{shortNumberFormat(account.get('following_count'))} -
+
}
From 40937b9fdc2890d1f28632d1a62de09b03820057 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 11 Sep 2020 13:09:39 -0500 Subject: [PATCH 14/14] Chats: allow clicking recipient in chatroom --- app/soapbox/features/chats/chat_room.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/soapbox/features/chats/chat_room.js b/app/soapbox/features/chats/chat_room.js index f3e81cc12..a9f0ccd5d 100644 --- a/app/soapbox/features/chats/chat_room.js +++ b/app/soapbox/features/chats/chat_room.js @@ -3,6 +3,7 @@ import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { injectIntl } from 'react-intl'; +import { Link } from 'react-router-dom'; import ImmutablePureComponent from 'react-immutable-pure-component'; import Avatar from 'soapbox/components/avatar'; import { acctFull } from 'soapbox/utils/accounts'; @@ -75,12 +76,12 @@ class ChatRoom extends ImmutablePureComponent {
-
+
@{acctFull(account)}
-
+