Skip to content

Commit

Permalink
Add and implement QRScreen
Browse files Browse the repository at this point in the history
Builds a basic QR-screen which has the simple functionality to scan
QR-codes of a student-ID and send a companyConnection - request.
Next step is to add a 1-5 rating framework and also implement a QR-code
for the students.

Additional fixes:
Solves problem in profileScreen where ScrollView wouldn't scroll when
dragging on a <TextInput> which has style textAlign: 'center'. Solved
by adding the attribute numberoflines={1} to the <TextInput>.
More on the issue: facebook/react-native#12167
  • Loading branch information
berghdavid committed Oct 25, 2021
1 parent 9975c4d commit 1f7f7bd
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 16 deletions.
14 changes: 13 additions & 1 deletion navigation/BottomTabNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import EventListScreen from "../screens/EventListScreen";
import ProfileScreen from '../screens/ProfileScreen';
import EventDetailsScreen from '../screens/EventDetailsScreen';
import TicketsScreen from '../screens/TicketsScreen';
import QRScreen from '../screens/QRScreen';
import { Ticket } from '../api/tickets';


Expand Down Expand Up @@ -123,7 +124,8 @@ export type ProfileStackParamList = {
},
TicketsScreen: {
tickets: Ticket[];
}
},
QRScreen: undefined,
}
const ProfileStack = createStackNavigator<ProfileStackParamList>();
function ProfileNavigator() {
Expand All @@ -134,11 +136,21 @@ function ProfileNavigator() {
component={ProfileScreen}
options={{ headerTitle: 'Profile' }}
/>
<ProfileStack.Screen
name="EventDetailsScreen"
component={EventDetailsScreen}
options={{ headerTitle: 'Event' }}
/>
<ProfileStack.Screen
name="TicketsScreen"
component={TicketsScreen}
options={{ headerTitle: 'Tickets' }}
/>
<ProfileStack.Screen
name="QRScreen"
component={QRScreen}
options={{ headerTitle: 'QR' }}
/>
</ProfileStack.Navigator>
);
}
40 changes: 25 additions & 15 deletions screens/ProfileScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,32 @@
import React, { useContext, useEffect, useState } from 'react';
import { Dimensions, Image, ScrollView, StyleSheet, TextInput } from 'react-native';

import { Text, View } from '../components/Themed';
import { StackNavigationProp } from '@react-navigation/stack';
import Colors from '../constants/Colors';
import { Ionicons } from '@expo/vector-icons';

import { API } from '../api'
import { Role, UpdateUserDto, User } from '../api/users';
import { Event } from '../api/events';
import { Company, UpdateCompanySelfDto } from '../api/companies';
import { ProfileStackParamList } from '../navigation/BottomTabNavigator';

import ScreenActivityIndicator from '../components/ScreenActivityIndicator';
import { Text, View } from '../components/Themed';
import { ArkadText } from '../components/StyledText';
import { AuthContext } from '../components/AuthContext';

import { StackNavigationProp } from '@react-navigation/stack';
import { ProfileStackParamList } from '../navigation/BottomTabNavigator';
import { Company, UpdateCompanySelfDto } from '../api/companies';
import { EditProfileButton, LogoutButton, ScanQRButton, TicketsButton } from '../components/profileScreen/Buttons';
import { Ionicons } from '@expo/vector-icons';
import { EmptyEventItem } from '../components/profileScreen/EmptyEventItem';
import { BookedEventList } from '../components/profileScreen/BookedEventList';

const { width, height } = Dimensions.get("window");

