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          Send SMS
88        </Button>
89        <Button
90          title="Send message with null recipient"
91          disabled={!this.state.message}
92          onPress={() => this._sendSMSWithInvalidRecipient(undefined)}>
93          Send SMS
94        </Button>
95        <Button
96          title="Send message with undefined recipient"
97          disabled={!this.state.message}
98          onPress={() => this._sendSMSWithInvalidRecipient(null)}>
99          Send SMS
100        </Button>
101        {this.state.error && (
102          <View style={[styles.textView, styles.errorView]}>
103            <Text style={styles.errorText}>{this.state.error}</Text>
104          </View>
105        )}
106        {this.state.result && (
107          <View style={[styles.textView, styles.resultView]}>
108            <Text>{this.state.result}</Text>
109          </View>
110        )}
111      </View>
112    );
113  }
114}
115
116const styles = StyleSheet.create({
117  container: {
118    flex: 1,
119    marginBottom: 10,
120    padding: 10,
121  },
122  phoneNumbers: {
123    height: 40,
124  },
125  message: {
126    height: 40,
127  },
128  errorView: {
129    backgroundColor: 'red',
130  },
131  resultView: {
132    borderColor: 'blue',
133    borderWidth: 2,
134  },
135  textView: {
136    height: 60,
137    justifyContent: 'center',
138    alignItems: 'center',
139    padding: 20,
140    marginTop: 20,
141    marginBottom: 20,
142  },
143  errorText: {
144    color: 'white',
145  },
146});
147