Skip to content

Commit

Permalink
messageActionSheet : using Modal instead of `@expo/react-native-act…
Browse files Browse the repository at this point in the history
…ion-sheet`

when we long press on any message then `messageActionSheet` opens up and it
took a extra black screen on `Share` option when we share our message and
this issue is coming from `@expo/react-native-action-sheet` also
`@expo/react-native-action-sheet` restricts us upto a certain extent on basis
of UI for eg if we use `Modal` instead of `@expo/react-native-action-sheet`
then we have many options for eg colored messageActionSheet , popping out with
diff animations , can set some icons before any option which and greg have
already discussed in #mobile on CZO.

fix : tap above the top of the action sheet to make it disappear

using `touchableOpacity` outside the `modal` so that if we click
outside the `messageActionSheet` then it dismiss the `modal`
  • Loading branch information
abhi0504 committed Feb 24, 2021
1 parent c7d3735 commit 55c0746
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 63 deletions.
77 changes: 41 additions & 36 deletions src/chat/ChatScreen.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
/* @flow strict-local */
import React from 'react';
import React, { useState } from 'react';
import { View } from 'react-native';
import { useIsFocused } from '@react-navigation/native';
import { ActionSheetProvider } from '@expo/react-native-action-sheet';

import { useSelector, useDispatch } from '../react-redux';
import type { RouteProp } from '../react-navigation';
Expand All @@ -23,6 +22,7 @@ import { getLoading, getSession } from '../directSelectors';
import { getFetchingForNarrow } from './fetchingSelectors';
import { getShownMessagesForNarrow, isNarrowValid as getIsNarrowValid } from './narrowsSelectors';
import { getStreamColorForNarrow } from '../subscriptions/subscriptionSelectors';
import { ActionSheetModalHandler } from '../message/messageActionSheet';

