Page MenuHomePhabricator

D7630.diff
No OneTemporary

D7630.diff

diff --git a/native/chat/chat-input-bar.react.js b/native/chat/chat-input-bar.react.js
--- a/native/chat/chat-input-bar.react.js
+++ b/native/chat/chat-input-bar.react.js
@@ -84,6 +84,7 @@
import { ChatContext } from './chat-context.js';
import type { ChatNavigationProp } from './chat.react.js';
+import type { RemoveEditMode } from './message-list-types.js';
import TypeaheadTooltip from './typeahead-tooltip.react.js';
import Button from '../components/button.react.js';
// eslint-disable-next-line import/extensions
@@ -175,7 +176,7 @@
+textEdited: boolean,
+buttonsExpanded: boolean,
+selectionState: SyncedSelectionData,
- +isExitingEditMode: boolean,
+ +isExitingDuringEditMode: boolean,
};
class ChatInputBar extends React.PureComponent<Props, State> {
textInput: ?React.ElementRef<typeof TextInput>;
@@ -194,6 +195,8 @@
sendButtonContainerStyle: AnimatedViewStyle;
clearBeforeRemoveListener: () => void;
+ clearFocusListener: () => void;
+ clearBlurListener: () => void;
constructor(props: Props) {
super(props);
@@ -202,7 +205,7 @@
textEdited: false,
buttonsExpanded: true,
selectionState: { text: props.draft, selection: { start: 0, end: 0 } },
- isExitingEditMode: false,
+ isExitingDuringEditMode: false,
};
this.setUpActionIconAnimations();
@@ -337,15 +340,25 @@
}
componentDidMount() {
- if (this.props.isActive) {
+ const { isActive, navigation } = this.props;
+ if (isActive) {
this.addEditInputMessageListener();
}
- if (this.props.navigation) {
- this.clearBeforeRemoveListener = this.props.navigation.addListener(
- 'beforeRemove',
- this.onNavigationBeforeRemove,
- );
+ if (!navigation) {
+ return;
}
+ this.clearBeforeRemoveListener = navigation.addListener(
+ 'beforeRemove',
+ this.onNavigationBeforeRemove,
+ );
+ this.clearFocusListener = navigation.addListener(
+ 'focus',
+ this.onNavigationFocus,
+ );
+ this.clearBlurListener = navigation.addListener(
+ 'blur',
+ this.onNavigationBlur,
+ );
}
componentWillUnmount() {
@@ -355,6 +368,12 @@
if (this.clearBeforeRemoveListener) {
this.clearBeforeRemoveListener();
}
+ if (this.clearFocusListener) {
+ this.clearFocusListener();
+ }
+ if (this.clearBlurListener) {
+ this.clearBlurListener();
+ }
}
componentDidUpdate(prevProps: Props, prevState: State) {
@@ -408,6 +427,13 @@
this.expandButtons();
this.setIOSKeyboardHeight();
}
+
+ if (
+ this.props.inputState?.editState.editedMessage &&
+ !prevProps.inputState?.editState.editedMessage
+ ) {
+ this.blockNavigation();
+ }
}
addEditInputMessageListener() {
@@ -733,9 +759,12 @@
};
updateText = (text: string) => {
+ if (this.state.isExitingDuringEditMode) {
+ return;
+ }
this.setState({ text, textEdited: true });
this.props.inputState?.setEditedMessageChanged(this.isMessageEdited(text));
- if (this.isEditMode() || this.state.isExitingEditMode) {
+ if (this.isEditMode()) {
return;
}
this.saveDraft(text);
@@ -851,6 +880,41 @@
return text !== originalText;
};
+ unblockNavigation = () => {
+ const { navigation } = this.props;
+ if (!navigation) {
+ return;
+ }
+ navigation.setParams({ removeEditMode: null });
+ };
+
+ removeEditMode: RemoveEditMode = action => {
+ const { navigation } = this.props;
+ if (!navigation || this.state.isExitingDuringEditMode) {
+ return 'ignore_action';
+ }
+ if (!this.isMessageEdited()) {
+ this.unblockNavigation();
+ return 'reduce_action';
+ }
+ const unblockAndDispatch = () => {
+ this.unblockNavigation();
+ navigation.dispatch(action);
+ };
+ exitEditAlert(unblockAndDispatch);
+ return 'ignore_action';
+ };
+
+ blockNavigation = () => {
+ const { navigation } = this.props;
+ if (!navigation || !navigation.isFocused()) {
+ return;
+ }
+ navigation.setParams({
+ removeEditMode: this.removeEditMode,
+ });
+ };
+
editMessage = async (messageID: string, text: string) => {
if (!this.isMessageEdited()) {
this.exitEditMode();
@@ -896,12 +960,27 @@
exitEditMode = () => {
this.props.inputState?.setEditedMessage(null, () => {
+ this.unblockNavigation();
this.updateText(this.props.draft);
this.focusAndUpdateButtonsVisibility();
this.updateSendButton(this.props.draft);
});
};
+ onNavigationFocus = () => {
+ this.setState({ isExitingDuringEditMode: false });
+ };
+
+ onNavigationBlur = () => {
+ if (!this.isEditMode()) {
+ return;
+ }
+ this.setState(
+ { text: this.props.draft, isExitingDuringEditMode: true },
+ this.exitEditMode,
+ );
+ };
+
onNavigationBeforeRemove = e => {
if (!this.isEditMode()) {
return;
@@ -910,7 +989,7 @@
e.preventDefault();
const saveExit = () => {
this.props.inputState?.setEditedMessage(null, () => {
- this.setState({ isExitingEditMode: true }, () => {
+ this.setState({ isExitingDuringEditMode: true }, () => {
if (!this.props.navigation) {
return;
}
diff --git a/native/chat/chat-router.js b/native/chat/chat-router.js
--- a/native/chat/chat-router.js
+++ b/native/chat/chat-router.js
@@ -20,6 +20,7 @@
clearThreadsActionType,
pushNewThreadActionType,
} from '../navigation/action-types.js';
+import { getRemoveEditMode } from '../navigation/nav-selectors.js';
import {
removeScreensFromStack,
getThreadIDFromRoute,
@@ -128,7 +129,22 @@
);
return baseGetStateForAction(clearedState, navigateAction, options);
} else {
- return baseGetStateForAction(lastState, action, options);
+ const result = baseGetStateForAction(lastState, action, options);
+ const removeEditMode = getRemoveEditMode(lastState);
+
+ // We prevent navigating if the user is in edit mode. We don't block
+ // navigating back here because it is handled by the `beforeRemove`
+ // listener in the `ChatInputBar` component.
+ if (
+ result !== null &&
+ result?.index &&
+ result.index > lastState.index &&
+ removeEditMode &&
+ removeEditMode(action) === 'ignore_action'
+ ) {
+ return lastState;
+ }
+ return result;
}
},
actionCreators: {
diff --git a/native/chat/message-list-types.js b/native/chat/message-list-types.js
--- a/native/chat/message-list-types.js
+++ b/native/chat/message-list-types.js
@@ -7,6 +7,7 @@
import type { ThreadInfo } from 'lib/types/thread-types.js';
import { type UserInfo } from 'lib/types/user-types.js';
+import type { ChatRouterNavigationAction } from './chat-router.js';
import type { MarkdownRules } from '../markdown/rules.react.js';
import { useTextMessageRulesFunc } from '../markdown/rules.react.js';
import { MessageListRouteName } from '../navigation/route-names.js';
@@ -15,8 +16,13 @@
+threadInfo: ThreadInfo,
+pendingPersonalThreadUserInfo?: UserInfo,
+searching?: boolean,
+ +removeEditMode?: ?RemoveEditMode,
};
+export type RemoveEditMode = (
+ action: ChatRouterNavigationAction,
+) => 'ignore_action' | 'reduce_action';
+
export type MessageListContextType = {
+getTextMessageMarkdownRules: (useDarkStyle: boolean) => MarkdownRules,
};
diff --git a/native/navigation/nav-selectors.js b/native/navigation/nav-selectors.js
--- a/native/navigation/nav-selectors.js
+++ b/native/navigation/nav-selectors.js
@@ -31,6 +31,7 @@
threadRoutes,
CommunityDrawerNavigatorRouteName,
} from './route-names.js';
+import type { RemoveEditMode } from '../chat/message-list-types';
import { useSelector } from '../redux/redux-utils.js';
import type { NavPlusRedux } from '../types/selector-types.js';
import type { GlobalTheme } from '../types/themes.js';
@@ -316,6 +317,24 @@
},
);
+function getRemoveEditMode(
+ chatRouteState: ?PossiblyStaleNavigationState,
+): ?RemoveEditMode {
+ if (!chatRouteState) {
+ return null;
+ }
+ const messageListRoute =
+ chatRouteState.routes[chatRouteState.routes.length - 1];
+ if (messageListRoute.name !== MessageListRouteName) {
+ return null;
+ }
+ if (!messageListRoute || !messageListRoute.params) {
+ return null;
+ }
+ const removeEditMode: Function = messageListRoute.params.removeEditMode;
+ return removeEditMode;
+}
+
function useCurrentLeafRouteName(): ?string {
const navContext = React.useContext(NavContext);
return React.useMemo(() => {
@@ -342,4 +361,5 @@
useCalendarQuery,
drawerSwipeEnabledSelector,
useCurrentLeafRouteName,
+ getRemoveEditMode,
};
diff --git a/native/utils/edit-messages-utils.js b/native/utils/edit-messages-utils.js
--- a/native/utils/edit-messages-utils.js
+++ b/native/utils/edit-messages-utils.js
@@ -14,9 +14,7 @@
{
text: 'Discard edit',
style: 'destructive',
- onPress: () => {
- onDiscard();
- },
+ onPress: onDiscard,
},
],
);

File Metadata

Mime Type
text/plain
Expires
Tue, Dec 3, 5:01 PM (3 h, 32 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2612452
Default Alt Text
D7630.diff (8 KB)

Event Timeline