Skip to content

Commit

Permalink
QR History & Bug Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
DigiWorm0 committed Mar 1, 2022
1 parent cf6a64f commit f787946
Show file tree
Hide file tree
Showing 29 changed files with 343 additions and 124 deletions.
11 changes: 10 additions & 1 deletion api/TBA.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Linking } from 'react-native';
import { TBAEvent, TBAMatch, TBAMedia, TBARankings, TBAStatus, TBATeam } from '../types/TBAModels';
import { TBAEvent, TBAMatch, TBAMedia, TBARankings, TBAStatus, TBATeam, TBAZebra } from '../types/TBAModels';

const API_KEY = "i90dAcKHXvQ9havypHJKeGY8O1tfymFpaW1Po3RGYpvoMTRVwtiUsUFaLmstCDp3";
const URL_PREFIX = "https://www.thebluealliance.com/api/v3/";
Expand Down Expand Up @@ -33,6 +33,15 @@ export default class TBA {
return TBA._fetch<TBAEvent[]>("events/" + year);
}

/**
* Fetches Zebra Motionworks data for a match
* @param matchID - ID of the match
* @returns
*/
static async getZebra(matchID: string) {
return TBA._fetch<TBAZebra>("match/" + matchID + "/zebra_motionworks");
}

/**
* Fetches all teams at a given event
* @param eventID - ID of the event
Expand Down
8 changes: 4 additions & 4 deletions api/TBAAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,14 +163,14 @@ export async function DownloadMedia(teamID: string, year: number) {
await FileSystem.writeAsStringAsync(path, media.details.base64Image, {
encoding: FileSystem.EncodingType.Base64,
});

mediaPaths.push(path);
}
else if (media.direct_url) {
const path = FileSystem.documentDirectory + mediaID + ".jpg";
console.log("DOWNLOAD: " + media.direct_url);
await FileSystem.downloadAsync(media.direct_url, path);
mediaPaths.push(path);
const download = await FileSystem.downloadAsync(media.direct_url, path);
console.log("DOWNLOAD: " + media.direct_url + " (" + download.status + ")");
if (download.status === 200)
mediaPaths.push(path);
}
}

Expand Down
Binary file added assets/images/bolt.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/images/tba_lamp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion components/common/StandardButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default function StandardButton(props: ButtonProps) {
{/* FA Icon */}
{props.iconType ?
<View style={styles.buttonIconFA} >
<MaterialIcons name={props.iconType} size={30} style={{ color: palette.textPrimary }} />
<MaterialIcons name={props.iconType} size={28} style={{ color: palette.textPrimary }} />
</View>
: null}

