Skip to content

Instantly share code, notes, and snippets.

@rs6000
Last active January 20, 2022 17:14
Show Gist options
  • Save rs6000/cc8dd9b666212bb5f93b5f48d20d825e to your computer and use it in GitHub Desktop.
Save rs6000/cc8dd9b666212bb5f93b5f48d20d825e to your computer and use it in GitHub Desktop.
Expo_Barcode_BookSearch
```
source form React Native 教學 - 編寫一個圖書資訊 APP - Code Guy Studio
https://codeguy.studio/react-native-tutorial/book-app/
```
import React, { useEffect, useState } from 'react';
import {
ScrollView,
View,
StyleSheet,
ActivityIndicator,
Text,
TextInput,
Pressable,
} from 'react-native';
//
import Constants from 'expo-constants';
import { BarCodeScanner } from 'expo-barcode-scanner';
//
const RowItem = ({ title, content }) => (
<View style={styles.rowItem}>
<Text style={styles.title}>{title}</Text>
<Text style={styles.content}>{content}</Text>
</View>
);
export default function App() {
const [isbn, setIsbn] = useState('');
const [disabled, setDisabled] = useState(true);
const [result, setResult] = useState(null);
const [loading, setLoading] = useState(false);
const [errorMsg, setErrorMsg] = useState('');
const [hasPermission, setHasPermission] = useState(null);
const [scanning, setScanning] = useState(false);
//權限
useEffect(() => {
(async () => {
const { status } = await BarCodeScanner.requestPermissionsAsync();
setHasPermission(status === 'granted');
})();
}, []);
const handleBarCodeScanned = ({ type, data }) => {
if (type === BarCodeScanner.Constants.BarCodeType.ean13) {
search(data);
setErrorMsg('');
setIsbn(data);
setScanning(false);
}
};
const onChangeText = (text) => {
if (text.length === 10 || text.length === 13) {
setDisabled(false);
} else {
setDisabled(true);
}
setErrorMsg('');
setIsbn(text);
};
const search = async (q) => {
const apiKey = '你的 Google Book API Key';
const baseUrl = 'https://www.googleapis.com/books/v1/volumes?q=isbn:';
const url = baseUrl + q + '&key=' + apiKey;
setLoading(true);
await fetch(url)
.then((response) => {
return response.json();
})
.then((json) => {
if (json.totalItems > 0) {
const info = json.items[0].volumeInfo;
setResult(info);
setErrorMsg('');
} else {
setResult(null);
setErrorMsg('找不到結果');
}
})
.catch((error) => {
setErrorMsg('發生錯誤:' + error);
});
setLoading(false);
};
return (
<ScrollView>
<View style={styles.bar} />
<View style={styles.container}>
<Text style={styles.header}>搜尋書本</Text>
<Text style={styles.subHeader}>
請輸入10位或13位的ISBN條碼搜尋書本資料
</Text>
<TextInput
style={styles.input}
placeholder="ISBN"
keyboardType="numeric"
maxLength={13}
onChangeText={onChangeText}
placeholderTextColor="#666666"
value={isbn}
/>
{!loading ? (
<>
<Pressable
style={({ pressed }) => [
{
backgroundColor: pressed || disabled ? '#e5e5e5' : '#FF0266'
},
styles.btn
]}
disabled={disabled}
onPress={() => search(isbn)}
>
<Text style={styles.btnText}>搜尋</Text>
</Pressable>
<Pressable
style={({ pressed }) => [
{
backgroundColor: pressed ? '#e5e5e5' : '#FF0266'
},
styles.btn
]}
onPress={() =>
scanning ? setScanning(false) : setScanning(true)
}
>
<Text style={styles.btnText}>
{scanning ? '關閉相機' : '掃描條碼'}
</Text>
</Pressable>
{hasPermission && scanning ? (
<View style={styles.scanner}>
<BarCodeScanner
onBarCodeScanned={handleBarCodeScanned}
style={StyleSheet.absoluteFillObject}
/>
</View>
) : null}
</>
) : (
<ActivityIndicator size="large" color="#e5e5e5" />
)}
{result ? (
<View style={styles.result}>
{result.imageLinks ? (
<Image
style={styles.image}
source={{ uri: result.imageLinks.thumbnail }}
resizeMode="contain"
/>
) : null}
{result.title ? (
<RowItem title="標題" content={result.title} />
) : null}
{result.subtitle ? (
<RowItem title="副標題" content={result.subtitle} />
) : null}
{result.authors ? (
<RowItem title="作者" content={result.authors.toString()} />
) : null}
{result.publisher ? (
<RowItem title="出版商" content={result.publisher} />
) : null}
{result.publishedDate ? (
<RowItem title="出版日期" content={result.publishedDate} />
) : null}
{result.description ? (
<RowItem title="描述" content={result.description} />
) : null}
</View>
) : null}
{errorMsg ? (
<View style={styles.result}>
<Text style={styles.errorMsg}>{errorMsg}</Text>
</View>
) : null}
</View>
</ScrollView>
);
}
const styles = StyleSheet.create({
rowItem: {
paddingVertical: 10
},
title: {
fontWeight: 'bold',
fontSize: 18,
paddingBottom: 10
},
content: {
fontSize: 18,
color: '#666666'
},
bar: {
height: Constants.statusBarHeight + 30,
backgroundColor: '#FF0266',
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 3
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5
},
header: {
alignSelf: 'center',
fontSize: 40,
marginBottom: 25
},
subHeader: {
fontSize: 18,
marginBottom: 25,
color: '#666666'
},
input: {
borderWidth: 1,
borderRadius: 5,
paddingVertical: 18,
paddingHorizontal: 15,
fontSize: 18,
marginBottom: 25
},
btn: {
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
paddingVertical: 12,
paddingHorizontal: 18,
borderRadius: 5,
marginBottom: 25
},
btnText: {
color: '#fff',
fontSize: 20,
textAlign: 'center'
},
result: {
borderTopWidth: 1,
marginTop: 20,
paddingTop: 20,
paddingBottom: 20
},
image: {
width: '100%',
aspectRatio: 1 / 1,
marginBottom: 10
},
container: {
backgroundColor: '#fff',
marginTop: 50,
paddingHorizontal: 30
},
errorMsg: {
color: '#B00020',
fontSize: 20,
alignSelf: 'center'
},
scanner: {
width: '100%',
aspectRatio: 1 / 1
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment