할 일
바빌 키 등록의 성공 / 실패 화면으로 넘어가는 분기를 만들어야 한다. 분기는 크게 다음 두가지 상황에서 나타날 수 있다.
1. 타겟 디바이스를 입력한 뒤 babilScan.js 에서 해당 디바이스를 스캔한 결과에 따라.
2. 주변에 타겟 디바이스를 발견하여 닉네임을 설정했으나 서버로부터 응답이 거부된 경우.
닮아보이는 성공 실패 화면을 한 컴포넌트로 재활용하면 좋겠다는 생각이 든다.
해결
1. babilScan.js에서 타겟 디바이스를 어느정도 스캔해보고 promise로 결과를 받는다.
2. 그러기 위해선 먼저 nickName 화면 먼저 만들어야 한다. 그냥 문자열을 적어 제출하면 끝이다.
userId : uid
productId: device uid
brand
modelName
bikeNickName
맨 위 필드를 제외한 나머지는 전부 계속해서 props로 넘어왔고, userId만 액션으로 가져오면 된다. User 디렉토리 vehicles에는 device uid를 넣어줄거고, Product 디렉토리에서 해당 uid는 활성화 상태로 바꾼다.
코드
babilScan.js
componentDidMount() {
this.focusUnsubscribe = this.props.navigation.addListener('focus', () => {
console.log('start scanning')
this.props.emptyBLEList();
const targetDeviceUid = this.props.route.params.newBabilKey.deviceUid;
this.props.startScan([targetDeviceUid])
});
// 이 화면이 focus 됐을 때, store의 BLEList를 비우고 타겟 디바이스를 스캔한다.
this.blurUnsubscribe = this.props.navigation.addListener('blur', () => {
this.props.terminateScan();
})
// 이 화면을 떠나면 스캔을 종료한다.
Animated.sequence([
Animated.loop(
Animated.timing(
this.state.scale,
{
toValue: 250,
duration: 1000,
useNativeDriver: false
}
)
),
Animated.delay(1000)
])
.start();
// 애니메이션
console.log(this.props.route.params.newBabilKey.deviceUid)
this.timeBomb
.then(() => this.setState({
isLoading: false,
isDeviceFound: true
}))
.catch(() => this.setState({
isLoading: false,
isDeviceFound: false
}))
// timeBomb 결과에 따라 state 갱신을 한다.
}
componentDidUpdate(prevProps, prevState) {
if (this.state !== prevState) {
const timer = setTimeout(() => {
if (!this.state.isLoading) {
if (this.state.isDeviceFound) {
this.props.navigation.push('BikeNickName', { newBabilKey: { ...this.props.route.params.newBabilKey } });
} else {
console.log('why is this happening');
this.props.navigation.push('RegistrationResult', { type: 'fail' });
}
clearTimeout(timer);
}
}, 1000)
}
}
//state 갱신이 되면 state에 따라 어떤 화면으로 넘어갈지 정한다.
...
timeBomb = new Promise((resolve, reject) => {
let cnt = 0
const checker = setInterval(() => {
console.log(`looking for ${this.props.route.params.newBabilKey.deviceName} ${cnt}, ${JSON.stringify(this.props.BLEList)}`);
this.setState({
scanCounter: cnt % 4
})
if (this.props.BLEList?.some((item) => item.name == this.props.route.params.newBabilKey.deviceName)) {
clearInterval(checker);
resolve();
}
if (cnt == 25) {
clearInterval(checker)
reject()
}
cnt += 1
}, 500)
})
스캔 중 BLEList에 찾으려는 name을 가진 device가 있으면 resolve하는 timeBomb 프로미스 객체로 작동한다. 결과가 성공이면 바빌 키 닉네임 입력 화면으로, 실패하면 등록 실패화면으로 넘어간다. 전반적으로 새로운 바빌 키를 등록하는 로직이 조잡한 느낌이 있다. JS와 React에 대해 더 배워봐야 할것 같다. 그리고 무엇보다, 글을 수정 중인 2022.06.17 현재, 수정해야할 부분이 있다.
먼저 스캔을 디바이스의 uuid만으로 하는것. 현재는 AddVehicle 컴포넌트들이 계속 물려주는 newBabilKey props의 deviceUid를 타겟 uuid로 삼아 startScan의 인자로 넘겨주는데, 이는 디바이스를 특정하는 방식이 디바이스 마다 custom service uuid 를 다르게 설정하여 찾는다는 뜻이다. 하지만 굳이 service uuid를 각각 변경해줘서 디바이스를 식별하기 보다는, 차라리 스마트폰 os에 따라 분기해서 (os에 따라 스캔 결과로 받아오는 device 객체가 조금씩 달랐었나 그랬다) 예를 들면 안드로이드면 local name으로 특정 짓는다던지 하는 방법을 써야한다. 즉 native 로 접근해서 새로운 ble 로직을 찾아야 한다는 것이다. 물론 지금 생각이 그렇다는거지 지금 방법이 맞을 수도 있다.
+ 추가적으로, ble 관련 액션들의 상세한 예외 처리 분기가 안되어 있다. firebase 쪽도, navigation 쪽도 마찬가지인데, 참 당장만 생각해도 해결해야할 이슈가 너무나도 많다...
bikeNickName.js
...
manageAccess = () => {
this.props.setNewBabilKey(this.props.route.params.newBabilKey, this.props.User.uid, this.state.input.value)
.then(() => this.props.navigation.push('RegistrationResult', { type: 'complete' }))
.catch((error) => alert('등록 실패'))
}
...
다시 보니까 firebase db에 set 실패시 alert만 띄워주게 해놨다. 그런데 또 문득 이런생각이 든다. 이게 오히려 맞지 않을까? 생각해보면, 스캔 실패 했다고 (실패도 사실 여러가지 경우가 있을텐데, 주변에 실제로 device가 없을 때, 모종의 이유로 ble manager 객체가 파괴되었을때...! 등등) 바로 실패 화면으로 보내버린 뒤 처음부터 바이크 등록을 해야하도록 할 이유가 있는가? 정리해볼수록 바빌 키 추가 로직의 엉성함이 더욱 돋보인다...!
registrationResult.js
state = {
type: this.props.route.params.type,
}
renderResult = () => {
const isCompleteScreen = this.state.type == 'complete';
return (
<View style={ styles.viewContainer }>
<View style={{ flex:2, alignItems: 'center', justifyContent: 'flex-end' }}>
<Image
source={ isCompleteScreen ? Approved : Disapproved }
style={{ resizeMode:'contain', height:'40%'}}
/>
</View>
<View style={{ flex:1, alignItems: 'center', paddingTop:50 }}>
<Text style={{ fontSize:20, color: isCompleteScreen ? '#0FC760' : '#e82a2a' }}>{isCompleteScreen ? '등록 완료' : '등록 실패'}</Text>
</View>
</View>
)
}
renderButtons = () => {
const isCompleteScreen = this.state.type === 'complete';
return (
<View style={ styles.buttonContainer }>
<TouchableHighlight
onPress={() => this.props.navigation.push('BrandSelect')}
style={[ styles.button, {
backgroundColor: 'white',
borderWidth: 1,
borderColor: '#0FC760'
}]}
underlayColor='gray'
>
<Text style={{ fontSize:15, color: '#0FC760' }}>{isCompleteScreen ? '추가 등록하기' : '다시 등록하기'}</Text>
</TouchableHighlight>
<TouchableHighlight
onPress={() => this.props.navigation.getParent().push('MainHome')}
style={[ styles.button, {
backgroundColor: isCompleteScreen ? '#0FC760' : '#e82a2a'
}]}
underlayColor='gray'
>
<Text style={{ fontSize:15, color: 'white' }}>{isCompleteScreen ? '완료' : '등록 중단하기'}</Text>
</TouchableHighlight>
</View>
)
}
별건 없고, props로 받는 type에 따라 다른 화면을 보여주는 스캔결과 컴포넌트이다.
'BABIL_PROJECT > APP' 카테고리의 다른 글
졸업 프로젝트 결과 (0) | 2022.06.18 |
---|---|
React-Navigation & Lifecycle (0) | 2022.05.07 |
TwoStep Component (0) | 2022.01.29 |