Skip to content

Commit

Permalink
dx: [des] fix keyboard scrolling on inverted FlatLists
Browse files Browse the repository at this point in the history
  • Loading branch information
staltz committed Mar 1, 2022
1 parent 648eb38 commit 4e0cfd6
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 7 deletions.
70 changes: 69 additions & 1 deletion patches/react-native-web+0.17.6.patch
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
diff --git a/node_modules/react-native-web/dist/vendor/react-native/VirtualizedList/index.js b/node_modules/react-native-web/dist/vendor/react-native/VirtualizedList/index.js
index b15746f..518b888 100644
index b15746f..73fad96 100644
--- a/node_modules/react-native-web/dist/vendor/react-native/VirtualizedList/index.js
+++ b/node_modules/react-native-web/dist/vendor/react-native/VirtualizedList/index.js
@@ -650,6 +650,8 @@ var VirtualizedList = /*#__PURE__*/function (_React$PureComponent) {
Expand All @@ -11,3 +11,71 @@ index b15746f..518b888 100644
}
}
} else {
@@ -868,7 +870,66 @@ var VirtualizedList = /*#__PURE__*/function (_React$PureComponent) {
var _this2 = this;

if (this._scrollRef && this._scrollRef.getScrollableNode) {
- this._scrollRef.getScrollableNode().addEventListener('wheel', this.invertedWheelEventHandler);
+ const node = this._scrollRef.getScrollableNode();
+ node.addEventListener('wheel', this.invertedWheelEventHandler);
+ let lastKeyDown = 0;
+ node.addEventListener('keydown', (ev) => {
+ const DELTA = 40;
+ const PAGE = node.clientHeight * 0.9;
+ const TOTAL = node.scrollHeight;
+ const behavior = (Date.now() - lastKeyDown) > 60 ? 'smooth' : 'instant';
+ lastKeyDown = Date.now();
+ if (ev.code === 'ArrowDown') {
+ node.scroll({
+ top: node.scrollTop + (this.props.inverted ? -DELTA : +DELTA),
+ left: 0,
+ behavior
+ });
+ } else if (ev.code === 'ArrowUp') {
+ node.scroll({
+ top: node.scrollTop + (this.props.inverted ? +DELTA : -DELTA),
+ left: 0,
+ behavior
+ });
+ } else if (ev.code === 'PageDown') {
+ node.scroll({
+ top: node.scrollTop + (this.props.inverted ? -PAGE : +PAGE),
+ left: 0,
+ behavior
+ });
+ } else if (ev.code === 'PageUp') {
+ node.scroll({
+ top: node.scrollTop + (this.props.inverted ? +PAGE : -PAGE),
+ left: 0,
+ behavior
+ });
+ } else if (ev.code === 'Space' && !ev.shiftKey) {
+ node.scroll({
+ top: node.scrollTop + (this.props.inverted ? -PAGE : +PAGE),
+ left: 0,
+ behavior
+ });
+ } else if (ev.code === 'Space' && ev.shiftKey) {
+ node.scroll({
+ top: node.scrollTop + (this.props.inverted ? +PAGE : -PAGE),
+ left: 0,
+ behavior
+ });
+ } else if (ev.code === 'End') {
+ node.scroll({
+ top: this.props.inverted ? 0 : TOTAL,
+ left: 0,
+ behavior: 'smooth'
+ });
+ } else if (ev.code === 'Home') {
+ node.scroll({
+ top: this.props.inverted ? TOTAL : 0,
+ left: 0,
+ behavior: 'smooth'
+ });
+ }
+ ev.preventDefault();
+ });
} else {
setTimeout(function () {
return _this2.setupWebWheelHandler();
1 change: 1 addition & 0 deletions src/frontend/components/messages/Message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export interface Props {
preferredReactions: Array<string>;
expandCW?: boolean;
replyCount?: number;
webFocusHack?: boolean;
onPressReactions?: (ev: PressReactionsEvent) => void;
onPressAddReaction?: (ev: PressAddReactionEvent) => void;
onPressReply?: () => void;
Expand Down
38 changes: 34 additions & 4 deletions src/frontend/components/messages/MessageContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
//
// SPDX-License-Identifier: MPL-2.0

import {PureComponent, createElement as $} from 'react';
import {View, StyleSheet, ViewStyle, Platform} from 'react-native';
import {PureComponent, createElement as $, RefObject, createRef} from 'react';
import {View, StyleSheet, ViewStyle, Platform, UIManager} from 'react-native';
import {Palette} from '~frontend/global-styles/palette';
import {Dimensions} from '~frontend/global-styles/dimens';

Expand All @@ -17,6 +17,7 @@ const card: ViewStyle = {
...Platform.select({
web: {
width: Dimensions.desktopMiddleWidth.px,
outlineStyle: 'none',
},
}),
};
Expand All @@ -36,14 +37,43 @@ export const styles = StyleSheet.create({
interface Props {
style?: any;
unread?: boolean;

/**
* This hack is for issue #1784. We need to *focus* a child component in the
* FullThread's FlatList in order to allow it to receive `keydown` events in
* VirtualizedList (see our react-native-web patch file). We chose to focus
* this MessageContainer because we don't want any scroll to happen onFocus,
* and the first message's MessageContainer is positioned exactly at the
* default scroll position.
*
* I know, this is a very dirty hack, but it's the only way to do it. I would
* gladly accept a PR to make this cleaner.
*/
webFocusHack?: boolean;
}

export default class MessageContainer extends PureComponent<Props> {
private ref: RefObject<View> = createRef();

private onLayout = () => {
if (Platform.OS === 'web') {
setTimeout(() => {
if (this.ref?.current) {
(UIManager as any).focus(this.ref.current);
}
}, 50);
}
};

public render() {
const {style, children, unread} = this.props;
const {style, children, unread, webFocusHack} = this.props;
return $(
View,
{style: [unread ? styles.unreadCard : styles.readCard, style]},
{
ref: this.ref,
style: [unread ? styles.unreadCard : styles.readCard, style],
onLayout: webFocusHack ? this.onLayout : undefined,
},
children,
);
}
Expand Down
5 changes: 3 additions & 2 deletions src/frontend/components/messages/PostMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export interface Props {
replyCount: number;
selfFeedId: FeedId;
expandCW?: boolean;
webFocusHack?: boolean;
onPressReactions?: (ev: PressReactionsEvent) => void;
onPressAddReaction?: (ev: PressAddReactionEvent) => void;
onPressReply?: () => void;
Expand Down Expand Up @@ -72,15 +73,15 @@ export default class PostMessage extends PureComponent<Props, State> {

public render() {
const props = this.props;
const {msg, lastSessionTimestamp} = props;
const {msg, lastSessionTimestamp, webFocusHack} = props;
const cwMsg = msg as Msg<CWPost>;
const hasCW =
!!cwMsg.value.content.contentWarning &&
typeof cwMsg.value.content.contentWarning === 'string';
const opened = hasCW ? this.state.cwOpened : true;
const unread = msg.timestamp > lastSessionTimestamp;

return h(MessageContainer, {}, [
return h(MessageContainer, {webFocusHack}, [
h(MessageHeader, {...props, unread}),
hasCW
? h(ContentWarning, {
Expand Down
1 change: 1 addition & 0 deletions src/frontend/screens/thread/view/FullThread.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ export default class FullThread extends Component<Props, State> {
msg,
key: msg.key,
expandCW: index === rootIndex && this.props.expandRootCW === true,
webFocusHack: Platform.OS === 'web' && index === 0,
selfFeedId,
lastSessionTimestamp,
preferredReactions,
Expand Down

0 comments on commit 4e0cfd6

Please sign in to comment.