]> cat aescling's git repositories - mastodon.git/commitdiff
[Glitch] Freeze scroll position when a dropdown menu is open in the TL
authorThibG <thib@sitedethib.com>
Thu, 9 Jul 2020 13:09:19 +0000 (15:09 +0200)
committerThibaut Girka <thib@sitedethib.com>
Fri, 10 Jul 2020 15:05:44 +0000 (17:05 +0200)
Port 6fda3cbbebfdc7b050f4437b996b2ad36c1db64c to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
app/javascript/flavours/glitch/actions/dropdown_menu.js
app/javascript/flavours/glitch/components/scrollable_list.js
app/javascript/flavours/glitch/components/status.js
app/javascript/flavours/glitch/components/status_action_bar.js
app/javascript/flavours/glitch/components/status_list.js
app/javascript/flavours/glitch/containers/dropdown_menu_container.js
app/javascript/flavours/glitch/features/direct_timeline/components/conversation.js
app/javascript/flavours/glitch/features/direct_timeline/components/conversations_list.js
app/javascript/flavours/glitch/reducers/dropdown_menu.js

index 14f2939c78db9cdfdd25612176a917f50876200d..fb6e55612e708e976322cc2cc6e5a8e282d7486e 100644 (file)
@@ -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) {
index fae0a73934354cad17db41ca55d227c4314d5128..5d10ed650234b534241ee9391dc728ce94d8f871 100644 (file)
@@ -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;
index ba0823a608ab9de5d7a1e69ddf24c94ba0dd704f..4e628a420a1e7730fe58a9353be0b72ff61bbf05 100644 (file)
@@ -96,6 +96,7 @@ class Status extends ImmutablePureComponent {
     cacheMediaWidth: PropTypes.func,
     cachedMediaWidth: PropTypes.number,
     onClick: PropTypes.func,
+    scrollKey: PropTypes.string,
   };
 
   state = {
index 0a481c816e85840f20226bdadb2b3546c5a12c75..c314c5fd524355f5c6a78006f86857f7e6db63ad 100644 (file)
@@ -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>,
         ]}
 
index a399ff5675461fe6bab177ebcc55e38c44f263f5..60cc23f4b0630b3e47019df166513fa69ae6214c 100644 (file)
@@ -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);
     }
index 1378e75fe467abfefd47a331026c5c9fa235750d..e60ee814d8707a1c941f606a8128c71fb862de69 100644 (file)
@@ -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'));
index 47b92560d17c8dffa376434a5bea3ed4ab692b94..0af8b348f23844dc36ce2d3f83c7351b482163d5 100644 (file)
@@ -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>
index 4fa76fd6df17f1240999c8f77f3075c1ef324c29..c2aff1b57fd1a511fab9968ea16c5cadda34647c 100644 (file)
@@ -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>
index 36fd4f13217522000bab317a98a94cbcfc1aeed6..a78a11accacf41c804219743d2bcd78e6651ec62 100644 (file)
@@ -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;
   }