|
| 1 | +/* @flow strict-local */ |
| 2 | +import React from 'react'; |
| 3 | +import { View, StyleSheet, Image, ScrollView, Modal, BackHandler } from 'react-native'; |
| 4 | +import { type NavigationNavigatorProps } from 'react-navigation'; |
| 5 | +import type { Dispatch, SharedData, User, Auth, GetText } from '../types'; |
| 6 | +import { TranslationContext } from '../boot/TranslationProvider'; |
| 7 | + |
| 8 | +import { connect } from '../react-redux'; |
| 9 | +import { ZulipButton, Input, Label } from '../common'; |
| 10 | +import UserItem from '../users/UserItem'; |
| 11 | +import { sendMessage, uploadFile } from '../api'; |
| 12 | +import { getAuth } from '../selectors'; |
| 13 | +import { showToast } from '../utils/info'; |
| 14 | +import { navigateBack } from '../nav/navActions'; |
| 15 | +import ChooseRecipientsScreen from './ChooseRecipientsScreen'; |
| 16 | + |
| 17 | +const styles = StyleSheet.create({ |
| 18 | + wrapper: { |
| 19 | + flex: 1, |
| 20 | + padding: 10, |
| 21 | + }, |
| 22 | + imagePreview: { |
| 23 | + margin: 10, |
| 24 | + borderRadius: 5, |
| 25 | + width: 200, |
| 26 | + height: 200, |
| 27 | + }, |
| 28 | + container: { |
| 29 | + flex: 1, |
| 30 | + }, |
| 31 | + actions: { |
| 32 | + flexDirection: 'row', |
| 33 | + }, |
| 34 | + button: { |
| 35 | + flex: 1, |
| 36 | + margin: 8, |
| 37 | + }, |
| 38 | + usersPreview: { |
| 39 | + padding: 10, |
| 40 | + }, |
| 41 | + chooseButton: { |
| 42 | + marginTop: 8, |
| 43 | + marginBottom: 8, |
| 44 | + width: '50%', |
| 45 | + alignSelf: 'flex-end', |
| 46 | + }, |
| 47 | + message: { |
| 48 | + height: 70, |
| 49 | + borderRadius: 5, |
| 50 | + borderWidth: 1, |
| 51 | + borderColor: 'whitesmoke', |
| 52 | + padding: 5, |
| 53 | + }, |
| 54 | +}); |
| 55 | + |
| 56 | +type Props = $ReadOnly<{| |
| 57 | + ...$Exact<NavigationNavigatorProps<{||}, {| params: {| sharedData: SharedData |} |}>>, |
| 58 | + dispatch: Dispatch, |
| 59 | + auth: Auth, |
| 60 | +|}>; |
| 61 | + |
| 62 | +type State = $ReadOnly<{| |
| 63 | + selectedRecipients: User[], |
| 64 | + message: string, |
| 65 | + choosingRecipients: boolean, |
| 66 | + sending: boolean, |
| 67 | +|}>; |
| 68 | + |
| 69 | +class ShareToPm extends React.Component<Props, State> { |
| 70 | + static contextType = TranslationContext; |
| 71 | + context: GetText; |
| 72 | + |
| 73 | + constructor(props) { |
| 74 | + super(props); |
| 75 | + const { sharedData } = this.props.navigation.state.params; |
| 76 | + this.state = { |
| 77 | + selectedRecipients: [], |
| 78 | + message: sharedData.type === 'text' ? sharedData.sharedText : '', |
| 79 | + choosingRecipients: false, |
| 80 | + sending: false, |
| 81 | + }; |
| 82 | + } |
| 83 | + |
| 84 | + setSending = () => { |
| 85 | + this.setState({ sending: true }); |
| 86 | + }; |
| 87 | + |
| 88 | + handleChooseRecipients = (selectedRecipients: Array<User>) => { |
| 89 | + this.setState({ selectedRecipients }); |
| 90 | + this.setState({ choosingRecipients: false }); |
| 91 | + }; |
| 92 | + |
| 93 | + handleSend = async () => { |
| 94 | + this.setSending(); |
| 95 | + |
| 96 | + const _ = this.context; |
| 97 | + const { selectedRecipients, message } = this.state; |
| 98 | + let messageToSend = message; |
| 99 | + const { auth } = this.props; |
| 100 | + const { sharedData } = this.props.navigation.state.params; |
| 101 | + const to = JSON.stringify(selectedRecipients.map(user => user.user_id)); |
| 102 | + |
| 103 | + try { |
| 104 | + showToast(_('Sending Message...')); |
| 105 | + |
| 106 | + if (sharedData.type === 'image' || sharedData.type === 'file') { |
| 107 | + const url = |
| 108 | + sharedData.type === 'image' ? sharedData.sharedImageUrl : sharedData.sharedFileUrl; |
| 109 | + const fileName = url.split('/').pop(); |
| 110 | + const response = await uploadFile(auth, url, fileName); |
| 111 | + messageToSend += `\n[${fileName}](${response.uri})`; |
| 112 | + } |
| 113 | + await sendMessage(auth, { content: messageToSend, type: 'private', to }); |
| 114 | + } catch (err) { |
| 115 | + showToast(_('Failed to send message')); |
| 116 | + this.finishShare(); |
| 117 | + return; |
| 118 | + } |
| 119 | + |
| 120 | + showToast(_('Message sent')); |
| 121 | + this.finishShare(); |
| 122 | + }; |
| 123 | + |
| 124 | + finishShare = () => { |
| 125 | + const { dispatch } = this.props; |
| 126 | + |
| 127 | + dispatch(navigateBack()); |
| 128 | + BackHandler.exitApp(); |
| 129 | + }; |
| 130 | + |
| 131 | + handleMessageChange = message => { |
| 132 | + this.setState({ message }); |
| 133 | + }; |
| 134 | + |
| 135 | + isSendButtonEnabled = () => { |
| 136 | + const { message, selectedRecipients } = this.state; |
| 137 | + const { sharedData } = this.props.navigation.state.params; |
| 138 | + |
| 139 | + if (sharedData.type === 'text') { |
| 140 | + return message !== '' && selectedRecipients.length > 0; |
| 141 | + } |
| 142 | + |
| 143 | + return selectedRecipients.length > 0; |
| 144 | + }; |
| 145 | + |
| 146 | + renderUsersPreview = () => { |
| 147 | + const { selectedRecipients } = this.state; |
| 148 | + |
| 149 | + if (selectedRecipients.length === 0) { |
| 150 | + return <Label text="Please choose recipients to share with" />; |
| 151 | + } |
| 152 | + const preview = []; |
| 153 | + selectedRecipients.forEach((user: User) => { |
| 154 | + preview.push( |
| 155 | + <UserItem |
| 156 | + avatarUrl={user.avatar_url} |
| 157 | + email={user.email} |
| 158 | + fullName={user.full_name} |
| 159 | + onPress={() => {}} |
| 160 | + key={user.user_id} |
| 161 | + />, |
| 162 | + ); |
| 163 | + }); |
| 164 | + return preview; |
| 165 | + }; |
| 166 | + |
| 167 | + render() { |
| 168 | + const { message, choosingRecipients, sending } = this.state; |
| 169 | + |
| 170 | + if (choosingRecipients) { |
| 171 | + return ( |
| 172 | + <Modal> |
| 173 | + <ChooseRecipientsScreen onComplete={this.handleChooseRecipients} /> |
| 174 | + </Modal> |
| 175 | + ); |
| 176 | + } |
| 177 | + |
| 178 | + const { sharedData } = this.props.navigation.state.params; |
| 179 | + let sharePreview = null; |
| 180 | + if (sharedData.type === 'image') { |
| 181 | + sharePreview = ( |
| 182 | + <Image |
| 183 | + source={{ uri: sharedData.sharedImageUrl }} |
| 184 | + width={200} |
| 185 | + height={200} |
| 186 | + style={styles.imagePreview} |
| 187 | + /> |
| 188 | + ); |
| 189 | + } |
| 190 | + |
| 191 | + return ( |
| 192 | + <> |
| 193 | + <ScrollView style={styles.wrapper} keyboardShouldPersistTaps="always"> |
| 194 | + <View style={styles.container}>{sharePreview}</View> |
| 195 | + <View style={styles.usersPreview}>{this.renderUsersPreview()}</View> |
| 196 | + <ZulipButton |
| 197 | + onPress={() => this.setState({ choosingRecipients: true })} |
| 198 | + style={styles.chooseButton} |
| 199 | + text="Choose recipients" |
| 200 | + /> |
| 201 | + <Input |
| 202 | + value={message} |
| 203 | + placeholder="Message" |
| 204 | + onChangeText={this.handleMessageChange} |
| 205 | + multiline |
| 206 | + /> |
| 207 | + </ScrollView> |
| 208 | + <View style={styles.actions}> |
| 209 | + <ZulipButton onPress={this.finishShare} style={styles.button} secondary text="Cancel" /> |
| 210 | + <ZulipButton |
| 211 | + style={styles.button} |
| 212 | + onPress={this.handleSend} |
| 213 | + text="Send" |
| 214 | + progress={sending} |
| 215 | + disabled={!this.isSendButtonEnabled()} |
| 216 | + /> |
| 217 | + </View> |
| 218 | + </> |
| 219 | + ); |
| 220 | + } |
| 221 | +} |
| 222 | + |
| 223 | +export default connect(state => ({ |
| 224 | + auth: getAuth(state), |
| 225 | +}))(ShareToPm); |
0 commit comments