From 133c7e0d6f7ce31763d90d8b6678d85ffd9763ee Mon Sep 17 00:00:00 2001 From: SilentCruzer Date: Sat, 22 Jan 2022 02:23:50 +0530 Subject: [PATCH] message_actions: Add move message option --- src/action-sheets/index.js | 9 ++ src/message/MoveMessage.js | 216 +++++++++++++++++++++++++++ src/nav/AppNavigator.js | 3 + src/nav/navActions.js | 11 +- static/translations/messages_en.json | 1 + 5 files changed, 239 insertions(+), 1 deletion(-) create mode 100644 src/message/MoveMessage.js diff --git a/src/action-sheets/index.js b/src/action-sheets/index.js index 3bbf31bb3a6..7d2717ac25f 100644 --- a/src/action-sheets/index.js +++ b/src/action-sheets/index.js @@ -35,6 +35,7 @@ import { doNarrow, deleteOutboxMessage, navigateToEmojiPicker, navigateToStream import { navigateToMessageReactionScreen, navigateToPmConversationDetails, + navigateToMoveMessage, } from '../nav/navActions'; import { deleteMessagesForTopic } from '../topics/topicActions'; import * as logging from '../utils/logging'; @@ -81,6 +82,7 @@ type MessageArgs = { dispatch: Dispatch, _: GetText, startEditMessage: (editMessage: EditMessage) => void, + narrow: Narrow, ... }; @@ -137,6 +139,12 @@ const editMessage = async ({ message, dispatch, startEditMessage, auth }) => { editMessage.title = 'Edit message'; editMessage.errorMessage = 'Failed to edit message'; +const moveMessage = async ({ message, dispatch, startEditMessage, auth, narrow }) => { + NavigationService.dispatch(navigateToMoveMessage(message, narrow)); +}; +moveMessage.title = 'Move message'; +moveMessage.errorMessage = 'Move message'; + const deleteMessage = async ({ auth, message, dispatch }) => { if (message.isOutbox) { dispatch(deleteOutboxMessage(message.timestamp)); @@ -449,6 +457,7 @@ export const constructMessageActionButtons = ({ && (isStreamOrTopicNarrow(narrow) || isPmNarrow(narrow)) ) { buttons.push(editMessage); + buttons.push(moveMessage); } if (message.sender_id === ownUser.user_id && messageNotDeleted(message)) { // TODO(#2793): Don't show if message isn't deletable. diff --git a/src/message/MoveMessage.js b/src/message/MoveMessage.js new file mode 100644 index 00000000000..0c69231eb95 --- /dev/null +++ b/src/message/MoveMessage.js @@ -0,0 +1,216 @@ +/* @flow strict-local */ +import React, { useState, useContext } from 'react'; +import { Text, View, Platform, Picker, TouchableOpacity, ScrollView } from 'react-native'; +import type { Node } from 'react'; +import Toast from 'react-native-simple-toast'; +import { ThemeContext, BRAND_COLOR } from '../styles'; +import type { RouteProp } from '../react-navigation'; +import * as api from '../api'; +import { Input } from '../common'; +import type { AppNavigationProp } from '../nav/AppNavigator'; +import { getAuth, getStreams, getOwnUser } from '../selectors'; +import { useSelector } from '../react-redux'; +import { showErrorAlert } from '../utils/info'; +import { Icon } from '../common/Icons'; +import type { Narrow, Message, Outbox } from '../types'; +import TopicAutocomplete from '../autocomplete/TopicAutocomplete'; +import { streamNameOfNarrow, streamNarrow } from '../utils/narrow'; + +type Props = $ReadOnly<{| + navigation: AppNavigationProp<'move-message'>, + route: RouteProp<'move-message', {| message: Message | Outbox, messageNarrow: Narrow |}>, +|}>; + +const inputMarginPadding = { + paddingHorizontal: 8, + paddingVertical: Platform.select({ + ios: 8, + android: 2, + }), +}; + +export default function MoveMessage(props: Props): Node { + const themeContext = useContext(ThemeContext); + const backgroundColor = themeContext.backgroundColor; + const cardColor = themeContext.cardColor; + const iconName = Platform.OS === 'android' ? 'arrow-left' : 'chevron-left'; + const auth = useSelector(getAuth); + const allStreams = useSelector(getStreams); + const isAdmin = useSelector(getOwnUser).is_admin; + const messageId = props.route.params.message.id; + const currentStreamName = streamNameOfNarrow(props.route.params.messageNarrow); + const currentStreamId = allStreams.find(s => s.name === currentStreamName)?.stream_id; + const [narrow, setNarrow] = useState(streamNarrow(currentStreamName)); + const [subject, setSubject] = useState(props.route.params.message.subject); + const [propagateMode, setPropagateMode] = useState('change_one'); + const [streamId, setStreamId] = useState(currentStreamId); + const [topicFocus, setTopicFocus] = useState(false); + + const styles = { + parent: { + backgroundColor: cardColor, + }, + layout: { + margin: 10, + }, + title: { + fontSize: 18, + color: backgroundColor === 'white' ? 'black' : 'white', + }, + topicInput: { + height: 50, + backgroundColor, + ...inputMarginPadding, + }, + viewTitle: { + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + height: 50, + paddingHorizontal: 10, + marginBottom: 20, + }, + textColor: { + color: backgroundColor === 'white' ? 'black' : 'white', + }, + picker: { backgroundColor, marginBottom: 20 }, + submitButton: { + marginTop: 10, + paddingTop: 15, + paddingBottom: 15, + marginLeft: 30, + marginRight: 30, + backgroundColor: BRAND_COLOR, + borderRadius: 10, + borderWidth: 1, + }, + }; + + const handleTopicChange = (topic: string) => { + setSubject(topic); + }; + + const handleTopicFocus = () => { + setTopicFocus(true); + }; + + const setTopicInputValue = (topic: string) => { + handleTopicChange(topic); + setTopicFocus(false); + }; + + const handleTopicAutocomplete = (topic: string) => { + setTopicInputValue(topic); + }; + + const updateMessage = () => { + if (isAdmin) { + api + .updateMessage( + auth, + { subject, stream_id: streamId, propagate_mode: propagateMode }, + messageId, + ) + .catch(error => { + showErrorAlert('Failed to move message', error.message); + props.navigation.goBack(); + }); + } else { + api + .updateMessage(auth, { subject, propagate_mode: propagateMode }, messageId) + .catch(error => { + showErrorAlert('Failed to move message', error.message); + props.navigation.goBack(); + }); + } + props.navigation.goBack(); + Toast.show('Moved Message'); + }; + + const handleNarrow = (pickedStreamId: number) => { + setStreamId(pickedStreamId); + const pickedStreamName = allStreams.find(s => s.stream_id === pickedStreamId)?.name; + setNarrow(streamNarrow(String(pickedStreamName))); + }; + + const handlePropagateMode = (propagatePickerIndex: number) => { + if (propagatePickerIndex === 2) { + setPropagateMode('change_all'); + } else if (propagatePickerIndex === 1) { + setPropagateMode('change_later'); + } else { + setPropagateMode('change_one'); + } + }; + + return ( + + + + props.navigation.goBack()}> + + + Move Message + + + Content: + + {props.route.params.message.content.replace(/<(?:.|\n)*?>/gm, '')} + + Stream: + {isAdmin ? ( + + handleNarrow(parseInt(itemValue, 10))} + style={styles.textColor} + > + {allStreams.map(item => ( + + ))} + + + ) : ( + {currentStreamName} + )} + Topic: + + handleTopicChange(value)} + onFocus={handleTopicFocus} + blurOnSubmit={false} + returnKeyType="next" + style={styles.topicInput} + /> + + + Move options: + + handlePropagateMode(itemIndex)} + style={styles.textColor} + > + + + + + + + Submit + + + + ); +} diff --git a/src/nav/AppNavigator.js b/src/nav/AppNavigator.js index b27deae261f..fca41da8b67 100644 --- a/src/nav/AppNavigator.js +++ b/src/nav/AppNavigator.js @@ -45,6 +45,7 @@ import SettingsScreen from '../settings/SettingsScreen'; import UserStatusScreen from '../user-status/UserStatusScreen'; import SharingScreen from '../sharing/SharingScreen'; import { useHaveServerDataGate } from '../withHaveServerDataGate'; +import moveMessage from '../message/MoveMessage'; export type AppNavigatorParamList = {| 'account-pick': RouteParamsOf, @@ -56,6 +57,7 @@ export type AppNavigatorParamList = {| 'emoji-picker': RouteParamsOf, 'main-tabs': RouteParamsOf, 'message-reactions': RouteParamsOf, + 'move-message': RouteParamsOf, 'password-auth': RouteParamsOf, 'realm-input': RouteParamsOf, 'search-messages': RouteParamsOf, @@ -124,6 +126,7 @@ export default function AppNavigator(props: Props): Node { name="message-reactions" component={useHaveServerDataGate(MessageReactionsScreen)} /> + StackActions.push('message-reactions', { messageId, reactionName }); +export const navigateToMoveMessage = ( + message: Message | Outbox, + messageNarrow: Narrow, +): GenericNavigationAction => + StackActions.push('move-message', { + message, + messageNarrow, + }); + export const navigateToLegal = (): GenericNavigationAction => StackActions.push('legal'); export const navigateToUserStatus = (): GenericNavigationAction => StackActions.push('user-status'); diff --git a/static/translations/messages_en.json b/static/translations/messages_en.json index 79e195dd0d8..db96622961c 100644 --- a/static/translations/messages_en.json +++ b/static/translations/messages_en.json @@ -191,6 +191,7 @@ "Cancel": "Cancel", "Message copied": "Message copied", "Edit message": "Edit message", + "Move message": "Move message", "Network request failed": "Network request failed", "Failed to add reaction": "Failed to add reaction", "Failed to reply": "Failed to reply",