1import * as Haptics from 'expo-haptics';
2import React from 'react';
3import { SectionList, SectionListData, StyleSheet, Text, View, ViewStyle } from 'react-native';
4
5import Button from '../components/Button';
6import MonoText from '../components/MonoText';
7import Colors from '../constants/Colors';
8
9type SectionData =
10  // See: https://github.com/expo/expo/pull/10229#discussion_r490961694
11  // eslint-disable-next-line @typescript-eslint/ban-types
12  | {}
13  | {
14      accessor: string;
15      value: any;
16    };
17
18const sections: SectionListData<SectionData>[] = [
19  {
20    methodName: 'notificationAsync',
21    method: Haptics.notificationAsync,
22    data: [
23      {
24        accessor: 'Haptics.NotificationFeedbackType.Success',
25        value: Haptics.NotificationFeedbackType.Success,
26      },
27      {
28        accessor: 'Haptics.NotificationFeedbackType.Warning',
29        value: Haptics.NotificationFeedbackType.Warning,
30      },
31      {
32        accessor: 'Haptics.NotificationFeedbackType.Error',
33        value: Haptics.NotificationFeedbackType.Error,
34      },
35    ],
36  },
37  {
38    methodName: 'impactAsync',
39    method: Haptics.impactAsync,
40    data: [
41      {
42        accessor: 'Haptics.ImpactFeedbackStyle.Light',
43        value: Haptics.ImpactFeedbackStyle.Light,
44      },
45      {
46        accessor: 'Haptics.ImpactFeedbackStyle.Medium',
47        value: Haptics.ImpactFeedbackStyle.Medium,
48      },
49      {
50        accessor: 'Haptics.ImpactFeedbackStyle.Heavy',
51        value: Haptics.ImpactFeedbackStyle.Heavy,
52      },
53    ],
54  },
55  {
56    methodName: 'selectionAsync',
57    method: Haptics.selectionAsync,
58    data: [{}],
59  },
60];
61
62export default class HapticsScreen extends React.Component {
63  static navigationOptions = {
64    title: 'Haptics Feedback',
65  };
66
67  renderItem = ({
68    item,
69    section: { method },
70  }: {
71    item: { accessor: string; value: any };
72    section: { method: (type: string) => void };
73  }) => <Item method={method} type={item} />;
74
75  renderSectionHeader = ({ section: { methodName } }: { section: { methodName: string } }) => (
76    <Header title={methodName} />
77  );
78
79  keyExtractor = (data: SectionData) => {
80    if ('accessor' in data && 'value' in data) {
81      return `key-${data.accessor}-${data.value}`;
82    }
83    return 'key-undefined';
84  };
85
86  render() {
87    return (
88      <View style={styles.container}>
89        <SectionList<SectionData>
90          style={styles.list}
91          sections={sections}
92          renderItem={this.renderItem as any}
93          renderSectionHeader={this.renderSectionHeader as any}
94          keyExtractor={this.keyExtractor}
95        />
96      </View>
97    );
98  }
99}
100
101class Item extends React.Component<{
102  method: (type: string) => void;
103  type: { accessor: string; value: any };
104}> {
105  get code() {
106    const {
107      method,
108      type: { accessor },
109    } = this.props;
110    return `Haptics.${method.name}(${accessor || ''})`;
111  }
112  render() {
113    const {
114      method,
115      type: { value },
116    } = this.props;
117
118    return (
119      <View style={styles.itemContainer}>
120        <HapticButton style={styles.button} method={method} type={value} />
121        <MonoText containerStyle={styles.itemText}>{this.code}</MonoText>
122      </View>
123    );
124  }
125}
126
127const Header: React.FunctionComponent<{ title: string }> = ({ title }) => (
128  <View style={styles.headerContainer}>
129    <Text style={styles.headerText}>{title}</Text>
130  </View>
131);
132
133class HapticButton extends React.Component<{
134  method: (type: string) => void;
135  type: string;
136  style: ViewStyle;
137}> {
138  onPress = () => {
139    const { method, type } = this.props;
140    method(type);
141  };
142  render() {
143    return <Button onPress={this.onPress} style={this.props.style} title="Run" />;
144  }
145}
146
147const styles = StyleSheet.create({
148  container: {
149    paddingVertical: 16,
150    flex: 1,
151    backgroundColor: Colors.greyBackground,
152  },
153  list: {
154    flex: 1,
155    paddingHorizontal: 12,
156  },
157  headerContainer: {
158    alignItems: 'stretch',
159    borderBottomColor: Colors.border,
160    borderBottomWidth: StyleSheet.hairlineWidth,
161    backgroundColor: Colors.greyBackground,
162  },
163  headerText: {
164    color: Colors.tintColor,
165    paddingVertical: 4,
166    fontSize: 14,
167    fontWeight: 'bold',
168  },
169  itemContainer: {
170    flexDirection: 'row',
171    alignItems: 'center',
172  },
173  itemText: {
174    borderWidth: 0,
175    flex: 1,
176    marginVertical: 8,
177    paddingVertical: 18,
178    paddingLeft: 12,
179  },
180  button: {
181    marginRight: 16,
182  },
183});
184