Expand Down
34 changes: 26 additions & 8 deletions components/containers/PanZoomContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ export default function PanZoomContainer(props: ViewProps) {
// Hooks
const deviceWidth = Dimensions.get('window').width;
const deviceHeight = Dimensions.get('window').height;
const SIZE = deviceWidth;

const pinchRef = React.createRef();
const panRef = React.createRef();

const startPan = { x: useSharedValue(0), y: useSharedValue(deviceHeight / 6) };
const endPan = { x: useSharedValue(0), y: useSharedValue(deviceHeight / 6) };
const startZoom = useSharedValue(1);
Expand All @@ -26,6 +29,12 @@ export default function PanZoomContainer(props: ViewProps) {
onActive: (event, ctx: any) => {
endPan.x.value = startPan.x.value + event.translationX / endZoom.value;
endPan.y.value = startPan.y.value + event.translationY / endZoom.value;

/*
const deltaX = (SIZE / 2) - ((SIZE * endZoom.value) / 2) + (endPan.x.value * endZoom.value);
if (deltaX > 0) {
startPan.x.value -= deltaX;
}*/
},
onEnd: (event, ctx) => {
startPan.x.value = endPan.x.value;
Expand All @@ -38,13 +47,13 @@ export default function PanZoomContainer(props: ViewProps) {
},
onActive: (event, ctx) => {
endZoom.value = startZoom.value * event.scale;
endPan.x.value = endZoom.value * endPan.x.value;
endPan.y.value = endZoom.value * endPan.y.value;
if (endZoom.value < 1)
endZoom.value = Math.pow(endZoom.value, 0.5);
},
onEnd: (event, ctx) => {
if (endZoom.value < 1) {
startZoom.value = withSpring(1);
endZoom.value = withSpring(1);
startZoom.value = 1;
endZoom.value = withSpring(1, { overshootClamping: false });
} else {
startZoom.value = endZoom.value;
}
Expand All @@ -64,12 +73,21 @@ export default function PanZoomContainer(props: ViewProps) {
});

return (
<PinchGestureHandler onGestureEvent={zoomGestureHandler} ref={pinchRef}>
<PanGestureHandler
onGestureEvent={panGestureHandler}
maxPointers={2}
avgTouches
minDist={10}
ref={panRef}>

<Animated.View style={[zoomStyle, { height: deviceHeight }]}>
<PanGestureHandler onGestureEvent={panGestureHandler} simultaneousHandlers={pinchRef}>
<PinchGestureHandler
onGestureEvent={zoomGestureHandler}
simultaneousHandlers={panRef}
ref={pinchRef}>
<Animated.View style={panStyle} {...props} />
</PanGestureHandler>
</PinchGestureHandler>
</Animated.View>
</PinchGestureHandler>
</PanGestureHandler>
);
}
56 changes: 51 additions & 5 deletions hooks/useCompressedData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,63 @@ export function getChecksum(data: ScoutingData[]) {
}, 0).toString();
}

export function useDataImporter() {
export function useQRImporter() {
const [event] = useEvent();
const [scoutingData, setScoutingData] = useScoutingData();
const [importedIDs, setImportedIDs] = React.useState([] as string[]);

const importJsonData = async (data: string) => {
const importData = async (data: string) => {
try {
// Decompress
const splitData = data.split("|");
if (splitData.length <= 0) {
ToastAndroid.show("Invalid QR code", ToastAndroid.SHORT);
return;
}
const exportID = splitData.shift() as string;
const scouts = splitData.map((data) => {
const split = data.split(",");
if (split.length < 2)
return undefined;
return {
teamID: split[0],
matchID: split[1],
values: split.slice(2).map((val) => parseInt(val))
} as ScoutingData;
}).filter((scout) => scout !== undefined) as ScoutingData[];


// Check Duplicates
if (importedIDs.includes(exportID)) {
return;
}
importedIDs.push(exportID);
setImportedIDs(importedIDs);

// Append Data
scoutingData.push(...scouts);
setScoutingData(scoutingData);
ToastAndroid.show("Imported " + scouts.length + " matches.", ToastAndroid.SHORT);
}
catch (e) {
ToastAndroid.show("Invalid Data Import", ToastAndroid.SHORT);
}
};

return importData;
}

export function useJSONImporter() {
const [event] = useEvent();
const [scoutingData, setScoutingData] = useScoutingData();
const [importedIDs, setImportedIDs] = React.useState([] as string[]);

const importData = async (data: string) => {
try {
// Decompress
const decompressedData = JSON.parse(data) as ExportData;
if (!decompressedData) {
ToastAndroid.show("Invalid QR code", ToastAndroid.SHORT);
ToastAndroid.show("Invalid JSON Data", ToastAndroid.SHORT);
return;
}

Expand All @@ -50,10 +96,10 @@ export function useDataImporter() {
}
};

return importJsonData;
return importData;
}

export function useDataExporter() {
export function useJSONExporter() {
const [scoutingData] = useScoutingData();
const [event] = useEvent();

Expand Down
2 changes: 1 addition & 1 deletion hooks/usePalette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const PaletteContext = React.createContext({
});

export function PaletteProvider(props: { children: React.ReactNode }) {
const [palette, setPalette] = useStorage<Palette>("palette", DEFAULT_PALETTE);
const [palette, setPalette] = useStorage<Palette>("palette", Object.assign({}, DEFAULT_PALETTE));
const [version, setVersion] = React.useState(0);

const setPaletteFromContext = (newPalette: Palette) => {
Expand Down
12 changes: 12 additions & 0 deletions hooks/useQRHistory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { QRHistory } from "../types/OtherTypes";
import useStorage from "./useStorage";

/**
* Grabs template data as a react hook
* @param templateType - Type of the template
* @returns current template data and a setting function
*/
export default function useQRHistory(): [QRHistory[], (history: QRHistory[]) => Promise<void>] {
const [qrHistory, setQRHistory] = useStorage<QRHistory[]>("qrhistory", []);
return [qrHistory, setQRHistory];
}
1 change: 0 additions & 1 deletion hooks/useStats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ export function useEventStats(): [Stat[], number] {
const teamScoutingData = scoutingData.filter((data) => data.teamID === teamID);
const teamValues = getScoutingValues(teamScoutingData, i);


let teamAvg = 0;
if (teamValues.length > 0)
teamAvg = teamValues.reduce((prev, cur) => prev + cur) / teamValues.length;
Expand Down
2 changes: 2 additions & 0 deletions navigation/RootNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import YearScreen from "../screens/Settings/Download/YearScreen";
import { PaletteScreen } from "../screens/Settings/PaletteScreen";
import EditTemplateScreen from "../screens/Settings/Template/EditTemplateScreen";
import ElementChooserScreen from "../screens/Settings/Template/ElementChooserScreen";
import ExportQRHistory from "../screens/Sharing/ExportQRHistory";
import ExportQRScreen from "../screens/Sharing/ExportQRScreen";
import ImportQRScreen from "../screens/Sharing/ImportQRScreen";
import PrintSummaryScreen from "../screens/Sharing/PrintSummaryScreen";
Expand Down Expand Up @@ -91,6 +92,7 @@ export default function RootNavigator() {
<Stack.Screen name="Scout" component={ScoutingScreen} />
<Stack.Screen name="DefaultTeam" component={DefaultTeamScreen} />
<Stack.Screen name="ExportQR" component={ExportQRScreen} />
<Stack.Screen name="ExportQRHistory" component={ExportQRHistory} />
<Stack.Screen name="ImportQR" component={ImportQRScreen} />
<Stack.Screen name="PrintSummaryScreen" component={PrintSummaryScreen} />
<Stack.Screen name="About" component={AboutScreen} />
Expand Down
3 changes: 2 additions & 1 deletion navigation/RootParamList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ export type RootNavParamList = {
TeamSelect: { matchID: string },
Scout: { teamID: string, matchID: string },
DefaultTeam: undefined,
ExportQR: undefined,
ExportQR: (undefined | { scoutIDs: string[] }),
ExportQRHistory: undefined,
ImportQR: undefined,
PrintSummaryScreen: undefined,
About: undefined,
Expand Down
11 changes: 6 additions & 5 deletions navigation/TabNavigator.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { MaterialIcons } from '@expo/vector-icons';
import { BottomTabBarButtonProps, createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import * as React from 'react';
import { TouchableNativeFeedback, View } from 'react-native';
import { PixelRatio, TouchableNativeFeedback, View } from 'react-native';
import { usePalette } from '../hooks/usePalette';
import MatchesScreen from '../screens/Matches/MatchesScreen';
import SettingsScreen from '../screens/Settings/SettingsScreen';
Expand All @@ -13,6 +13,8 @@ const Tab = createBottomTabNavigator();
export default function TabNavigator() {
const [palette] = usePalette();

const dpi = PixelRatio.get();

const buttonNativeFeedback = ({ children, style, ...props }: BottomTabBarButtonProps) => (
<TouchableNativeFeedback
{...props}
Expand All @@ -23,7 +25,6 @@ export default function TabNavigator() {
);

return (

<Tab.Navigator
initialRouteName="Matches"
sceneContainerStyle={{
Expand All @@ -36,12 +37,12 @@ export default function TabNavigator() {
tabBarActiveTintColor: palette.navigationTextSelected,
tabBarInactiveTintColor: palette.navigationText,
tabBarStyle: {
height: 60,
height: 170 / dpi,
borderTopWidth: 0
},
tabBarLabelStyle: {
marginBottom: 8,
marginTop: -8
marginTop: -8,
marginBottom: 8
},
unmountOnBlur: false,
tabBarButton: buttonNativeFeedback
Expand Down
4 changes: 2 additions & 2 deletions screens/Matches/TeamPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ export default function TeamPreview(props: { teamID: string }) {
<View style={{ padding: 1 }}>
<View style={[styles.thumbnail, { backgroundColor: palette.innerBox }]}>
<MaterialIcons
name="block"
size={40}
name="image-not-supported"
size={30}
color={palette.navigationText} />
</View>

Expand Down
13 changes: 9 additions & 4 deletions screens/Scout/ScoutingScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useNavigation } from '@react-navigation/native';
import * as React from 'react';
import { Alert, Image, StyleSheet, View } from 'react-native';
import { Alert, Image, StyleSheet, Vibration, View } from 'react-native';
import { ScrollView } from 'react-native-gesture-handler';
import Button from '../../components/common/Button';
import ScoutingElement from '../../components/elements/ScoutingElement';
Expand All @@ -27,22 +27,27 @@ export default function ScoutingScreen({ route }: any) {
}
const onSubmit = () => {
let data: ScoutingData = {
id: "s_" + route.params.matchID + "_" + route.params.teamID,
teamID: route.params.teamID,
matchID: route.params.matchID,
values: template.map(elem => elem.value).filter(val => val != undefined) as number[]
};
setScoutingDataHook([...scoutingData, data]);

navigator.goBack();
navigator.goBack();
if (navigator.canGoBack())
navigator.goBack();
if (navigator.canGoBack())
navigator.goBack();
Vibration.vibrate(200);
Alert.alert("Success", "Data has been saved to storage", [
{
text: "Undo",
onPress: () => {
getScoutingData().then((data) => {
if (data) {
data.splice(scoutingData.length - 1, 1);
const oldData = data.splice(scoutingData.length - 1, 1);
setScoutingData(scoutingData);
Vibration.vibrate(200);
Alert.alert("Success", "Last round has been cleared");
}
});
Expand Down
29 changes: 1 addition & 28 deletions screens/Settings/AboutScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,13 @@
import { MaterialIcons } from "@expo/vector-icons";
import * as Application from 'expo-application';
import { Accelerometer } from 'expo-sensors';
import * as React from "react";
import { Image, StyleSheet } from "react-native";
import { StyleSheet } from "react-native";
import { ScrollView } from "react-native-gesture-handler";
import Subtitle from "../../components/text/Subtitle";
import Text from "../../components/text/Text";
import Title from "../../components/text/Title";

export default function AboutScreen() {
const [accelerometer, setAccelerometer] = React.useState({ x: 0, y: 0, z: 0 });
const [isEasterEgg, setEasterEgg] = React.useState(false);

Accelerometer.setUpdateInterval(1000);
React.useEffect(() => {
const subscription = Accelerometer.addListener((data) => {
setAccelerometer(data);
});

return () => {
subscription.remove();
};
}, []);

React.useEffect(() => {
const acceleration = Math.max(accelerometer.x, accelerometer.y, accelerometer.z);
if (acceleration > 1 && !isEasterEgg) {
setEasterEgg(true);
console.log(acceleration);
}
}, [accelerometer, setEasterEgg, isEasterEgg]);

return (
<ScrollView style={styles.container}>
<Title>Blitz Scouter</Title>
Expand All @@ -49,10 +26,6 @@ export default function AboutScreen() {
{"\n"}
[email protected]
</Text>

{isEasterEgg ?
<Image style={{ height: 300, width: 200, margin: 20 }} fadeDuration={1000} source={{ uri: "https://i.redd.it/ru3eqcdd99b81.jpg" }} />
: null}
</ScrollView>)
}

Expand Down
Loading

0 comments on commit f787946

Please sign in to comment.