Page MenuHomePhabricator

D5524.id18062.diff
No OneTemporary

D5524.id18062.diff

diff --git a/native/chat/text-message-tooltip-modal.react.js b/native/chat/text-message-tooltip-modal.react.js
--- a/native/chat/text-message-tooltip-modal.react.js
+++ b/native/chat/text-message-tooltip-modal.react.js
@@ -46,18 +46,18 @@
const spec = {
entries: [
- { id: 'copy', text: 'Copy', onPress: onPressCopy },
{ id: 'reply', text: 'Reply', onPress: onPressReply },
- {
- id: 'report',
- text: 'Report',
- onPress: onPressReport,
- },
{
id: 'sidebar',
text: 'Thread',
onPress: navigateToSidebar,
},
+ { id: 'copy', text: 'Copy', onPress: onPressCopy },
+ {
+ id: 'report',
+ text: 'Report',
+ onPress: onPressReport,
+ },
],
};
diff --git a/native/navigation/tooltip.react.js b/native/navigation/tooltip.react.js
--- a/native/navigation/tooltip.react.js
+++ b/native/navigation/tooltip.react.js
@@ -1,5 +1,9 @@
// @flow
+import {
+ useActionSheet,
+ type ShowActionSheetWithOptions,
+} from '@expo/react-native-action-sheet';
import type { RouteProp } from '@react-navigation/native';
import * as Haptics from 'expo-haptics';
import invariant from 'invariant';
@@ -10,6 +14,7 @@
TouchableWithoutFeedback,
Platform,
TouchableOpacity,
+ Keyboard,
} from 'react-native';
import Animated from 'react-native-reanimated';
import { useDispatch } from 'react-redux';
@@ -111,7 +116,12 @@
// withInputState
+inputState: ?InputState,
+chatContext: ?ChatContextType,
+ +showActionSheetWithOptions: ShowActionSheetWithOptions,
+};
+type State = {
+ +actionSheetShown: boolean,
};
+
function createTooltip<
RouteName: $Keys<TooltipModalParamList>,
BaseTooltipPropsType: BaseTooltipProps<RouteName> = BaseTooltipProps<RouteName>,
@@ -138,6 +148,10 @@
size={16}
/>
);
+ } else if (this.props.spec.id === 'more') {
+ icon = (
+ <SWMansionIcon name="menu-vertical" style={styles.icon} size={16} />
+ );
}
return (
@@ -159,6 +173,7 @@
}
class Tooltip extends React.PureComponent<
TooltipProps<BaseTooltipPropsType>,
+ State,
> {
backdropOpacity: Node;
tooltipContainerOpacity: Node;
@@ -171,6 +186,10 @@
constructor(props: TooltipProps<BaseTooltipPropsType>) {
super(props);
+ this.state = {
+ actionSheetShown: false,
+ };
+
const { overlayContext } = props;
invariant(overlayContext, 'Tooltip should have OverlayContext');
const { position } = overlayContext;
@@ -363,6 +382,7 @@
overlayContext,
inputState,
chatContext,
+ showActionSheetWithOptions,
...navAndRouteForFlow
} = this.props;
@@ -391,6 +411,27 @@
);
});
+ if (this.location === 'fixed' && entries.length > 3) {
+ items.splice(3);
+
+ const moreSpec = {
+ id: 'more',
+ text: 'More',
+ onPress: this.onPressMore,
+ };
+
+ const moreTooltipItem = (
+ <TooltipItem
+ key={entries.length}
+ spec={moreSpec}
+ onPress={moreSpec.onPress}
+ containerStyle={tooltipContainerStyle}
+ />
+ );
+
+ items.push(moreTooltipItem);
+ }
+
let triangleStyle;
const { route } = this.props;
const { initialCoordinates } = route.params;
@@ -434,6 +475,11 @@
itemsStyle.push(styles.itemsFixed);
}
+ let tooltip = <View style={itemsStyle}>{items}</View>;
+ if (this.state.actionSheetShown) {
+ tooltip = null;
+ }
+
return (
<TouchableWithoutFeedback onPress={this.onPressBackdrop}>
<View style={styles.container}>
@@ -448,7 +494,7 @@
onLayout={this.onTooltipContainerLayout}
>
{triangleUp}
- <View style={itemsStyle}>{items}</View>
+ {tooltip}
{triangleDown}
</AnimatedView>
</View>
@@ -477,6 +523,86 @@
);
};
+ onPressMore = () => {
+ Keyboard.dismiss();
+ this.setState({ actionSheetShown: true });
+
+ const { entries } = this;
+ const options = entries.map(entry => entry.text);
+
+ const {
+ destructiveButtonIndex,
+ cancelButtonIndex,
+ } = this.handlePlatformSpecificActionSheetOptions(options);
+
+ // reversing because we want the order of the normal tooltip,
+ // of left to right to now be from bottom to up
+ options.reverse();
+
+ const containerStyle = {
+ paddingBottom: 24,
+ };
+
+ const icons = [
+ <SWMansionIcon
+ key="report"
+ name="warning-circle"
+ style={styles.bottomSheetIcon}
+ size={16}
+ />,
+ <SWMansionIcon
+ key="copy"
+ name="copy"
+ style={styles.bottomSheetIcon}
+ size={16}
+ />,
+ <SWMansionIcon
+ key="thread"
+ name="message-circle-lines"
+ style={styles.bottomSheetIcon}
+ size={16}
+ />,
+ <CommIcon
+ key="reply"
+ name="reply"
+ style={styles.bottomSheetIcon}
+ size={12}
+ />,
+ ];
+
+ this.props.showActionSheetWithOptions(
+ {
+ options,
+ cancelButtonIndex,
+ destructiveButtonIndex,
+ containerStyle,
+ icons,
+ },
+ (selectedIndex?: number) => {
+ if (selectedIndex === cancelButtonIndex) {
+ this.props.navigation.goBackOnce();
+ return;
+ }
+ const index = entries.length - (selectedIndex ?? 0);
+ const entry = entries[Platform.OS === 'ios' ? index : index - 1];
+ this.onPressEntry(entry);
+ },
+ );
+ };
+
+ // A cancel action button and colors with the action text are almost
+ // always iOS specific
+ handlePlatformSpecificActionSheetOptions = (options: Array<string>) => {
+ const destructiveButtonIndex = Platform.OS === 'ios' ? 1 : undefined;
+ const cancelButtonIndex = Platform.OS === 'ios' ? 0 : -1;
+
+ if (Platform.OS === 'ios') {
+ options.push('Cancel');
+ }
+
+ return { destructiveButtonIndex, cancelButtonIndex };
+ };
+
bindServerCall = <F>(serverCall: ActionFunc<F>): F => {
const {
cookie,
@@ -515,6 +641,8 @@
return React.memo<BaseTooltipPropsType>(function ConnectedTooltip(
props: BaseTooltipPropsType,
) {
+ const { showActionSheetWithOptions } = useActionSheet();
+
const dimensions = useSelector(state => state.dimensions);
const serverCallState = useSelector(serverCallStateSelector);
const viewerID = useSelector(
@@ -536,6 +664,7 @@
overlayContext={overlayContext}
inputState={inputState}
chatContext={chatContext}
+ showActionSheetWithOptions={showActionSheetWithOptions}
/>
);
});
@@ -550,6 +679,9 @@
right: 0,
top: 0,
},
+ bottomSheetIcon: {
+ color: '#000000',
+ },
container: {
flex: 1,
},
diff --git a/native/root.react.js b/native/root.react.js
--- a/native/root.react.js
+++ b/native/root.react.js
@@ -1,5 +1,6 @@
// @flow
+import { ActionSheetProvider } from '@expo/react-native-action-sheet';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useReduxDevToolsExtension } from '@react-navigation/devtools';
import { NavigationContainer } from '@react-navigation/native';
@@ -248,23 +249,25 @@
<RootContext.Provider value={rootContext}>
<InputStateContainer>
<SafeAreaProvider initialMetrics={initialWindowMetrics}>
- <ChatContextProvider>
- <SQLiteContextProvider>
- <ConnectedStatusBar />
- <ReduxPersistGate persistor={getPersistor()}>
- {gated}
- </ReduxPersistGate>
- <PersistedStateGate>
- <Socket
- detectUnsupervisedBackgroundRef={
- detectUnsupervisedBackgroundRef
- }
- />
- </PersistedStateGate>
- {navigation}
- <NavigationHandler />
- </SQLiteContextProvider>
- </ChatContextProvider>
+ <ActionSheetProvider>
+ <ChatContextProvider>
+ <SQLiteContextProvider>
+ <ConnectedStatusBar />
+ <ReduxPersistGate persistor={getPersistor()}>
+ {gated}
+ </ReduxPersistGate>
+ <PersistedStateGate>
+ <Socket
+ detectUnsupervisedBackgroundRef={
+ detectUnsupervisedBackgroundRef
+ }
+ />
+ </PersistedStateGate>
+ {navigation}
+ <NavigationHandler />
+ </SQLiteContextProvider>
+ </ChatContextProvider>
+ </ActionSheetProvider>
</SafeAreaProvider>
</InputStateContainer>
</RootContext.Provider>

File Metadata

Mime Type
text/plain
Expires
Fri, Nov 22, 5:15 AM (19 h, 7 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2560066
Default Alt Text
D5524.id18062.diff (9 KB)

Event Timeline