type profileNavigation = {
export type profileNavigation = {
navigation: StackNavigationProp<ProfileStackParamList, 'ProfileScreen'>
};


export default function ProfileScreen({navigation}: profileNavigation) {
export default function ProfileScreen({ navigation }: profileNavigation) {
const [user, setUser] = useState<User | null>(null);
const [company, setCompany] = useState<Company | null>(null);
const [loading, setLoading] = useState<boolean>(false);
Expand Down Expand Up @@ -63,6 +62,10 @@ export default function ProfileScreen({navigation}: profileNavigation) {
navigation.navigate('TicketsScreen', { tickets });
}

function openQR() {
navigation.navigate('QRScreen');
}

function updateCompany(newComp: Object) {
if(company) {
setCompany(Object.assign(company, newComp));
Expand All @@ -75,10 +78,6 @@ export default function ProfileScreen({navigation}: profileNavigation) {
}
}

function openQR() {
// Open QR-scanner
}

async function editProfile() {
if(user == null) {
setEditingProfile(!editingProfile);
Expand Down Expand Up @@ -139,13 +138,15 @@ export default function ProfileScreen({navigation}: profileNavigation) {
<TextInput
defaultValue={company.name}
style={[styles.text, styles.companyName]}
multiline={true}
editable={false} />

<View style={styles.infoItem}>
<Ionicons name="link" size={16} color="black"/>
<TextInput
defaultValue={company.website != null ? company.website : "www.example.com"}
style={[styles.text, styles.itemText]}
multiline={true}
editable={editingProfile}
onChangeText={text => updateCompany({website: text})} />
</View>
Expand All @@ -169,20 +170,23 @@ export default function ProfileScreen({navigation}: profileNavigation) {
<TextInput
defaultValue={company.hostName != null ? company.hostName : "Host name"}
style={[styles.text, styles.name]}
multiline={true}
editable={false} />

<View style={styles.infoItem}>
<Ionicons name="mail" size={16} color="black"/>
<TextInput
defaultValue={company.hostEmail != null ? company.hostEmail : "host@example.com"}
style={[styles.text, styles.itemText]}
multiline={true}
editable={false} />
</View>
<View style={styles.infoItem}>
<Ionicons name="call" size={16} color="black" />
<TextInput
defaultValue={company.hostPhone ? company.hostPhone : '\u2013'}
style={[styles.text, styles.itemText]}
multiline={true}
editable={false} />
</View>
</View>
Expand All @@ -209,6 +213,7 @@ export default function ProfileScreen({navigation}: profileNavigation) {
defaultValue={user.firstName + " " + user.lastName}
style={[styles.text, styles.name]}
editable={editingProfile}
multiline={true}
onChangeText={text => {
let name = text.split("")
updateUser({
Expand All @@ -232,6 +237,7 @@ export default function ProfileScreen({navigation}: profileNavigation) {
<TextInput
defaultValue={user.phoneNr ? user.phoneNr : '\u2013'}
style={[styles.text, styles.itemText]}
multiline={true}
editable={editingProfile}
onChangeText={text => updateUser({phoneNr: text})} />
</View>
Expand Down Expand Up @@ -279,7 +285,8 @@ const styles = StyleSheet.create({
},
companyName: {
paddingTop: '2%',
fontSize: 24,
fontSize: 28,
fontWeight: 'bold',
color: Colors.darkBlue,
},
name: {
Expand All @@ -292,6 +299,7 @@ const styles = StyleSheet.create({
paddingTop: '2%',
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center'
},
itemText: {
color: Colors.darkBlue,
Expand Down Expand Up @@ -319,7 +327,8 @@ const styles = StyleSheet.create({
width: '100%',
fontSize: 14,
padding: 12,
textAlign: 'left'
textAlign: 'left',
textAlignVertical: 'top'
},
studentContainer: {
alignItems: 'center',
Expand All @@ -337,7 +346,8 @@ const styles = StyleSheet.create({
},
text: {
justifyContent: "center",
textAlign: "center",
textAlignVertical: 'center',
textAlign: 'center',
fontFamily: 'montserrat',
color: Colors.white,
},
Expand Down
142 changes: 142 additions & 0 deletions screens/QRScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { BarCodeScanner } from "expo-barcode-scanner";
import React, { useEffect, useState } from "react";
import { TextInput, StyleSheet, Text, Button, View, Dimensions } from "react-native";
import { ScrollView } from "react-native-gesture-handler";
import { API } from "../api";
import { CompanyCompanyConnectionDto, CreateCompanyConnectionDto } from "../api/companyconnections";
import { ArkadButton } from "../components/Buttons";
import { ArkadText } from "../components/StyledText";
import Colors from "../constants/Colors";
import { profileNavigation } from "./ProfileScreen";

const { width, height } = Dimensions.get("window");

interface QRProps{
type: string,
data: string,
}

export default function QRScreen({ navigation }: profileNavigation) {
const [hasPermission, setHasPermission] = useState<boolean>(false);
const [scanned, setScanned] = useState<boolean>(false);
const [studentID, setStudentID] = useState<number>(-1);
const [description, setDescription] = useState<string>("");

async function getPermission() {
const { status } = await BarCodeScanner.requestPermissionsAsync();
setHasPermission(status === 'granted');
}

useEffect(() => {
getPermission();
}, []);

async function createCompanyConnection() {
const connection: CreateCompanyConnectionDto = {
studentId: studentID,
rating: 5,
comment: description
}
const response: CompanyCompanyConnectionDto = await API.companyconnections.createConnection(connection)
if(response) {
alert(`Successfully connected with student!`);
} else {
alert(`Could not connect with student.`);
}
navigation.goBack()
}

const handleBarCodeScanned = ({ type, data }: QRProps) => {
setScanned(true);
setStudentID(Number(data));
};

if (hasPermission === null) {
return <Text>Requesting for camera permission</Text>;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
if(scanned) {
return (
<View style={styles.container}>
<ArkadText text={"Student ID: " + studentID.toString()} style={styles.id} />

{/* Eventual star rating library */}
<ArkadText text={"Comments"} style={styles.header} />

<View style={styles.descriptionContainer}>
<ScrollView showsVerticalScrollIndicator={false} style={{height: height * 0.2}}>
<TextInput
style={styles.description}
defaultValue={''}
multiline={true}
numberOfLines={8}
editable={true}
onChangeText={text => setDescription(text)} />
</ScrollView>
</View>

<ArkadButton
onPress={createCompanyConnection}
style={styles.button}>
<ArkadText text={"Connect with student"} style={{}}/>
</ArkadButton>
</View>
)
}

return (
<View style={styles.container}>
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
style={StyleSheet.absoluteFillObject} />
{scanned && <Button title={'Tap to Scan Again'} onPress={() => setScanned(false)} />}
</View>
)
}

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center'
},
id: {
paddingTop: '4%',
color: Colors.darkBlue,
fontSize: 24
},
header: {
paddingTop: '20%',
paddingLeft: '7%',
width: '100%',
textAlign: 'left',
fontSize: 16,
color: Colors.darkBlue,
},
descriptionContainer: {
marginTop: '4%',
width: '86%',
borderRadius: 8,
borderColor: Colors.black,
borderWidth: 1,
backgroundColor: Colors.lightGray
},
scroll: {
width: '86%',
},
description: {
color: Colors.darkBlue,
width: '100%',
fontSize: 14,
padding: 12,
textAlign: 'left',
textAlignVertical: 'top',
justifyContent: "center",
fontFamily: 'montserrat',
},
button: {
marginTop: '20%',
width: '86%',
},
});

0 comments on commit 1f7f7bd

Please sign in to comment.