initial import
This commit is contained in:
parent
fa1f694e72
commit
e65ade8ffe
115
App.tsx
115
App.tsx
|
@ -1,118 +1,9 @@
|
||||||
/**
|
import Main from './src//Main';
|
||||||
* Sample React Native App
|
|
||||||
* https://github.com/facebook/react-native
|
|
||||||
*
|
|
||||||
* @format
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React from 'react';
|
const App = () => {
|
||||||
import type {PropsWithChildren} from 'react';
|
|
||||||
import {
|
|
||||||
SafeAreaView,
|
|
||||||
ScrollView,
|
|
||||||
StatusBar,
|
|
||||||
StyleSheet,
|
|
||||||
Text,
|
|
||||||
useColorScheme,
|
|
||||||
View,
|
|
||||||
} from 'react-native';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Colors,
|
|
||||||
DebugInstructions,
|
|
||||||
Header,
|
|
||||||
LearnMoreLinks,
|
|
||||||
ReloadInstructions,
|
|
||||||
} from 'react-native/Libraries/NewAppScreen';
|
|
||||||
|
|
||||||
type SectionProps = PropsWithChildren<{
|
|
||||||
title: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
function Section({children, title}: SectionProps): React.JSX.Element {
|
|
||||||
const isDarkMode = useColorScheme() === 'dark';
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.sectionContainer}>
|
<Main></Main>
|
||||||
<Text
|
|
||||||
style={[
|
|
||||||
styles.sectionTitle,
|
|
||||||
{
|
|
||||||
color: isDarkMode ? Colors.white : Colors.black,
|
|
||||||
},
|
|
||||||
]}>
|
|
||||||
{title}
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
style={[
|
|
||||||
styles.sectionDescription,
|
|
||||||
{
|
|
||||||
color: isDarkMode ? Colors.light : Colors.dark,
|
|
||||||
},
|
|
||||||
]}>
|
|
||||||
{children}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
function App(): React.JSX.Element {
|
|
||||||
const isDarkMode = useColorScheme() === 'dark';
|
|
||||||
|
|
||||||
const backgroundStyle = {
|
|
||||||
backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
|
||||||
<SafeAreaView style={backgroundStyle}>
|
|
||||||
<StatusBar
|
|
||||||
barStyle={isDarkMode ? 'light-content' : 'dark-content'}
|
|
||||||
backgroundColor={backgroundStyle.backgroundColor}
|
|
||||||
/>
|
|
||||||
<ScrollView
|
|
||||||
contentInsetAdjustmentBehavior="automatic"
|
|
||||||
style={backgroundStyle}>
|
|
||||||
<Header />
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
backgroundColor: isDarkMode ? Colors.black : Colors.white,
|
|
||||||
}}>
|
|
||||||
<Section title="Step One">
|
|
||||||
Edit <Text style={styles.highlight}>App.tsx</Text> to change this
|
|
||||||
screen and then come back to see your edits.
|
|
||||||
</Section>
|
|
||||||
<Section title="See Your Changes">
|
|
||||||
<ReloadInstructions />
|
|
||||||
</Section>
|
|
||||||
<Section title="Debug">
|
|
||||||
<DebugInstructions />
|
|
||||||
</Section>
|
|
||||||
<Section title="Learn More">
|
|
||||||
Read the docs to discover what to do next:
|
|
||||||
</Section>
|
|
||||||
<LearnMoreLinks />
|
|
||||||
</View>
|
|
||||||
</ScrollView>
|
|
||||||
</SafeAreaView>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
sectionContainer: {
|
|
||||||
marginTop: 32,
|
|
||||||
paddingHorizontal: 24,
|
|
||||||
},
|
|
||||||
sectionTitle: {
|
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: '600',
|
|
||||||
},
|
|
||||||
sectionDescription: {
|
|
||||||
marginTop: 8,
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: '400',
|
|
||||||
},
|
|
||||||
highlight: {
|
|
||||||
fontWeight: '700',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default App;
|
export default App;
|
|
@ -1,6 +1,11 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.NFC"/>
|
||||||
|
|
||||||
|
<uses-feature
|
||||||
|
android:name="android.hardware.nfc.hce"
|
||||||
|
android:required="true" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".MainApplication"
|
android:name=".MainApplication"
|
||||||
|
|
|
@ -18,4 +18,36 @@ buildscript {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subprojects {
|
||||||
|
afterEvaluate {
|
||||||
|
if (project.hasProperty("android")) {
|
||||||
|
android {
|
||||||
|
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||||
|
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
// Speed up Tests Stage
|
||||||
|
tasks.withType(Test).configureEach {
|
||||||
|
// https://docs.gradle.org/current/userguide/performance.html#execute_tests_in_parallel
|
||||||
|
maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1
|
||||||
|
// https://docs.gradle.org/current/userguide/performance.html#fork_tests_into_multiple_processes
|
||||||
|
forkEvery = 100
|
||||||
|
// https://docs.gradle.org/current/userguide/performance.html#disable_reports
|
||||||
|
reports.html.required = false
|
||||||
|
reports.junitXml.required = false
|
||||||
|
}
|
||||||
|
// Speed up Java Compile Stage
|
||||||
|
// https://docs.gradle.org/current/userguide/performance.html#run_the_compiler_as_a_separate_process
|
||||||
|
tasks.withType(JavaCompile).configureEach {
|
||||||
|
options.fork = true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
apply plugin: "com.facebook.react.rootproject"
|
apply plugin: "com.facebook.react.rootproject"
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
{
|
{
|
||||||
"name": "keycardExit",
|
"name": "KeycardExit",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "keycardExit",
|
"name": "KeycardExit",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
"react-native": "0.75.2"
|
"react-native": "0.75.2",
|
||||||
|
"react-native-modal": "^13.0.1",
|
||||||
|
"react-native-status-keycard": "git+https://github.com/status-im/react-native-status-keycard.git#refs/tags/v2.5.39"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.20.0",
|
"@babel/core": "^7.20.0",
|
||||||
|
@ -11344,7 +11346,6 @@
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
|
@ -11851,7 +11852,6 @@
|
||||||
"version": "15.8.1",
|
"version": "15.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.4.0",
|
"loose-envify": "^1.4.0",
|
||||||
"object-assign": "^4.1.1",
|
"object-assign": "^4.1.1",
|
||||||
|
@ -11861,8 +11861,7 @@
|
||||||
"node_modules/prop-types/node_modules/react-is": {
|
"node_modules/prop-types/node_modules/react-is": {
|
||||||
"version": "16.13.1",
|
"version": "16.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/punycode": {
|
"node_modules/punycode": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
|
@ -12018,6 +12017,32 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-native-animatable": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-native-animatable/-/react-native-animatable-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-2ckIxZQAsvWn25Ho+DK3d1mXIgj7tITkrS4pYDvx96WyOttSvzzFeQnM2od0+FUMzILbdHDsDEqZvnz1DYNQ1w==",
|
||||||
|
"dependencies": {
|
||||||
|
"prop-types": "^15.7.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-native-modal": {
|
||||||
|
"version": "13.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-native-modal/-/react-native-modal-13.0.1.tgz",
|
||||||
|
"integrity": "sha512-UB+mjmUtf+miaG/sDhOikRfBOv0gJdBU2ZE1HtFWp6UixW9jCk/bhGdHUgmZljbPpp0RaO/6YiMmQSSK3kkMaw==",
|
||||||
|
"dependencies": {
|
||||||
|
"prop-types": "^15.6.2",
|
||||||
|
"react-native-animatable": "1.3.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "*",
|
||||||
|
"react-native": ">=0.65.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-native-status-keycard": {
|
||||||
|
"version": "2.5.39",
|
||||||
|
"resolved": "git+ssh://git@github.com/status-im/react-native-status-keycard.git#93dd64754e676172310e6ea7187cc49f2dc013c6",
|
||||||
|
"license": "MPL 2.0"
|
||||||
|
},
|
||||||
"node_modules/react-native/node_modules/@jest/types": {
|
"node_modules/react-native/node_modules/@jest/types": {
|
||||||
"version": "26.6.2",
|
"version": "26.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "keycardExit",
|
"name": "KeycardExit",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -11,7 +11,9 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
"react-native": "0.75.2"
|
"react-native": "0.75.2",
|
||||||
|
"react-native-modal": "^13.0.1",
|
||||||
|
"react-native-status-keycard": "git+https://github.com/status-im/react-native-status-keycard.git#refs/tags/v2.5.39"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.20.0",
|
"@babel/core": "^7.20.0",
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
import { Platform, SafeAreaView, StyleSheet, useColorScheme, DeviceEventEmitter } from 'react-native';
|
||||||
|
import { Colors } from 'react-native/Libraries/NewAppScreen';
|
||||||
|
import StartScreen from './components/StartScreen';
|
||||||
|
|
||||||
|
//@ts-ignore
|
||||||
|
import Keycard from "react-native-status-keycard";
|
||||||
|
|
||||||
|
const Main = () => {
|
||||||
|
const isDarkMode = useColorScheme() === 'dark';
|
||||||
|
const [isModalVisible, setIsModalVisible] = useState<boolean>(false);
|
||||||
|
const didMount = useRef(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!didMount.current) {
|
||||||
|
didMount.current = true;
|
||||||
|
DeviceEventEmitter.addListener("keyCardOnConnected", async () => {
|
||||||
|
console.log(await Keycard.getApplicationInfo());
|
||||||
|
setIsModalVisible(false);
|
||||||
|
});
|
||||||
|
DeviceEventEmitter.addListener("keyCardOnDisconnected", () => console.log("keycard disconnected"));
|
||||||
|
DeviceEventEmitter.addListener("keyCardOnNFCEnabled", () => console.log("nfc enabled"));
|
||||||
|
DeviceEventEmitter.addListener("keyCardOnNFCDisabled", () => console.log("nfc disabled"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const backgroundStyle = {
|
||||||
|
backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
|
||||||
|
};
|
||||||
|
|
||||||
|
const exitFunc = async () => {
|
||||||
|
if (await Keycard.nfcIsSupported() && !await Keycard.nfcIsEnabled()) {
|
||||||
|
await Keycard.openNfcSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
await Keycard.startNFC("Tap your Keycard");
|
||||||
|
|
||||||
|
if (Platform.OS === 'android') {
|
||||||
|
setIsModalVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SafeAreaView style={[backgroundStyle, styles.container]}>
|
||||||
|
<StartScreen onExitBtnFunc={exitFunc} isModalVisible={isModalVisible} modalVisibilityFunc={setIsModalVisible}></StartScreen>
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
paddingTop: '30%',
|
||||||
|
height: '100%',
|
||||||
|
paddingLeft: '6%',
|
||||||
|
paddingRight: '6%'
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Main;
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
import React, {FC, useEffect, useState } from "react";
|
||||||
|
import {StyleSheet, Text, View } from "react-native";
|
||||||
|
import Modal from "react-native-modal/dist/modal";
|
||||||
|
|
||||||
|
type NFCModalProps = {
|
||||||
|
isVisible: boolean;
|
||||||
|
onChangeFunc: (val: boolean) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const NFCModal: FC<NFCModalProps> = props => {
|
||||||
|
const {isVisible, onChangeFunc} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal isVisible={isVisible} onSwipeComplete={() => onChangeFunc(!isVisible)} swipeDirection={['up', 'left', 'right', 'down']} style={modalStyle.modalContainer}>
|
||||||
|
<View style={modalStyle.container}>
|
||||||
|
<Text style={modalStyle.header}>Ready to Scan</Text>
|
||||||
|
<Text style={modalStyle.prompt}>Tap your Keycard</Text>
|
||||||
|
</View>
|
||||||
|
</Modal>
|
||||||
|
)};
|
||||||
|
|
||||||
|
const modalStyle = StyleSheet.create({
|
||||||
|
modalContainer: {
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
margin: 0
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
backgroundColor: '#4A646C',
|
||||||
|
height: 250,
|
||||||
|
paddingBottom: 20,
|
||||||
|
alignItems: 'center',
|
||||||
|
borderTopLeftRadius: 10,
|
||||||
|
borderTopRightRadius: 10,
|
||||||
|
borderColor: 'rgba(0, 0, 0, 0.1)',
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
paddingTop: '5%',
|
||||||
|
fontSize: 24
|
||||||
|
},
|
||||||
|
prompt: {
|
||||||
|
paddingTop: '10%',
|
||||||
|
paddingBottom: '10%',
|
||||||
|
fontSize: 15
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default NFCModal;
|
|
@ -0,0 +1,42 @@
|
||||||
|
import {FC } from "react";
|
||||||
|
import { DimensionValue, StyleSheet, Text, TouchableOpacity, View } from "react-native";
|
||||||
|
|
||||||
|
type ButtonProps = {
|
||||||
|
label: string;
|
||||||
|
disabled: boolean;
|
||||||
|
btnColor: string;
|
||||||
|
btnWidth: string;
|
||||||
|
btnJustifyContent: string;
|
||||||
|
onChangeFunc: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Button: FC<ButtonProps> = props => {
|
||||||
|
const {label, disabled, btnColor, btnWidth, btnJustifyContent, onChangeFunc} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={[buttonStyle.textBtnContainer, {width: btnWidth as DimensionValue, justifyContent: btnJustifyContent as any}]}>
|
||||||
|
<TouchableOpacity key={label} disabled={disabled} style={buttonStyle.button} onPress={onChangeFunc}>
|
||||||
|
<Text style={[buttonStyle.title, {color: btnColor}]}>{label}</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
)};
|
||||||
|
|
||||||
|
const buttonStyle = StyleSheet.create({
|
||||||
|
textBtnContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
textAlign: 'center',
|
||||||
|
paddingTop: 25,
|
||||||
|
paddingBottom: 15,
|
||||||
|
justifyContent: 'flex-start'
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 15,
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Button;
|
|
@ -0,0 +1,34 @@
|
||||||
|
import {FC } from "react";
|
||||||
|
import { StyleSheet, Text, View } from "react-native";
|
||||||
|
import NFCModal from "../NFCModal";
|
||||||
|
import Button from "./Button";
|
||||||
|
|
||||||
|
type StartScreenProps = {
|
||||||
|
onExitBtnFunc: () => void;
|
||||||
|
isModalVisible: boolean;
|
||||||
|
modalVisibilityFunc: (val: boolean) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const StartScreen: FC<StartScreenProps> = props => {
|
||||||
|
const {onExitBtnFunc, isModalVisible, modalVisibilityFunc} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<View>
|
||||||
|
<Text style={styles.heading}> We are recruiting Operators to be the founders of a new, self-sovereign world in cyberspace</Text>
|
||||||
|
<Button label="Exit" disabled={false} btnColor="#4A646C" btnWidth="100%" onChangeFunc={onExitBtnFunc} btnJustifyContent='center'></Button>
|
||||||
|
</View>
|
||||||
|
<View>
|
||||||
|
<NFCModal isVisible={isModalVisible} onChangeFunc={modalVisibilityFunc}></NFCModal>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
heading: {
|
||||||
|
textAlign: 'center',
|
||||||
|
fontSize: 16
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default StartScreen;
|
|
@ -1,3 +1,4 @@
|
||||||
{
|
{
|
||||||
"extends": "@react-native/typescript-config/tsconfig.json"
|
"extends": "@react-native/typescript-config/tsconfig.json",
|
||||||
|
"noImplicitAny": false
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue