1import * as SMS from 'expo-sms';
2import React from 'react';
3import { Button, StyleSheet, Text, TextInput, View } from 'react-native';
4
5interface State {
6  phoneNumbers: string[];
7  message?: string;
8  error?: string;
9  result?: string;
10}
11
12// See: https://github.com/expo/expo/pull/10229#discussion_r490961694
13// eslint-disable-next-line @typescript-eslint/ban-types
14export default class SMSScreen extends React.Component<{}, State> {
15  static navigationOptions = {
16    title: 'SMS',
17  };
18
19  readonly state: State = {
20    phoneNumbers: [],
21  };
22
23  _sendSMS = async () => {
24    const isAvailable = await SMS.isAvailableAsync();
25    if (!isAvailable) {
26      this.setState({
27        error: 'SMS functionality is not available on this device!',
28      });
29      setTimeout(() => this.setState({ error: undefined }), 10000);
30      return;
31    }
32    try {
33      if (this.state.message) {
34        const { result } = await SMS.sendSMSAsync(this.state.phoneNumbers, this.state.message);
35        this.setState({ phoneNumbers: [], message: undefined, result });
36        setTimeout(() => this.setState({ result: undefined }), 5000);
37      }
38    } catch (e) {
39      this.setState({ error: e.message });
40      setTimeout(() => this.setState({ error: undefined }), 10000);
41    }
42  };
43
44  _sendSMSWithInvalidRecipient = async (address: null | undefined) => {
45    const isAvailable = await SMS.isAvailableAsync();
46    if (!isAvailable) {
47      this.setState({
48        error: 'SMS functionality is not available on this device!',
49      });
50      setTimeout(() => this.setState({ error: undefined }), 10000);
51      return;
52    }
53    try {
54      if (this.state.message) {
55        // @ts-ignore -- testing if addresses === null is handled
56        // expected behavior: exception is thrown
57        const { result } = await SMS.sendSMSAsync(address, this.state.message);
58        this.setState({ phoneNumbers: [], message: undefined, result });
59        setTimeout(() => this.setState({ result: undefined }), 5000);
60      }
61    } catch (e) {
62      this.setState({ error: e.message });
63      setTimeout(() => this.setState({ error: undefined }), 10000);
64    }
65  };
66
67  render() {
68    return (
69      <View style={styles.container}>
70        <TextInput
71          style={styles.phoneNumbers}
72          placeholder="Phone numbers, comma separated"
73          value={this.state.phoneNumbers.join(',')}
74          onChangeText={(phoneNumbers) =>
75            this.setState({
76              phoneNumbers: phoneNumbers.split(',').map((e) => e.trim()),
77            })
78          }
79        />
80        <TextInput
81          style={styles.message}
82          placeholder="Message"
83          value={this.state.message}
84          onChangeText={(message) => this.setState({ message })}
85        />
86        <Button title="Send" disabled={!this.state.message} onPress={this._sendSMS} />
87        <Button
88          title="Send message with null recipient"
89          disabled={!this.state.message}
90          onPress={() => this._sendSMSWithInvalidRecipient(undefined)}
91        />
92        <Button
93          title="Send message with undefined recipient"
94          disabled={!this.state.message}
95          onPress={() => this._sendSMSWithInvalidRecipient(null)}
96        />
97        {this.state.error && (
98          <View style={[styles.textView, styles.errorView]}>
99            <Text style={styles.errorText}>{this.state.error}</Text>
100          </View>
101        )}
102        {this.state.result && (
103          <View style={[styles.textView, styles.resultView]}>
104            <Text>{this.state.result}</Text>
105          </View>
106        )}
107      </View>
108    );
109  }
110}
111
112const styles = StyleSheet.create({
113  container: {
114    flex: 1,
115    marginBottom: 10,
116    padding: 10,
117  },
118  phoneNumbers: {
119    height: 40,
120  },
121  message: {
122    height: 40,
123  },
124  errorView: {
125    backgroundColor: 'red',
126  },
127  resultView: {
128    borderColor: 'blue',
129    borderWidth: 2,
130  },
131  textView: {
132    height: 60,
133    justifyContent: 'center',
134    alignItems: 'center',
135    padding: 20,
136    marginTop: 20,
137    marginBottom: 20,
138  },
139  errorText: {
140    color: 'white',
141  },
142});
143