From eadd41f1d78cc5eacda192db290da0bef5c77687 Mon Sep 17 00:00:00 2001 From: Juchan Roh Date: Mon, 6 Feb 2023 19:46:04 +0900 Subject: [PATCH] build: change package name to dev.nulldori.eamemu --- android/app/build.gradle | 11 +- .../nulldori/eamemu/ReactNativeFlipper.java | 2 +- .../java/{tk => dev}/nulldori/eamemu/A.kt | 2 +- .../java/{tk => dev}/nulldori/eamemu/B.kt | 2 +- .../nulldori/eamemu/CardConvModule.java | 12 +- .../nulldori/eamemu/CardConvPackage.java | 2 +- .../java/{tk => dev}/nulldori/eamemu/E.kt | 2 +- .../nulldori/eamemu/HcefModule.java | 63 +- .../nulldori/eamemu/HcefPackage.java | 2 +- .../nulldori/eamemu/MainActivity.java | 2 +- .../nulldori/eamemu/MainApplication.java | 4 +- .../nulldori/eamemu/eAMEMuService.java | 2 +- .../nulldori/eamemu/ReactNativeFlipper.java | 2 +- src/screens/CardEditScreen.js | 604 ------------------ src/screens/CardEditScreen.tsx | 289 +++++++++ 15 files changed, 321 insertions(+), 680 deletions(-) rename android/app/src/debug/java/{tk => dev}/nulldori/eamemu/ReactNativeFlipper.java (99%) rename android/app/src/main/java/{tk => dev}/nulldori/eamemu/A.kt (99%) rename android/app/src/main/java/{tk => dev}/nulldori/eamemu/B.kt (99%) rename android/app/src/main/java/{tk => dev}/nulldori/eamemu/CardConvModule.java (75%) rename android/app/src/main/java/{tk => dev}/nulldori/eamemu/CardConvPackage.java (96%) rename android/app/src/main/java/{tk => dev}/nulldori/eamemu/E.kt (97%) rename android/app/src/main/java/{tk => dev}/nulldori/eamemu/HcefModule.java (65%) rename android/app/src/main/java/{tk => dev}/nulldori/eamemu/HcefPackage.java (96%) rename android/app/src/main/java/{tk => dev}/nulldori/eamemu/MainActivity.java (98%) rename android/app/src/main/java/{tk => dev}/nulldori/eamemu/MainApplication.java (97%) rename android/app/src/main/java/{tk => dev}/nulldori/eamemu/eAMEMuService.java (97%) rename android/app/src/release/java/{tk => dev}/nulldori/eamemu/ReactNativeFlipper.java (95%) delete mode 100644 src/screens/CardEditScreen.js create mode 100644 src/screens/CardEditScreen.tsx diff --git a/android/app/build.gradle b/android/app/build.gradle index 9f2da03..2936849 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -98,14 +98,13 @@ android { ndkVersion rootProject.ext.ndkVersion compileSdkVersion rootProject.ext.compileSdkVersion - namespace "tk.nulldori.eamemu" + namespace "dev.nulldori.eamemu" defaultConfig { - applicationId "tk.nulldori.eamemu" + applicationId "dev.nulldori.eamemu" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 220 - versionName "2.2.0" - vectorDrawables.useSupportLibrary = true + versionCode 300 + versionName "3.0.0" } splits { abi { @@ -172,8 +171,6 @@ dependencies { implementation jscFlavor } - // for react-native-fs - implementation project(':react-native-fs') implementation("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion") } diff --git a/android/app/src/debug/java/tk/nulldori/eamemu/ReactNativeFlipper.java b/android/app/src/debug/java/dev/nulldori/eamemu/ReactNativeFlipper.java similarity index 99% rename from android/app/src/debug/java/tk/nulldori/eamemu/ReactNativeFlipper.java rename to android/app/src/debug/java/dev/nulldori/eamemu/ReactNativeFlipper.java index e270212..5b708dc 100644 --- a/android/app/src/debug/java/tk/nulldori/eamemu/ReactNativeFlipper.java +++ b/android/app/src/debug/java/dev/nulldori/eamemu/ReactNativeFlipper.java @@ -4,7 +4,7 @@ *

This source code is licensed under the MIT license found in the LICENSE file in the root * directory of this source tree. */ -package tk.nulldori.eamemu; +package dev.nulldori.eamemu; import android.content.Context; import com.facebook.flipper.android.AndroidFlipperClient; import com.facebook.flipper.android.utils.FlipperUtils; diff --git a/android/app/src/main/java/tk/nulldori/eamemu/A.kt b/android/app/src/main/java/dev/nulldori/eamemu/A.kt similarity index 99% rename from android/app/src/main/java/tk/nulldori/eamemu/A.kt rename to android/app/src/main/java/dev/nulldori/eamemu/A.kt index 84675d3..34c9c9a 100644 --- a/android/app/src/main/java/tk/nulldori/eamemu/A.kt +++ b/android/app/src/main/java/dev/nulldori/eamemu/A.kt @@ -1,4 +1,4 @@ -package tk.nulldori.eamemu +package dev.nulldori.eamemu import java.io.UnsupportedEncodingException import kotlin.experimental.xor diff --git a/android/app/src/main/java/tk/nulldori/eamemu/B.kt b/android/app/src/main/java/dev/nulldori/eamemu/B.kt similarity index 99% rename from android/app/src/main/java/tk/nulldori/eamemu/B.kt rename to android/app/src/main/java/dev/nulldori/eamemu/B.kt index 3260bfc..b17b825 100644 --- a/android/app/src/main/java/tk/nulldori/eamemu/B.kt +++ b/android/app/src/main/java/dev/nulldori/eamemu/B.kt @@ -1,4 +1,4 @@ -package tk.nulldori.eamemu +package dev.nulldori.eamemu class B(arg4: ByteArray) { private val k: IntArray diff --git a/android/app/src/main/java/tk/nulldori/eamemu/CardConvModule.java b/android/app/src/main/java/dev/nulldori/eamemu/CardConvModule.java similarity index 75% rename from android/app/src/main/java/tk/nulldori/eamemu/CardConvModule.java rename to android/app/src/main/java/dev/nulldori/eamemu/CardConvModule.java index 8195688..7cff1d2 100644 --- a/android/app/src/main/java/tk/nulldori/eamemu/CardConvModule.java +++ b/android/app/src/main/java/dev/nulldori/eamemu/CardConvModule.java @@ -1,20 +1,10 @@ -package tk.nulldori.eamemu; +package dev.nulldori.eamemu; -import android.widget.Toast; - -import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.LifecycleEventListener; -import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.Promise; -import java.util.Map; -import java.util.HashMap; - -import tk.nulldori.eamemu.A; - public class CardConvModule extends ReactContextBaseJavaModule { private static ReactApplicationContext reactContext; private static A converter; diff --git a/android/app/src/main/java/tk/nulldori/eamemu/CardConvPackage.java b/android/app/src/main/java/dev/nulldori/eamemu/CardConvPackage.java similarity index 96% rename from android/app/src/main/java/tk/nulldori/eamemu/CardConvPackage.java rename to android/app/src/main/java/dev/nulldori/eamemu/CardConvPackage.java index f43ccb3..0d2a522 100644 --- a/android/app/src/main/java/tk/nulldori/eamemu/CardConvPackage.java +++ b/android/app/src/main/java/dev/nulldori/eamemu/CardConvPackage.java @@ -1,4 +1,4 @@ -package tk.nulldori.eamemu; +package dev.nulldori.eamemu; import com.facebook.react.ReactPackage; import com.facebook.react.bridge.NativeModule; diff --git a/android/app/src/main/java/tk/nulldori/eamemu/E.kt b/android/app/src/main/java/dev/nulldori/eamemu/E.kt similarity index 97% rename from android/app/src/main/java/tk/nulldori/eamemu/E.kt rename to android/app/src/main/java/dev/nulldori/eamemu/E.kt index 70a68db..390f060 100644 --- a/android/app/src/main/java/tk/nulldori/eamemu/E.kt +++ b/android/app/src/main/java/dev/nulldori/eamemu/E.kt @@ -1,4 +1,4 @@ -package tk.nulldori.eamemu +package dev.nulldori.eamemu object E { fun a(arg4: CharSequence): ByteArray { diff --git a/android/app/src/main/java/tk/nulldori/eamemu/HcefModule.java b/android/app/src/main/java/dev/nulldori/eamemu/HcefModule.java similarity index 65% rename from android/app/src/main/java/tk/nulldori/eamemu/HcefModule.java rename to android/app/src/main/java/dev/nulldori/eamemu/HcefModule.java index 85956b6..4b900f3 100644 --- a/android/app/src/main/java/tk/nulldori/eamemu/HcefModule.java +++ b/android/app/src/main/java/dev/nulldori/eamemu/HcefModule.java @@ -1,4 +1,4 @@ -package tk.nulldori.eamemu; +package dev.nulldori.eamemu; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; @@ -50,9 +50,9 @@ public class HcefModule extends ReactContextBaseJavaModule implements LifecycleE isHceFSupport = true; nfcAdapter = NfcAdapter.getDefaultAdapter(getReactApplicationContext()); - if(nfcAdapter != null){ + if(nfcAdapter != null && nfcAdapter.isEnabled()){ nfcFCardEmulation = NfcFCardEmulation.getInstance(nfcAdapter); - componentName = new ComponentName("tk.nulldori.eamemu","tk.nulldori.eamemu.eAMEMuService"); + componentName = new ComponentName("dev.nulldori.eamemu","dev.nulldori.eamemu.eAMEMuService"); if(nfcFCardEmulation != null){ nfcFCardEmulation.registerSystemCodeForService(componentName, "4000"); isHceFEnabled = true; @@ -74,52 +74,23 @@ public class HcefModule extends ReactContextBaseJavaModule implements LifecycleE } @ReactMethod - void setSID(String sid, Promise promise){ + void enableService(String sid, Promise promise){ if(nfcFCardEmulation == null || componentName == null){ promise.reject("NULL_ERROR", "nfcFCardEmulation or componentName is null"); + return ; } - sid = sid.toUpperCase(); - - if(sid.length() != 16) - promise.reject("LENGTH_ERROR", "The length of sid must be 16"); - if(sid.matches("[0-9a-fA-F]+") == false) - promise.reject("HEX_ERROR", "SID must be 16-digit hex number"); - if(sid.substring(0,4).contentEquals("02FE") == false) - promise.reject("PREFIX_ERROR", "SID must be start with 02FE"); - - boolean result = nfcFCardEmulation.setNfcid2ForService(componentName, sid); - - if (result) { - promise.resolve(true); - } else { - promise.reject("FAIL", "setNfcid2ForService returned false"); - } - } - - @ReactMethod - void enableService(Promise promise){ - if(nfcFCardEmulation == null || componentName == null){ - promise.reject("NULL_ERROR", "nfcFCardEmulation or componentName is null"); + if (!nfcFCardEmulation.setNfcid2ForService(componentName, sid)) { + promise.reject("SET_NFCID2_FAIL", "setNfcid2ForService returned false"); + return ; } - String cardId = nfcFCardEmulation.getNfcid2ForService(componentName); - - if(cardId.length() != 16) - promise.reject("LENGTH_ERROR", "The length of sid must be 16"); - if(cardId.matches("[0-9a-fA-F]+") == false) - promise.reject("HEX_ERROR", "SID must be 16-digit hex number"); - if(cardId.substring(0,4).contentEquals("02FE") == false) - promise.reject("PREFIX_ERROR", "SID must be start with 02FE"); - - boolean result = nfcFCardEmulation.enableService(getCurrentActivity(), componentName); - - if (result) { - nowUsing = true; - promise.resolve(true); - } else { + if (!nfcFCardEmulation.enableService(getCurrentActivity(), componentName)) { promise.reject("FAIL", "enableService returned false"); } + + nowUsing = true; + promise.resolve(true); } @ReactMethod @@ -128,14 +99,12 @@ public class HcefModule extends ReactContextBaseJavaModule implements LifecycleE promise.reject("NULL_ERROR", "nfcFCardEmulation or componentName is null"); } - boolean result = nfcFCardEmulation.disableService(getCurrentActivity()); - - if (result) { - nowUsing = false; - promise.resolve(true); - } else { + if (!nfcFCardEmulation.disableService(getCurrentActivity())) { promise.reject("FAIL", "disableService returned false"); } + + nowUsing = false; + promise.resolve(true); } @Override diff --git a/android/app/src/main/java/tk/nulldori/eamemu/HcefPackage.java b/android/app/src/main/java/dev/nulldori/eamemu/HcefPackage.java similarity index 96% rename from android/app/src/main/java/tk/nulldori/eamemu/HcefPackage.java rename to android/app/src/main/java/dev/nulldori/eamemu/HcefPackage.java index 2695374..864fb7f 100644 --- a/android/app/src/main/java/tk/nulldori/eamemu/HcefPackage.java +++ b/android/app/src/main/java/dev/nulldori/eamemu/HcefPackage.java @@ -1,4 +1,4 @@ -package tk.nulldori.eamemu; +package dev.nulldori.eamemu; import com.facebook.react.ReactPackage; import com.facebook.react.bridge.NativeModule; diff --git a/android/app/src/main/java/tk/nulldori/eamemu/MainActivity.java b/android/app/src/main/java/dev/nulldori/eamemu/MainActivity.java similarity index 98% rename from android/app/src/main/java/tk/nulldori/eamemu/MainActivity.java rename to android/app/src/main/java/dev/nulldori/eamemu/MainActivity.java index 9a38830..acca880 100644 --- a/android/app/src/main/java/tk/nulldori/eamemu/MainActivity.java +++ b/android/app/src/main/java/dev/nulldori/eamemu/MainActivity.java @@ -1,4 +1,4 @@ -package tk.nulldori.eamemu; +package dev.nulldori.eamemu; import android.os.Bundle; import com.facebook.react.ReactActivity; diff --git a/android/app/src/main/java/tk/nulldori/eamemu/MainApplication.java b/android/app/src/main/java/dev/nulldori/eamemu/MainApplication.java similarity index 97% rename from android/app/src/main/java/tk/nulldori/eamemu/MainApplication.java rename to android/app/src/main/java/dev/nulldori/eamemu/MainApplication.java index 205aa8a..021d99f 100644 --- a/android/app/src/main/java/tk/nulldori/eamemu/MainApplication.java +++ b/android/app/src/main/java/dev/nulldori/eamemu/MainApplication.java @@ -1,4 +1,4 @@ -package tk.nulldori.eamemu; +package dev.nulldori.eamemu; import android.app.Application; import com.facebook.react.PackageList; @@ -8,7 +8,7 @@ import com.facebook.react.ReactPackage; import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; import com.facebook.react.defaults.DefaultReactNativeHost; import com.facebook.soloader.SoLoader; -import com.rnfs.RNFSPackage; + import java.util.List; public class MainApplication extends Application implements ReactApplication { diff --git a/android/app/src/main/java/tk/nulldori/eamemu/eAMEMuService.java b/android/app/src/main/java/dev/nulldori/eamemu/eAMEMuService.java similarity index 97% rename from android/app/src/main/java/tk/nulldori/eamemu/eAMEMuService.java rename to android/app/src/main/java/dev/nulldori/eamemu/eAMEMuService.java index 0666fff..02caf12 100644 --- a/android/app/src/main/java/tk/nulldori/eamemu/eAMEMuService.java +++ b/android/app/src/main/java/dev/nulldori/eamemu/eAMEMuService.java @@ -1,4 +1,4 @@ -package tk.nulldori.eamemu; +package dev.nulldori.eamemu; import android.nfc.cardemulation.HostNfcFService; import android.os.Bundle; diff --git a/android/app/src/release/java/tk/nulldori/eamemu/ReactNativeFlipper.java b/android/app/src/release/java/dev/nulldori/eamemu/ReactNativeFlipper.java similarity index 95% rename from android/app/src/release/java/tk/nulldori/eamemu/ReactNativeFlipper.java rename to android/app/src/release/java/dev/nulldori/eamemu/ReactNativeFlipper.java index 4f93cbc..f79eaf7 100644 --- a/android/app/src/release/java/tk/nulldori/eamemu/ReactNativeFlipper.java +++ b/android/app/src/release/java/dev/nulldori/eamemu/ReactNativeFlipper.java @@ -4,7 +4,7 @@ *

This source code is licensed under the MIT license found in the LICENSE file in the root * directory of this source tree. */ -package com.rndiffapp; +package dev.nulldori.eamemu; import android.content.Context; import com.facebook.react.ReactInstanceManager; /** diff --git a/src/screens/CardEditScreen.js b/src/screens/CardEditScreen.js deleted file mode 100644 index 03f6e3d..0000000 --- a/src/screens/CardEditScreen.js +++ /dev/null @@ -1,604 +0,0 @@ -import React from 'react'; -import { - View, - Text, - StatusBar, - SafeAreaView, - ScrollView, - TouchableOpacity, - Dimensions, - ImageBackground, - findNodeHandle, - Alert, - StyleSheet, - Image, - KeyboardAvoidingView, - TextInput, -} from 'react-native'; -import update from 'react-addons-update'; -import Icon from 'react-native-vector-icons/MaterialIcons'; -import ImagePicker from 'react-native-image-crop-picker'; -import CardConv from '../modules/CardConv'; -import i18n from 'i18n-js'; - -class CardPreview extends React.Component { - render() { - let cardContent = ( - - - - - {this.props.name} - - - - {this.props.uid - ? this.props.uid.substr(0, 4) + - '-' + - this.props.uid.substr(4, 4) + - '-' + - this.props.uid.substr(8, 4) + - '-' + - this.props.uid.substr(12, 4) - : ''} - - - {i18n.t('card_touch_to_enable')} - - - - - - - - - {i18n.t('card_edit')} - - - - - - {i18n.t('card_delete')} - - - - - ); - return ( - - {this.props.image ? ( - - {cardContent} - - ) : ( - - {cardContent} - - )} - - ); - } -} - -class ETextInput extends React.Component { - state = { - focused: false, - value: this.props.value ?? '', - }; - - onFocusCallback() { - this.setState({ focused: true }); - if (typeof this.props.onFocus === 'function') { - this.props.onFocus(); - } - } - - onBlurCallback() { - this.setState({ focused: false }); - if (typeof this.props.onBlur === 'function') { - this.props.onBlur(); - } - } - - onChangeTextCallback(text) { - if (typeof this.props.filter === 'function') { - text = this.props.filter(text); - } - if (typeof this.props.onChangeText === 'function') { - this.props.onChangeText(text); - } - } - - focus() { - this.textInput.focus(); - } - - render() { - return ( - - - {this.props.title} - - - this.onFocusCallback()} - onBlur={() => this.onBlurCallback()} - onChangeText={text => this.onChangeTextCallback(text)} - editable={this.props.editable} - maxLength={this.props.maxLength} - autoCapitalize={this.props.autoCapitalize} - ref={ref => (this.textInput = ref)} - /> - - - ); - } -} - -class CardEditScreen extends React.Component { - state = { - name: this.props.navigation.getParam('name', ''), - sidAll: this.props.navigation.getParam('sid', '02FE'), - sid2: '', - sid3: '', - sid4: '', - uid: '', - sidError: false, - - image: this.props.navigation.getParam('image', ''), - index: this.props.navigation.getParam('index', null), // null 이면 카드 새로 생성 - mode: '', - - update: this.props.navigation.getParam('update', null), - - cardFocused: 'false', - }; - - componentDidMount(): void { - let { height, width } = Dimensions.get('window'); - - function randomHex4Byte() { - let hexString = Math.min( - Math.floor(Math.random() * 65536), - 65535, - ).toString(16); - - if (hexString.length < 4) { - hexString = '0'.repeat(4 - hexString.length) + hexString; - } - - return hexString.toUpperCase(); - } - - if (this.state.index === null) { - this.setState( - { - mode: 'add', - sid2: randomHex4Byte(), - sid3: randomHex4Byte(), - sid4: randomHex4Byte(), - cardHeight: ((width - 48) * 53.98) / 85.6, - }, - () => this.updateSID(), - ); - } else { - this.setState( - { - mode: 'edit', - sid2: this.state.sidAll.substr(4, 4), - sid3: this.state.sidAll.substr(8, 4), - sid4: this.state.sidAll.substr(12, 4), - cardHeight: ((width - 48) * 53.98) / 85.6, - }, - () => { - this.updateSID(); - }, - ); - } - - this.nameInput.focus(); - } - - makeSid() { - let sid = '02FE' + this.state.sid2 + this.state.sid3 + this.state.sid4; - sid = sid.toUpperCase(); - if (sid.length !== 16) { - return ''; - } - if (sid.replace(/[^0-9A-F]/g, '') !== sid) { - return ''; - } - - return sid; - } - - async updateSID() { - if (this.makeSid() !== '') { - let uid = await CardConv.convertSID(this.makeSid()); - this.setState({ uid: uid }); - } - } - - saveCard() { - let sid = this.makeSid(); - - if (sid === '') { - this.setState({ sidError: true }); - return; - } - - if (typeof this.state.update === 'function') { - this.state.update( - this.state.name, - sid, - this.state.index, - this.state.image, - this.props.navigation, - ); - } - } - - setRandomSid() { - function randomHex4Byte() { - let hexString = Math.min( - Math.floor(Math.random() * 65536), - 65535, - ).toString(16); - - if (hexString.length < 4) { - hexString = '0'.repeat(4 - hexString.length) + hexString; - } - - return hexString.toUpperCase(); - } - - this.setState( - { - sid2: randomHex4Byte(), - sid3: randomHex4Byte(), - sid4: randomHex4Byte(), - }, - () => this.updateSID(), - ); - } - - selectPhoto() { - ImagePicker.openPicker({ - cropping: true, - width: 856, - height: 540, - mediaType: 'photo', - }).then(res => { - if (res && res.path) { - this.setState({ image: res.path }); - } - }); - } - - render() { - return ( - - - - - - - {this.state.mode === 'add' - ? i18n.t('header_add') - : i18n.t('header_edit')} - - - this.saveCard()} - > - - - - - - this.setState({ name: text })} - ref={ref => (this.nameInput = ref)} - /> - this.selectPhoto()}> - - - - - this.setState({ sid: text })} - style={{ flex: 1 }} - editable={false} - error={this.state.sidError} - textStyle={{ textAlign: 'center' }} - titleStyle={ - this.state.sidError !== true && - this.state.sidFocus && { color: '#03A9F4' } - } - /> - - - - - { - this.setState({ sid2: text }, () => this.updateSID()); - if (text.length === 4) { - this.sid3Input.focus(); - } - }} - style={{ flex: 1 }} - maxLength={4} - error={this.state.sidError} - onFocus={() => this.setState({ sidFocus: true })} - onBlur={() => this.setState({ sidFocus: false })} - autoCapitalize={'characters'} - ref={ref => (this.sid2Input = ref)} - editable={false} - /> - - - - - { - this.setState({ sid3: text }, () => this.updateSID()); - if (text.length === 4) { - this.sid4Input.focus(); - } - }} - style={{ flex: 1 }} - maxLength={4} - error={this.state.sidError} - onFocus={() => this.setState({ sidFocus: true })} - onBlur={() => this.setState({ sidFocus: false })} - autoCapitalize={'characters'} - ref={ref => (this.sid3Input = ref)} - editable={false} - /> - - - - - { - this.setState({ sid4: text }, () => this.updateSID()); - this.updateSID(); - }} - style={{ flex: 1 }} - error={this.state.sidError} - maxLength={4} - onFocus={() => this.setState({ sidFocus: true })} - onBlur={() => this.setState({ sidFocus: false })} - autoCapitalize={'characters'} - ref={ref => (this.sid4Input = ref)} - editable={false} - /> - - - {i18n.t('edit_sid_notice')} - - this.setRandomSid()} - > - - {i18n.t('edit_random')} - - - - - {i18n.t('edit_preview')} - - - - - - - - ); - } -} - -export default CardEditScreen; diff --git a/src/screens/CardEditScreen.tsx b/src/screens/CardEditScreen.tsx new file mode 100644 index 0000000..36332a8 --- /dev/null +++ b/src/screens/CardEditScreen.tsx @@ -0,0 +1,289 @@ +import React, { useCallback, useMemo, useState } from 'react'; +import { + View, + Text, + ScrollView, + TouchableOpacity, + StyleSheet, + KeyboardAvoidingView, + TextInput, + TextInputProps, + ViewStyle, + TextInputFocusEventData, + NativeSyntheticEvent, + TextStyle, +} from 'react-native'; +import CardConv from '../modules/CardConv'; +import { NativeStackScreenProps } from '@react-navigation/native-stack'; +import { RootStackParams } from '../../App'; +import { Shadow } from 'react-native-shadow-2'; +import CardView from '../components/Card'; +import { addCard, updateCard } from '../data/cards'; +import { useMutation, useQuery, useQueryClient } from 'react-query'; +import { Card } from '../types'; + +type TextFieldProps = TextInputProps & { + title: string; + containerStyle?: ViewStyle; +}; + +const generateRandomCardNumber = () => { + const getRandom4Byte = () => { + return Math.trunc(Math.random() * 65536) + .toString(16) + .toUpperCase() + .padStart(4, '0'); + }; + + return `02FE${getRandom4Byte()}${getRandom4Byte()}${getRandom4Byte()}`; +}; + +const FieldTitle = (props: { title: string; style?: TextStyle }) => { + return ( + {props.title} + ); +}; + +const TextField = (props: TextFieldProps) => { + const { onFocus, onBlur, title, containerStyle, ...textInputProps } = props; + + const [isFocused, setIsFocused] = useState(false); + const onFocusCallback = useCallback( + (event: NativeSyntheticEvent) => { + setIsFocused(true); + onFocus?.(event); + }, + [onFocus], + ); + + const onBlurCallback = useCallback( + (event: NativeSyntheticEvent) => { + setIsFocused(false); + onBlur?.(event); + }, + [onBlur], + ); + + return ( + + + + + + ); +}; + +type CardAddScreenProps = NativeStackScreenProps; +type CardEditScreenProps = NativeStackScreenProps; + +const CardEditScreen = (props: CardAddScreenProps | CardEditScreenProps) => { + const initialData = props.route.params?.card ?? undefined; + + const [mode] = useState<'add' | 'edit'>(() => { + return initialData ? 'edit' : 'add'; + }); + + const [cardName, setCardName] = useState(initialData?.name ?? 'eAM'); + const [cardNumber, setCardNumber] = useState(() => { + return initialData?.sid ?? generateRandomCardNumber(); + }); + const uid = useQuery(['uid', cardNumber], () => + CardConv.convertSID(cardNumber), + ); + + const styledUid = useMemo(() => { + if (!uid.isSuccess) { + return '카드번호를 불러오는 중...'; + } + + return ( + uid.data.match(/[A-Za-z0-9]{4}/g)?.join(' - ') ?? + '잘못된 카드 번호입니다.' + ); + }, [uid]); + + const onChangeCardName = useCallback((s: string) => { + setCardName(s); + }, []); + + const changeCardNumber = useCallback(() => { + setCardNumber(generateRandomCardNumber()); + }, []); + + const queryClient = useQueryClient(); + const addMutation = useMutation( + (card: Card) => { + return addCard(card); + }, + { + onSuccess: async () => { + await queryClient.invalidateQueries('cards'); + props.navigation.goBack(); + }, + }, + ); + const editMutation = useMutation( + ({ index, card }: { index: number; card: Card }) => { + return updateCard(index, card); + }, + { + onSuccess: async () => { + await queryClient.invalidateQueries('cards'); + props.navigation.goBack(); + }, + }, + ); + + const save = useCallback(() => { + const card = { + sid: cardNumber, + name: cardName, + }; + + if (mode === 'add') { + addMutation.mutate(card); + } else { + editMutation.mutate({ index: props.route.params!.index, card: card }); + } + }, [ + addMutation, + cardName, + cardNumber, + editMutation, + mode, + props.route.params, + ]); + + return ( + + + + + + + + + + + + + + + 카드 번호 변경 + + + + + + + 저장 + + + + + ); +}; + +const styles = StyleSheet.create({ + screen: { + flex: 1, + }, + scrollView: { + padding: 16, + }, + cardPreviewContainer: { + paddingBottom: 32, + }, + fieldItemContainer: { + paddingTop: 24, + }, + textInput: { + fontSize: 16, + paddingTop: 4, + }, + textInputTitle: { + fontSize: 14, + fontWeight: 'bold', + color: '#9e9e9e', + }, + textInputTitleFocused: { + color: 'skyblue', + }, + textInputBottomBorder: { + paddingTop: 2, + backgroundColor: '#9e9e9e', + height: 1, + }, + textInputBottomBorderFocused: { + backgroundColor: 'skyblue', + height: 2, + }, + button: { + height: 48, + borderRadius: 8, + backgroundColor: 'skyblue', + justifyContent: 'center', + alignItems: 'center', + }, + buttonShadowStyle: { + width: '100%', + }, + saveButtonContainerStyle: { + marginTop: 32, + }, + cardNumberChangeButton: { + marginTop: 16, + }, + cardImageSelectButton: { + marginTop: 8, + }, + buttonText: { + color: 'white', + }, +}); + +export default CardEditScreen;