[Glitch] Freeze scroll position when a dropdown menu is open in the TL
Port 6fda3cbbeb to glitch-soc
Signed-off-by: Thibaut Girka <thib@sitedethib.com>
			
			
This commit is contained in:
		| @@ -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) { | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -96,6 +96,7 @@ class Status extends ImmutablePureComponent { | ||||
|     cacheMediaWidth: PropTypes.func, | ||||
|     cachedMediaWidth: PropTypes.number, | ||||
|     onClick: PropTypes.func, | ||||
|     scrollKey: PropTypes.string, | ||||
|   }; | ||||
|  | ||||
|   state = { | ||||
|   | ||||
| @@ -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 { | ||||
|           <IconButton key='bookmark-button' className='status__action-bar-button bookmark-icon' disabled={anonymousAccess} active={status.get('bookmarked')} pressed={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' onClick={this.handleBookmarkClick} />, | ||||
|           filterButton, | ||||
|           <div key='dropdown-button' className='status__action-bar-dropdown'> | ||||
|             <DropdownMenuContainer disabled={anonymousAccess} status={status} items={menu} icon='ellipsis-h' size={18} direction='right' ariaLabel={intl.formatMessage(messages.more)} /> | ||||
|             <DropdownMenuContainer | ||||
|               scrollKey={scrollKey} | ||||
|               disabled={anonymousAccess} | ||||
|               status={status} | ||||
|               items={menu} | ||||
|               icon='ellipsis-h' | ||||
|               size={18} | ||||
|               direction='right' | ||||
|               ariaLabel={intl.formatMessage(messages.more)} | ||||
|             /> | ||||
|           </div>, | ||||
|         ]} | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
|     } | ||||
|   | ||||
| @@ -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')); | ||||
|   | ||||
| @@ -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 { | ||||
|               <IconButton className='status__action-bar-button' title={intl.formatMessage(messages.reply)} icon='reply' onClick={this.handleReply} /> | ||||
|  | ||||
|               <div className='status__action-bar-dropdown'> | ||||
|                 <DropdownMenuContainer status={lastStatus} items={menu} icon='ellipsis-h' size={18} direction='right' title={intl.formatMessage(messages.more)} /> | ||||
|                 <DropdownMenuContainer | ||||
|                   scrollKey={scrollKey} | ||||
|                   status={lastStatus} | ||||
|                   items={menu} | ||||
|                   icon='ellipsis-h' | ||||
|                   size={18} | ||||
|                   direction='right' | ||||
|                   title={intl.formatMessage(messages.more)} | ||||
|                 /> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|   | ||||
| @@ -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 ( | ||||
|       <ScrollableList {...other} onLoadMore={onLoadMore && this.handleLoadOlder} scrollKey='direct' ref={this.setRef}> | ||||
|       <ScrollableList {...other} onLoadMore={onLoadMore && this.handleLoadOlder} ref={this.setRef}> | ||||
|         {conversations.map(item => ( | ||||
|           <ConversationContainer | ||||
|             key={item.get('id')} | ||||
|             conversationId={item.get('id')} | ||||
|             onMoveUp={this.handleMoveUp} | ||||
|             onMoveDown={this.handleMoveDown} | ||||
|             scrollKey={this.props.scrollKey} | ||||
|           /> | ||||
|         ))} | ||||
|       </ScrollableList> | ||||
|   | ||||
| @@ -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; | ||||
|   } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user