From e248399220617efe3c0832e9538ef2fe3e35b35e Mon Sep 17 00:00:00 2001 From: ThibG Date: Thu, 9 Jul 2020 15:09:19 +0200 Subject: [PATCH] [Glitch] Freeze scroll position when a dropdown menu is open in the TL Port 6fda3cbbebfdc7b050f4437b996b2ad36c1db64c to glitch-soc Signed-off-by: Thibaut Girka --- .../flavours/glitch/actions/dropdown_menu.js | 4 ++-- .../flavours/glitch/components/scrollable_list.js | 15 ++++++++++++--- .../flavours/glitch/components/status.js | 1 + .../glitch/components/status_action_bar.js | 14 ++++++++++++-- .../flavours/glitch/components/status_list.js | 2 ++ .../glitch/containers/dropdown_menu_container.js | 4 ++-- .../direct_timeline/components/conversation.js | 13 +++++++++++-- .../components/conversations_list.js | 4 +++- .../flavours/glitch/reducers/dropdown_menu.js | 6 +++--- 9 files changed, 48 insertions(+), 15 deletions(-) diff --git a/app/javascript/flavours/glitch/actions/dropdown_menu.js b/app/javascript/flavours/glitch/actions/dropdown_menu.js index 14f2939c7..fb6e55612 100644 --- a/app/javascript/flavours/glitch/actions/dropdown_menu.js +++ b/app/javascript/flavours/glitch/actions/dropdown_menu.js @@ -1,8 +1,8 @@ export const DROPDOWN_MENU_OPEN = 'DROPDOWN_MENU_OPEN'; export const DROPDOWN_MENU_CLOSE = 'DROPDOWN_MENU_CLOSE'; -export function openDropdownMenu(id, placement, keyboard) { - return { type: DROPDOWN_MENU_OPEN, id, placement, keyboard }; +export function openDropdownMenu(id, placement, keyboard, scroll_key) { + return { type: DROPDOWN_MENU_OPEN, id, placement, keyboard, scroll_key }; } export function closeDropdownMenu(id) { diff --git a/app/javascript/flavours/glitch/components/scrollable_list.js b/app/javascript/flavours/glitch/components/scrollable_list.js index fae0a7393..5d10ed650 100644 --- a/app/javascript/flavours/glitch/components/scrollable_list.js +++ b/app/javascript/flavours/glitch/components/scrollable_list.js @@ -10,10 +10,18 @@ import { List as ImmutableList } from 'immutable'; import classNames from 'classnames'; import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from 'flavours/glitch/util/fullscreen'; import LoadingIndicator from './loading_indicator'; +import { connect } from 'react-redux'; const MOUSE_IDLE_DELAY = 300; -export default class ScrollableList extends PureComponent { +const mapStateToProps = (state, { scrollKey }) => { + return { + preventScroll: scrollKey === state.getIn(['dropdown_menu', 'scroll_key']), + }; +}; + +export default @connect(mapStateToProps) +class ScrollableList extends PureComponent { static contextTypes = { router: PropTypes.object, @@ -37,6 +45,7 @@ export default class ScrollableList extends PureComponent { emptyMessage: PropTypes.node, children: PropTypes.node, bindToDocument: PropTypes.bool, + preventScroll: PropTypes.bool, }; static defaultProps = { @@ -124,7 +133,7 @@ export default class ScrollableList extends PureComponent { }); handleMouseIdle = () => { - if (this.scrollToTopOnMouseIdle) { + if (this.scrollToTopOnMouseIdle && !this.props.preventScroll) { this.setScrollTop(0); } this.mouseMovedRecently = false; @@ -176,7 +185,7 @@ export default class ScrollableList extends PureComponent { this.getFirstChildKey(prevProps) !== this.getFirstChildKey(this.props); const pendingChanged = (prevProps.numPending > 0) !== (this.props.numPending > 0); - if (pendingChanged || someItemInserted && (this.getScrollTop() > 0 || this.mouseMovedRecently)) { + if (pendingChanged || someItemInserted && (this.getScrollTop() > 0 || this.mouseMovedRecently || this.props.preventScroll)) { return this.getScrollHeight() - this.getScrollTop(); } else { return null; diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js index ba0823a60..4e628a420 100644 --- a/app/javascript/flavours/glitch/components/status.js +++ b/app/javascript/flavours/glitch/components/status.js @@ -96,6 +96,7 @@ class Status extends ImmutablePureComponent { cacheMediaWidth: PropTypes.func, cachedMediaWidth: PropTypes.number, onClick: PropTypes.func, + scrollKey: PropTypes.string, }; state = { diff --git a/app/javascript/flavours/glitch/components/status_action_bar.js b/app/javascript/flavours/glitch/components/status_action_bar.js index 0a481c816..c314c5fd5 100644 --- a/app/javascript/flavours/glitch/components/status_action_bar.js +++ b/app/javascript/flavours/glitch/components/status_action_bar.js @@ -74,6 +74,7 @@ class StatusActionBar extends ImmutablePureComponent { withDismiss: PropTypes.bool, showReplyCount: PropTypes.bool, directMessage: PropTypes.bool, + scrollKey: PropTypes.string, intl: PropTypes.object.isRequired, }; @@ -198,7 +199,7 @@ class StatusActionBar extends ImmutablePureComponent { } render () { - const { status, intl, withDismiss, showReplyCount, directMessage } = this.props; + const { status, intl, withDismiss, showReplyCount, directMessage, scrollKey } = this.props; const mutingConversation = status.get('muted'); const anonymousAccess = !me; @@ -300,7 +301,16 @@ class StatusActionBar extends ImmutablePureComponent { , filterButton,
- +
, ]} diff --git a/app/javascript/flavours/glitch/components/status_list.js b/app/javascript/flavours/glitch/components/status_list.js index a399ff567..60cc23f4b 100644 --- a/app/javascript/flavours/glitch/components/status_list.js +++ b/app/javascript/flavours/glitch/components/status_list.js @@ -99,6 +99,7 @@ export default class StatusList extends ImmutablePureComponent { onMoveUp={this.handleMoveUp} onMoveDown={this.handleMoveDown} contextType={timelineId} + scrollKey={this.props.scrollKey} /> )) ) : null; @@ -112,6 +113,7 @@ export default class StatusList extends ImmutablePureComponent { onMoveUp={this.handleMoveUp} onMoveDown={this.handleMoveDown} contextType={timelineId} + scrollKey={this.props.scrollKey} /> )).concat(scrollableContent); } diff --git a/app/javascript/flavours/glitch/containers/dropdown_menu_container.js b/app/javascript/flavours/glitch/containers/dropdown_menu_container.js index 1378e75fe..e60ee814d 100644 --- a/app/javascript/flavours/glitch/containers/dropdown_menu_container.js +++ b/app/javascript/flavours/glitch/containers/dropdown_menu_container.js @@ -11,7 +11,7 @@ const mapStateToProps = state => ({ openedViaKeyboard: state.getIn(['dropdown_menu', 'keyboard']), }); -const mapDispatchToProps = (dispatch, { status, items }) => ({ +const mapDispatchToProps = (dispatch, { status, items, scrollKey }) => ({ onOpen(id, onItemClick, dropdownPlacement, keyboard) { dispatch(isUserTouching() ? openModal('ACTIONS', { status, @@ -22,7 +22,7 @@ const mapDispatchToProps = (dispatch, { status, items }) => ({ onClick: item.action ? ((e) => { return onItemClick(i, e) }) : null, } : null ), - }) : openDropdownMenu(id, dropdownPlacement, keyboard)); + }) : openDropdownMenu(id, dropdownPlacement, keyboard, scrollKey)); }, onClose(id) { dispatch(closeModal('ACTIONS')); diff --git a/app/javascript/flavours/glitch/features/direct_timeline/components/conversation.js b/app/javascript/flavours/glitch/features/direct_timeline/components/conversation.js index 47b92560d..0af8b348f 100644 --- a/app/javascript/flavours/glitch/features/direct_timeline/components/conversation.js +++ b/app/javascript/flavours/glitch/features/direct_timeline/components/conversation.js @@ -36,6 +36,7 @@ class Conversation extends ImmutablePureComponent { accounts: ImmutablePropTypes.list.isRequired, lastStatus: ImmutablePropTypes.map, unread:PropTypes.bool.isRequired, + scrollKey: PropTypes.string, onMoveUp: PropTypes.func, onMoveDown: PropTypes.func, markRead: PropTypes.func.isRequired, @@ -156,7 +157,7 @@ class Conversation extends ImmutablePureComponent { } render () { - const { accounts, lastStatus, unread, intl } = this.props; + const { accounts, lastStatus, unread, scrollKey, intl } = this.props; const { isExpanded } = this.state; if (lastStatus === null) { @@ -223,7 +224,15 @@ class Conversation extends ImmutablePureComponent {
- +
diff --git a/app/javascript/flavours/glitch/features/direct_timeline/components/conversations_list.js b/app/javascript/flavours/glitch/features/direct_timeline/components/conversations_list.js index 4fa76fd6d..c2aff1b57 100644 --- a/app/javascript/flavours/glitch/features/direct_timeline/components/conversations_list.js +++ b/app/javascript/flavours/glitch/features/direct_timeline/components/conversations_list.js @@ -10,6 +10,7 @@ export default class ConversationsList extends ImmutablePureComponent { static propTypes = { conversations: ImmutablePropTypes.list.isRequired, + scrollKey: PropTypes.string.isRequired, hasMore: PropTypes.bool, isLoading: PropTypes.bool, onLoadMore: PropTypes.func, @@ -57,13 +58,14 @@ export default class ConversationsList extends ImmutablePureComponent { const { conversations, onLoadMore, ...other } = this.props; return ( - + {conversations.map(item => ( ))} diff --git a/app/javascript/flavours/glitch/reducers/dropdown_menu.js b/app/javascript/flavours/glitch/reducers/dropdown_menu.js index 36fd4f132..a78a11acc 100644 --- a/app/javascript/flavours/glitch/reducers/dropdown_menu.js +++ b/app/javascript/flavours/glitch/reducers/dropdown_menu.js @@ -4,14 +4,14 @@ import { DROPDOWN_MENU_CLOSE, } from '../actions/dropdown_menu'; -const initialState = Immutable.Map({ openId: null, placement: null, keyboard: false }); +const initialState = Immutable.Map({ openId: null, placement: null, keyboard: false, scroll_key: null }); export default function dropdownMenu(state = initialState, action) { switch (action.type) { case DROPDOWN_MENU_OPEN: - return state.merge({ openId: action.id, placement: action.placement, keyboard: action.keyboard }); + return state.merge({ openId: action.id, placement: action.placement, keyboard: action.keyboard, scroll_key: action.scroll_key }); case DROPDOWN_MENU_CLOSE: - return state.get('openId') === action.id ? state.set('openId', null) : state; + return state.get('openId') === action.id ? state.set('openId', null).set('scroll_key', null) : state; default: return state; }