type Props = $ReadOnly<{|
navigation: AppNavigationProp<'chat'>,
Expand Down Expand Up @@ -113,43 +113,48 @@ export default function ChatScreen(props: Props) {
const showMessagePlaceholders = haveNoMessages && isFetching;
const sayNoMessages = haveNoMessages && !isFetching;
const showComposeBox = canSendToNarrow(narrow) && !showMessagePlaceholders;
const [modalVisible, setModalVisible] = useState(false);

const streamColor = useSelector(state => getStreamColorForNarrow(state, narrow));

const longPressModalHandler = () => {
setModalVisible(!modalVisible);
};

return (
<ActionSheetProvider>
<View style={[componentStyles.screen, { backgroundColor }]}>
<KeyboardAvoider style={styles.flexed} behavior="padding">
<ZulipStatusBar backgroundColor={streamColor} />
<ChatNavBar narrow={narrow} editMessage={editMessage} />
<OfflineNotice />
<UnreadNotice narrow={narrow} />
{(() => {
if (!isNarrowValid) {
return <InvalidNarrow narrow={narrow} />;
} else if (fetchError !== null) {
return <FetchError narrow={narrow} error={fetchError} />;
} else if (sayNoMessages) {
return <NoMessages narrow={narrow} />;
} else {
return (
<MessageList
narrow={narrow}
showMessagePlaceholders={showMessagePlaceholders}
startEditMessage={setEditMessage}
/>
);
}
})()}
{showComposeBox && (
<ComposeBox
narrow={narrow}
editMessage={editMessage}
completeEditMessage={() => setEditMessage(null)}
/>
)}
</KeyboardAvoider>
</View>
</ActionSheetProvider>
<View style={[componentStyles.screen, { backgroundColor }]}>
<KeyboardAvoider style={styles.flexed} behavior="padding">
<ZulipStatusBar backgroundColor={streamColor} />
<ChatNavBar narrow={narrow} editMessage={editMessage} />
<OfflineNotice />
<UnreadNotice narrow={narrow} />
{(() => {
if (!isNarrowValid) {
return <InvalidNarrow narrow={narrow} />;
} else if (fetchError !== null) {
return <FetchError narrow={narrow} error={fetchError} />;
} else if (sayNoMessages) {
return <NoMessages narrow={narrow} />;
} else {
return (
<MessageList
narrow={narrow}
showMessagePlaceholders={showMessagePlaceholders}
startEditMessage={setEditMessage}
handleLongPressModal={longPressModalHandler}
/>
);
}
})()}
{showComposeBox && (
<ComposeBox
narrow={narrow}
editMessage={editMessage}
completeEditMessage={() => setEditMessage(null)}
/>
)}
<ActionSheetModalHandler modalVisible={modalVisible} modalHandler={longPressModalHandler} />
</KeyboardAvoider>
</View>
);
}
111 changes: 85 additions & 26 deletions src/message/messageActionSheet.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
/* @flow strict-local */
import invariant from 'invariant';
import { Clipboard, Share, Alert } from 'react-native';
import React from 'react';
import {
Alert,
Modal,
StyleSheet,
Text,
TouchableOpacity,
View,
Share,
Clipboard,
FlatList,
} from 'react-native';

import * as NavigationService from '../nav/NavigationService';
import type {
Expand All @@ -26,7 +37,7 @@ import * as api from '../api';
import { showToast } from '../utils/info';
import { doNarrow, deleteOutboxMessage, navigateToEmojiPicker } from '../actions';
import { navigateToMessageReactionScreen } from '../nav/navActions';
import { pmUiRecipientsFromMessage, streamNameOfStreamMessage } from '../utils/recipient';
import { streamNameOfStreamMessage } from '../utils/recipient';
import { deleteMessagesForTopic } from '../topics/topicActions';
import * as logging from '../utils/logging';

Expand Down Expand Up @@ -56,6 +67,11 @@ type ButtonDescription = {
errorMessage: string,
};

type Props = {
modalVisible: boolean,
modalHandler: () => void,
};

//
// Options for the action sheet go below: ...
//
Expand Down Expand Up @@ -337,17 +353,69 @@ export const constructNonHeaderActionButtons = ({
}
};

/** Returns the title for the action sheet. */
const getActionSheetTitle = (message: Message | Outbox, ownUser: User): string => {
if (message.type === 'private') {
const recipients = pmUiRecipientsFromMessage(message, ownUser.user_id);
return recipients
.map(r => r.full_name)
.sort()
.join(', ');
} else {
return `#${streamNameOfStreamMessage(message)} > ${message.subject}`;
}
let finalOptions = [];
let callback = index => {};

const styles = StyleSheet.create({
centeredView: {
position: 'absolute',
bottom: 0,
width: '100%',
backgroundColor: 'white',
},
modalView: {
backgroundColor: 'white',
padding: 20,
},
item: {
padding: 10,
fontSize: 15,
height: 50,
fontWeight: '800',
},
});

export const ActionSheetModalHandler = (props: Props) => {
const renderItem = ({ item, index }) => <Item title={item} index={index} />;

const Item = ({ title, index }) => (
<TouchableOpacity
onPress={() => {
props.modalHandler();
callback(index);
}}
>
<Text style={styles.item}>{title}</Text>
</TouchableOpacity>
);

const isModalTransparent = true;

return (
<View>
<Modal
animationType="slide"
transparent={isModalTransparent}
visible={props.modalVisible}
onRequestClose={() => {
props.modalHandler();
}}
>
<TouchableOpacity
style={{ flex: 1 }}
onPress={() => {
props.modalHandler();
}}
>
<View style={styles.centeredView}>
<View style={styles.modalView}>
<FlatList data={finalOptions} renderItem={renderItem} />
</View>
</View>
</TouchableOpacity>
</Modal>
</View>
);
};

/** Invoke the given callback to show an appropriate action sheet. */
Expand All @@ -360,11 +428,12 @@ export const showActionSheet = (
_: GetText,
|},
params: ConstructSheetParams<>,
handleLongPressModal: () => {},
): void => {
const optionCodes = isHeader
? constructHeaderActionButtons(params)
: constructNonHeaderActionButtons(params);
const callback = buttonIndex => {
callback = buttonIndex => {
(async () => {
const pressedButton: ButtonDescription = allButtons[optionCodes[buttonIndex]];
try {
Expand All @@ -380,16 +449,6 @@ export const showActionSheet = (
}
})();
};
showActionSheetWithOptions(
{
...(isHeader
? {
title: getActionSheetTitle(params.message, params.backgroundData.ownUser),
}
: {}),
options: optionCodes.map(code => callbacks._(allButtons[code].title)),
cancelButtonIndex: optionCodes.length - 1,
},
callback,
);
finalOptions = optionCodes.map(code => callbacks._(allButtons[code].title));
handleLongPressModal();
};
1 change: 1 addition & 0 deletions src/webview/MessageList.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export type Props = $ReadOnly<{|
narrow: Narrow,
showMessagePlaceholders: boolean,
startEditMessage: (editMessage: EditMessage) => void,
handleLongPressModal: () => {},

dispatch: Dispatch,
...SelectorProps,
Expand Down
1 change: 1 addition & 0 deletions src/webview/__tests__/generateInboundEvents-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ describe('generateInboundEvents', () => {
dispatch: jest.fn(),
...baseSelectorProps,
showActionSheetWithOptions: jest.fn(),
handleLongPressModal: jest.fn(),

_: jest.fn(),
});
Expand Down
11 changes: 10 additions & 1 deletion src/webview/handleOutboundEvents.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ type Props = $ReadOnly<{
narrow: Narrow,
showActionSheetWithOptions: ShowActionSheetWithOptions,
startEditMessage: (editMessage: EditMessage) => void,
handleLongPressModal: () => {},
}>;

const fetchMore = (props: Props, event: WebViewOutboundEventScroll) => {
Expand Down Expand Up @@ -206,12 +207,20 @@ const handleLongPress = (
if (!message) {
return;
}
const { dispatch, showActionSheetWithOptions, backgroundData, narrow, startEditMessage } = props;
const {
dispatch,
showActionSheetWithOptions,
backgroundData,
narrow,
startEditMessage,
handleLongPressModal,
} = props;
showActionSheet(
target === 'header',
showActionSheetWithOptions,
{ dispatch, startEditMessage, _ },
{ backgroundData, message, narrow },
handleLongPressModal,
);
};

Expand Down

0 comments on commit 55c0746

Please sign in to comment.