Skip to content

Commit 0970d26

Browse files
committed
Add SWCharacters
1 parent 3e44496 commit 0970d26

File tree

20,067 files changed

+2697031
-1
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

20,067 files changed

+2697031
-1
lines changed

swcharacters/App.js

+202
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
/**
2+
* Sample React Native App
3+
* https://github.com/facebook/react-native
4+
*
5+
* @format
6+
* @flow
7+
*/
8+
9+
import React, {useEffect, useState, useCallback} from 'react';
10+
import {
11+
SafeAreaView,
12+
StyleSheet,
13+
Alert,
14+
View,
15+
Text,
16+
FlatList,
17+
TouchableOpacity,
18+
Linking,
19+
Picker,
20+
ActivityIndicator,
21+
StatusBar,
22+
} from 'react-native';
23+
import API from './api';
24+
25+
const App: () => React$Node = () => {
26+
const [data, setData] = useState([]);
27+
const [dataType, setDataType] = useState('people');
28+
const [isLoadingData, setIsLoadingData] = useState(false);
29+
let dataMaxCount = 0;
30+
// eslint-disable-next-line no-unused-vars
31+
let flatList;
32+
33+
const _loadData = useCallback(() => {
34+
if (isLoadingData) {
35+
return;
36+
}
37+
if (dataMaxCount !== 0 && Object.keys(data).length === dataMaxCount) {
38+
return;
39+
}
40+
setIsLoadingData(true);
41+
API.getAll(dataType, Math.ceil(Object.keys(data).length / 10) + 1)
42+
.then(response => {
43+
if (response.results) {
44+
dataMaxCount = Number(response.count);
45+
46+
const newData = [...data.concat(response.results)];
47+
setData(newData);
48+
if (newData.length <= 10) {
49+
if (this.flatList !== undefined) {
50+
this.flatList.scrollToOffset(0);
51+
}
52+
}
53+
}
54+
setIsLoadingData(false);
55+
})
56+
.catch(e => {
57+
Alert.alert(e);
58+
setIsLoadingData(false);
59+
});
60+
});
61+
62+
useEffect(() => {
63+
_loadData();
64+
// eslint-disable-next-line react-hooks/exhaustive-deps
65+
}, [dataType]);
66+
67+
const _renderItem = (rowData, index) => {
68+
let rowDataComponent = [];
69+
Object.entries(rowData.item).forEach(([key, value]) => {
70+
if (typeof value === 'string') {
71+
rowDataComponent.push(
72+
<Text style={styles.characterRow}>
73+
<Text style={styles.characterRowTitle}>
74+
{key.replace('_', ' ')}:
75+
</Text>
76+
</Text>,
77+
);
78+
if (value.startsWith('http://') || value.startsWith('https://')) {
79+
rowDataComponent.push(
80+
<TouchableOpacity onPress={() => Linking.openURL(value)}>
81+
<Text style={styles.characterRowLink}>{value}</Text>
82+
</TouchableOpacity>,
83+
);
84+
} else {
85+
rowDataComponent.push(
86+
<Text style={styles.characterRow}>{value}</Text>,
87+
);
88+
}
89+
} else if (Array.isArray(value) && value.length > 0) {
90+
rowDataComponent.push(
91+
<Text style={styles.characterRowTitle}>
92+
{key.replace('_', ' ')}:
93+
</Text>,
94+
);
95+
rowDataComponent.push(<View />);
96+
for (const item in value) {
97+
if (
98+
value[item].startsWith('http://') ||
99+
value[item].startsWith('https://')
100+
) {
101+
rowDataComponent.push(
102+
<TouchableOpacity onPress={() => Linking.openURL(value[item])}>
103+
<Text style={styles.characterRowLink}>{value[item]}</Text>
104+
</TouchableOpacity>,
105+
);
106+
} else {
107+
rowDataComponent.push(
108+
<Text style={styles.characterRow}>{value[item]}</Text>,
109+
);
110+
}
111+
}
112+
}
113+
});
114+
115+
return <View style={styles.characterRowContainer}>{rowDataComponent}</View>;
116+
};
117+
118+
const pickerValueChanged = value => {
119+
dataMaxCount = 0;
120+
setData([]);
121+
setDataType(value);
122+
};
123+
124+
return (
125+
<>
126+
<StatusBar barStyle="dark-content" />
127+
<SafeAreaView style={styles.container}>
128+
<View style={styles.container}>
129+
<FlatList
130+
data={data}
131+
ref={ref => (this.flatList = ref)}
132+
extraData={data}
133+
keyExtractor={(_item, index) => `${index}`}
134+
style={styles.list}
135+
contentInset={{top: 4, left: 0, bottom: 0, right: 0}}
136+
renderItem={_renderItem}
137+
ItemSeparatorComponent={() => (
138+
<View style={styles.ItemSeparatorComponent} />
139+
)}
140+
onEndReachedThreshold={1}
141+
onEndReached={_loadData}
142+
ListFooterComponent={isLoadingData ? <ActivityIndicator /> : null}
143+
/>
144+
<View style={styles.separator} />
145+
<View
146+
style={styles.pickerContainer}
147+
pointerEvents={isLoadingData ? 'none' : 'auto'}>
148+
<Picker
149+
selectedValue={dataType}
150+
enabled={!isLoadingData}
151+
onValueChange={pickerValueChanged}>
152+
<Picker.Item label="People" value="people" />
153+
<Picker.Item label="Planets" value="planets" />
154+
<Picker.Item label="Films" value="films" />
155+
<Picker.Item label="Species" value="species" />
156+
<Picker.Item label="Starships" value="starships" />
157+
</Picker>
158+
</View>
159+
</View>
160+
</SafeAreaView>
161+
</>
162+
);
163+
};
164+
165+
const styles = StyleSheet.create({
166+
container: {
167+
flex: 1,
168+
justifyContent: 'space-between',
169+
},
170+
list: {
171+
flex: 0.7,
172+
},
173+
ItemSeparatorComponent: {
174+
height: 5,
175+
backgroundColor: '#000000',
176+
marginVertical: 8,
177+
},
178+
separator: {
179+
height: 2,
180+
backgroundColor: 'gray',
181+
},
182+
characterRowContainer: {
183+
padding: 8,
184+
},
185+
characterRow: {
186+
marginBottom: 8,
187+
},
188+
characterRowLink: {
189+
marginBottom: 8,
190+
color: 'blue',
191+
},
192+
pickerContainer: {
193+
flex: 0.3,
194+
},
195+
characterRowTitle: {
196+
marginBottom: 8,
197+
fontWeight: '700',
198+
textTransform: 'capitalize',
199+
},
200+
});
201+
202+
export default App;

swcharacters/__tests__/App-test.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* @format
3+
*/
4+
5+
import API from '../api';
6+
const mockPeopleResponse = require('./mockPeopleResponse.json');
7+
8+
beforeEach(() => {
9+
fetch.resetMocks();
10+
});
11+
12+
test('mock JSON response', () => {
13+
fetch.mockResponseOnce(JSON.stringify(mockPeopleResponse));
14+
const onResponse = jest.fn();
15+
const onError = jest.fn();
16+
17+
return API.getAll()
18+
.then(onResponse)
19+
.catch(onError)
20+
.finally(() => {
21+
expect(onResponse).toHaveBeenCalled();
22+
expect(onError).not.toHaveBeenCalled();
23+
const count = onResponse.mock.calls[0][0].count;
24+
expect(count).toEqual(87);
25+
});
26+
});

0 commit comments

Comments
 (0)