Page MenuHomePhabricator

D5515.id18740.diff
No OneTemporary

D5515.id18740.diff

diff --git a/native/chat/inner-text-message.react.js b/native/chat/inner-text-message.react.js
--- a/native/chat/inner-text-message.react.js
+++ b/native/chat/inner-text-message.react.js
@@ -5,6 +5,7 @@
import { View, StyleSheet, TouchableWithoutFeedback } from 'react-native';
import Animated from 'react-native-reanimated';
+import { messageKey } from 'lib/shared/message-utils';
import { colorIsDark } from 'lib/shared/thread-utils';
import GestureTouchableOpacity from '../components/gesture-touchable-opacity.react';
@@ -13,6 +14,7 @@
import { useColors, colors } from '../themes/colors';
import type { ChatTextMessageInfoItemWithHeight } from '../types/chat-types';
import { useComposedMessageMaxWidth } from './composed-message-width';
+import { MessageContext } from './message-context.react';
import { MessageListContext } from './message-list-types';
import {
allCorners,
@@ -101,6 +103,16 @@
return [styles.text, textStyle];
}, [darkColor]);
+ // We use a MessageContext to allow MarkdownLink and MarkdownSpoiler
+ // to access the messageKey so it is 'self-aware'
+ const key = messageKey(item.messageInfo);
+ const messageContextValue = React.useMemo(
+ () => ({
+ messageKey: key,
+ }),
+ [key],
+ );
+
const message = (
<TouchableWithoutFeedback>
<View>
@@ -111,9 +123,11 @@
style={[styles.message, cornerStyle]}
animatedStyle={messageStyle}
>
- <Markdown style={markdownStyles} rules={rules}>
- {text}
- </Markdown>
+ <MessageContext.Provider value={messageContextValue}>
+ <Markdown style={markdownStyles} rules={rules}>
+ {text}
+ </Markdown>
+ </MessageContext.Provider>
</GestureTouchableOpacity>
</View>
</TouchableWithoutFeedback>
diff --git a/native/chat/message-context.react.js b/native/chat/message-context.react.js
new file mode 100644
--- /dev/null
+++ b/native/chat/message-context.react.js
@@ -0,0 +1,15 @@
+// @flow
+
+import * as React from 'react';
+
+export type MessageContextType = {
+ +messageKey: string,
+};
+
+const MessageContext: React.Context<MessageContextType> = React.createContext<MessageContextType>(
+ {
+ messageKey: '',
+ },
+);
+
+export { MessageContext };
diff --git a/native/chat/text-message.react.js b/native/chat/text-message.react.js
--- a/native/chat/text-message.react.js
+++ b/native/chat/text-message.react.js
@@ -46,7 +46,7 @@
// ChatContext
+chatContext: ?ChatContextType,
// MarkdownContext
- +linkModalActive: boolean,
+ +isLinkModalActive: boolean,
+linkIsBlockingPresses: boolean,
};
class TextMessage extends React.PureComponent<Props> {
@@ -62,7 +62,7 @@
verticalBounds,
overlayContext,
chatContext,
- linkModalActive,
+ isLinkModalActive,
linkIsBlockingPresses,
canCreateSidebarFromMessage,
...viewProps
@@ -71,7 +71,7 @@
let swipeOptions = 'none';
const canReply = this.canReply();
const canNavigateToSidebar = this.canNavigateToSidebar();
- if (linkModalActive) {
+ if (isLinkModalActive) {
swipeOptions = 'none';
} else if (canReply && canNavigateToSidebar) {
swipeOptions = 'both';
@@ -147,6 +147,7 @@
message,
props: { verticalBounds, linkIsBlockingPresses },
} = this;
+
if (!message || !verticalBounds || linkIsBlockingPresses) {
return;
}
@@ -208,33 +209,41 @@
function ConnectedTextMessage(props: BaseProps) {
const overlayContext = React.useContext(OverlayContext);
const chatContext = React.useContext(ChatContext);
+ const markdownContext = React.useContext(MarkdownContext);
+ invariant(markdownContext, 'markdownContext should be set');
+
+ const {
+ linkModalActive,
+ linkPressActive,
+ clearMarkdownContextData,
+ } = markdownContext;
+
+ const key = messageKey(props.item.messageInfo);
+
+ // We check if there is an ID in the respective objects - if not, we
+ // default to false. The likely situation where the former statement
+ // evaluates to null is when the thread is opened for the first time.
+ const linkIsBlockingPresses =
+ (linkModalActive[key] || linkPressActive[key]) ?? false;
+
+ const isLinkModalActive = linkModalActive[key] ?? false;
- const [linkModalActive, setLinkModalActive] = React.useState(false);
- const [linkPressActive, setLinkPressActive] = React.useState(false);
- const markdownContext = React.useMemo(
- () => ({
- setLinkModalActive,
- setLinkPressActive,
- }),
- [setLinkModalActive, setLinkPressActive],
- );
const canCreateSidebarFromMessage = useCanCreateSidebarFromMessage(
props.item.threadInfo,
props.item.messageInfo,
);
- const linkIsBlockingPresses = linkModalActive || linkPressActive;
+ React.useEffect(() => clearMarkdownContextData, [clearMarkdownContextData]);
+
return (
- <MarkdownContext.Provider value={markdownContext}>
- <TextMessage
- {...props}
- canCreateSidebarFromMessage={canCreateSidebarFromMessage}
- overlayContext={overlayContext}
- chatContext={chatContext}
- linkModalActive={linkModalActive}
- linkIsBlockingPresses={linkIsBlockingPresses}
- />
- </MarkdownContext.Provider>
+ <TextMessage
+ {...props}
+ canCreateSidebarFromMessage={canCreateSidebarFromMessage}
+ overlayContext={overlayContext}
+ chatContext={chatContext}
+ isLinkModalActive={isLinkModalActive}
+ linkIsBlockingPresses={linkIsBlockingPresses}
+ />
);
},
);
diff --git a/native/markdown/markdown-context-provider.react.js b/native/markdown/markdown-context-provider.react.js
new file mode 100644
--- /dev/null
+++ b/native/markdown/markdown-context-provider.react.js
@@ -0,0 +1,48 @@
+// @flow
+
+import * as React from 'react';
+
+import { MarkdownContext } from './markdown-context.js';
+
+type Props = {
+ +children: React.Node,
+};
+
+function MarkdownContextProvider(props: Props): React.Node {
+ const [linkModalActive, setLinkModalActive] = React.useState<{
+ [key: string]: boolean,
+ }>({});
+ const [linkPressActive, setLinkPressActive] = React.useState<{
+ [key: string]: boolean,
+ }>({});
+
+ const clearMarkdownContextData = React.useCallback(() => {
+ setLinkModalActive({});
+ setLinkPressActive({});
+ }, []);
+
+ const contextValue = React.useMemo(
+ () => ({
+ setLinkModalActive,
+ linkModalActive,
+ setLinkPressActive,
+ linkPressActive,
+ clearMarkdownContextData,
+ }),
+ [
+ setLinkModalActive,
+ linkModalActive,
+ setLinkPressActive,
+ linkPressActive,
+ clearMarkdownContextData,
+ ],
+ );
+
+ return (
+ <MarkdownContext.Provider value={contextValue}>
+ {props.children}
+ </MarkdownContext.Provider>
+ );
+}
+
+export default MarkdownContextProvider;
diff --git a/native/markdown/markdown-context.js b/native/markdown/markdown-context.js
--- a/native/markdown/markdown-context.js
+++ b/native/markdown/markdown-context.js
@@ -2,9 +2,14 @@
import * as React from 'react';
+import type { SetState } from 'lib/types/hook-types';
+
export type MarkdownContextType = {
- +setLinkModalActive: boolean => void,
- +setLinkPressActive: boolean => void,
+ +setLinkModalActive: SetState<{ [key: string]: boolean }>,
+ +linkModalActive: { [key: string]: boolean },
+ +setLinkPressActive: SetState<{ [key: string]: boolean }>,
+ +linkPressActive: { [key: string]: boolean },
+ +clearMarkdownContextData: () => void,
};
const MarkdownContext: React.Context<?MarkdownContextType> = React.createContext<?MarkdownContextType>(
diff --git a/native/markdown/markdown-link.react.js b/native/markdown/markdown-link.react.js
--- a/native/markdown/markdown-link.react.js
+++ b/native/markdown/markdown-link.react.js
@@ -5,16 +5,21 @@
import { normalizeURL } from 'lib/utils/url-utils';
+import { MessageContext } from '../chat/message-context.react';
import { MarkdownContext, type MarkdownContextType } from './markdown-context';
function useDisplayLinkPrompt(
inputURL: string,
markdownContext: ?MarkdownContextType,
+ messageKey: string,
) {
const setLinkModalActive = markdownContext?.setLinkModalActive;
+ const linkModalActive = markdownContext?.linkModalActive;
const onDismiss = React.useCallback(() => {
- setLinkModalActive?.(false);
- }, [setLinkModalActive]);
+ if (linkModalActive) {
+ setLinkModalActive?.({ ...linkModalActive, [messageKey]: false });
+ }
+ }, [setLinkModalActive, linkModalActive, messageKey]);
const url = normalizeURL(inputURL);
const onConfirm = React.useCallback(() => {
@@ -27,7 +32,9 @@
displayURL += '…';
}
return React.useCallback(() => {
- setLinkModalActive && setLinkModalActive(true);
+ setLinkModalActive &&
+ linkModalActive &&
+ setLinkModalActive({ ...linkModalActive, [messageKey]: true });
Alert.alert(
'External link',
`You sure you want to open this link?\n\n${displayURL}`,
@@ -37,7 +44,14 @@
],
{ cancelable: true, onDismiss },
);
- }, [setLinkModalActive, displayURL, onConfirm, onDismiss]);
+ }, [
+ setLinkModalActive,
+ linkModalActive,
+ messageKey,
+ displayURL,
+ onConfirm,
+ onDismiss,
+ ]);
}
type TextProps = React.ElementConfig<typeof Text>;
@@ -48,15 +62,22 @@
};
function MarkdownLink(props: Props): React.Node {
const markdownContext = React.useContext(MarkdownContext);
+ const messageContext = React.useContext(MessageContext);
+
+ const messageKey = messageContext?.messageKey;
const { target, ...rest } = props;
- const onPressLink = useDisplayLinkPrompt(target, markdownContext);
+ const onPressLink = useDisplayLinkPrompt(target, markdownContext, messageKey);
const setLinkPressActive = markdownContext?.setLinkPressActive;
+ const linkPressActive = markdownContext?.linkPressActive;
+
const androidOnStartShouldSetResponderCapture = React.useCallback(() => {
- setLinkPressActive?.(true);
+ if (linkPressActive) {
+ setLinkPressActive?.({ ...linkPressActive, [messageKey]: true });
+ }
return true;
- }, [setLinkPressActive]);
+ }, [setLinkPressActive, linkPressActive, messageKey]);
const activePressHasMoved = React.useRef(false);
const androidOnResponderMove = React.useCallback(() => {
@@ -68,8 +89,10 @@
onPressLink();
}
activePressHasMoved.current = false;
- setLinkPressActive?.(false);
- }, [onPressLink, setLinkPressActive]);
+ if (linkPressActive) {
+ setLinkPressActive?.({ ...linkPressActive, [messageKey]: false });
+ }
+ }, [onPressLink, setLinkPressActive, linkPressActive, messageKey]);
if (Platform.OS !== 'android') {
return <Text onPress={onPressLink} {...rest} />;
diff --git a/native/root.react.js b/native/root.react.js
--- a/native/root.react.js
+++ b/native/root.react.js
@@ -27,6 +27,7 @@
import ErrorBoundary from './error-boundary.react';
import InputStateContainer from './input/input-state-container.react';
import LifecycleHandler from './lifecycle/lifecycle-handler.react';
+import MarkdownContextProvider from './markdown/markdown-context-provider.react';
import { defaultNavigationState } from './navigation/default-state';
import DisconnectedBarVisibilityHandler from './navigation/disconnected-bar-visibility-handler.react';
import { setGlobalNavContext } from './navigation/icky-global';
@@ -248,23 +249,25 @@
<InputStateContainer>
<SafeAreaProvider initialMetrics={initialWindowMetrics}>
<ActionSheetProvider>
- <ChatContextProvider>
- <SQLiteContextProvider>
- <ConnectedStatusBar />
- <ReduxPersistGate persistor={getPersistor()}>
- {gated}
- </ReduxPersistGate>
- <PersistedStateGate>
- <Socket
- detectUnsupervisedBackgroundRef={
- detectUnsupervisedBackgroundRef
- }
- />
- </PersistedStateGate>
- {navigation}
- <NavigationHandler />
- </SQLiteContextProvider>
- </ChatContextProvider>
+ <MarkdownContextProvider>
+ <ChatContextProvider>
+ <SQLiteContextProvider>
+ <ConnectedStatusBar />
+ <ReduxPersistGate persistor={getPersistor()}>
+ {gated}
+ </ReduxPersistGate>
+ <PersistedStateGate>
+ <Socket
+ detectUnsupervisedBackgroundRef={
+ detectUnsupervisedBackgroundRef
+ }
+ />
+ </PersistedStateGate>
+ {navigation}
+ <NavigationHandler />
+ </SQLiteContextProvider>
+ </ChatContextProvider>
+ </MarkdownContextProvider>
</ActionSheetProvider>
</SafeAreaProvider>
</InputStateContainer>

File Metadata

Mime Type
text/plain
Expires
Thu, Nov 28, 2:19 AM (19 h, 56 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2592438
Default Alt Text
D5515.id18740.diff (13 KB)

Event Timeline