diff --git a/app/soapbox/components/status.js b/app/soapbox/components/status.js index 889afc01e..41ccfc0ff 100644 --- a/app/soapbox/components/status.js +++ b/app/soapbox/components/status.js @@ -18,6 +18,7 @@ import classNames from 'classnames'; import Icon from 'soapbox/components/icon'; import PollContainer from 'soapbox/containers/poll_container'; import { NavLink } from 'react-router-dom'; +import UserPanel from '../features/ui/components/user_panel'; // We use the component (and not the container) since we do not want // to use the progress bar to show download progress @@ -104,6 +105,9 @@ class Status extends ImmutablePureComponent { state = { showMedia: defaultMediaVisibility(this.props.status, this.props.displayMedia), statusId: undefined, + profilePanelVisible: false, + profilePanelX: 0, + profilePanelY: 0, }; // Track height changes we know about to compensate scrolling @@ -249,6 +253,16 @@ class Status extends ImmutablePureComponent { this.handleToggleMediaVisibility(); } + isMobile = () => window.matchMedia('only screen and (max-width: 895px)').matches; + + handleProfileHover = e => { + if (!this.isMobile()) this.setState({ profilePanelVisible: true, profilePanelX: e.nativeEvent.offsetX, profilePanelY: e.nativeEvent.offsetY }); + } + + handleProfileLeave = e => { + if (!this.isMobile()) this.setState({ profilePanelVisible: false }); + } + _properStatus() { const { status } = this.props; @@ -435,6 +449,7 @@ class Status extends ImmutablePureComponent { }; const statusUrl = `/@${status.getIn(['account', 'acct'])}/posts/${status.get('id')}`; + const { profilePanelVisible, profilePanelX, profilePanelY } = this.state; return ( @@ -448,13 +463,15 @@ 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 a31d826be..a88395e2a 100644 --- a/app/soapbox/features/status/components/detailed_status.js +++ b/app/soapbox/features/status/components/detailed_status.js @@ -16,6 +16,7 @@ import classNames from 'classnames'; import Icon from 'soapbox/components/icon'; import PollContainer from 'soapbox/containers/poll_container'; import { StatusInteractionBar } from './status_interaction_bar'; +import UserPanel from '../../ui/components/user_panel'; export default class DetailedStatus extends ImmutablePureComponent { @@ -38,6 +39,9 @@ export default class DetailedStatus extends ImmutablePureComponent { state = { height: null, + profilePanelVisible: false, + profilePanelX: 0, + profilePanelY: 0, }; handleOpenVideo = (media, startTime) => { @@ -81,10 +85,21 @@ export default class DetailedStatus extends ImmutablePureComponent { window.open(href, 'soapbox-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes'); } + isMobile = () => window.matchMedia('only screen and (max-width: 895px)').matches; + + handleProfileHover = e => { + if (!this.isMobile()) this.setState({ profilePanelVisible: true, profilePanelX: e.nativeEvent.offsetX, profilePanelY: e.nativeEvent.offsetY }); + } + + handleProfileLeave = e => { + if (!this.isMobile()) this.setState({ profilePanelVisible: 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 { profilePanelVisible, profilePanelX, profilePanelY } = this.state; if (!status) { return null; @@ -158,10 +173,13 @@ export default class DetailedStatus extends ImmutablePureComponent { return (
- -
- -
+
+ +
+ + +
+
{status.get('group') && (
diff --git a/app/soapbox/features/ui/components/user_panel.js b/app/soapbox/features/ui/components/user_panel.js index 83dfd2099..da515ffed 100644 --- a/app/soapbox/features/ui/components/user_panel.js +++ b/app/soapbox/features/ui/components/user_panel.js @@ -10,6 +10,7 @@ import Avatar from 'soapbox/components/avatar'; import { shortNumberFormat } from 'soapbox/utils/numbers'; import { acctFull } from 'soapbox/utils/accounts'; import StillImage from 'soapbox/components/still_image'; +import classNames from 'classnames'; class UserPanel extends ImmutablePureComponent { @@ -17,16 +18,23 @@ class UserPanel extends ImmutablePureComponent { account: ImmutablePropTypes.map, intl: PropTypes.object.isRequired, domain: PropTypes.string, + style: PropTypes.object, + visible: PropTypes.bool, + } + + static defaultProps = { + style: {}, + visible: true, } render() { - const { account, intl, domain } = this.props; + const { account, intl, domain, style, visible } = this.props; if (!account) return null; const displayNameHtml = { __html: account.get('display_name_html') }; const acct = account.get('acct').indexOf('@') === -1 && domain ? `${account.get('acct')}@${domain}` : account.get('acct'); return ( -
+
@@ -84,17 +92,17 @@ class UserPanel extends ImmutablePureComponent { }; - -const mapStateToProps = state => { - const me = state.get('me'); +const makeMapStateToProps = () => { const getAccount = makeGetAccount(); - return { - account: getAccount(state, me), - }; + const mapStateToProps = (state, { accountId }) => ({ + account: getAccount(state, accountId), + }); + + return mapStateToProps; }; export default injectIntl( - connect(mapStateToProps, null, null, { + connect(makeMapStateToProps, null, null, { forwardRef: true, })(UserPanel)); diff --git a/app/soapbox/pages/home_page.js b/app/soapbox/pages/home_page.js index f06bd90eb..eeeafede9 100644 --- a/app/soapbox/pages/home_page.js +++ b/app/soapbox/pages/home_page.js @@ -15,6 +15,7 @@ import { getFeatures } from 'soapbox/utils/features'; const mapStateToProps = state => { const me = state.get('me'); return { + me, account: state.getIn(['accounts', me]), hasPatron: state.getIn(['soapbox', 'extensions', 'patron', 'enabled']), features: getFeatures(state.get('instance')), @@ -30,7 +31,7 @@ class HomePage extends ImmutablePureComponent { } render() { - const { children, account, hasPatron, features } = this.props; + const { me, children, account, hasPatron, features } = this.props; return (
@@ -39,7 +40,7 @@ class HomePage extends ImmutablePureComponent {
- + {hasPatron && } diff --git a/app/styles/components/drawer.scss b/app/styles/components/drawer.scss index 7dc5a1a10..39e936158 100644 --- a/app/styles/components/drawer.scss +++ b/app/styles/components/drawer.scss @@ -20,7 +20,7 @@ .column, .drawer { flex: 1 1 100%; - overflow: hidden; + overflow: visible; } .drawer__pager { diff --git a/app/styles/components/status.scss b/app/styles/components/status.scss index 0d349764b..f167cf2ac 100644 --- a/app/styles/components/status.scss +++ b/app/styles/components/status.scss @@ -152,7 +152,6 @@ .status__info .status__display-name { display: block; max-width: 100%; - padding-right: 25px; } .status__info { @@ -160,6 +159,27 @@ z-index: 4; } +.status__profile, +.detailed-status__profile { + display: inline-block; + + .user-panel { + position: absolute; + display: flex; + opacity: 0; + pointer-events: none; + transition-property: opacity; + transition-duration: 0.5s; + z-index: 999; + + &--visible { + opacity: 1; + transition-delay: 1s; + pointer-events: all; + } + } +} + .status-check-box { border-bottom: 1px solid var(--background-color); display: flex; diff --git a/app/styles/components/user-panel.scss b/app/styles/components/user-panel.scss index cf49a3d68..44ec3cd9b 100644 --- a/app/styles/components/user-panel.scss +++ b/app/styles/components/user-panel.scss @@ -3,7 +3,13 @@ display: flex; width: 265px; flex-direction: column; - overflow-y: hidden; + + &, + .user-panel__account__name, + .user-panel__account__username { + overflow: hidden; + text-overflow: ellipsis; + } &__header { display: block;