diff --git a/README.md b/README.md
index d9ee3f1..d4322dc 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,37 @@
-![Blitz Scouter](https://i.imgur.com/eANWZcA.png "Blitz Scouter")
+
+
+
+
+
-# Features
-- Easy to use
-- Import from [The Blue Alliance](https://www.thebluealliance.com/)
-- Import & Export data without internet access
-- Customizable color palette
+
+
+
+
+
+
+
+
+
+
+
+
+
# Download
-.APKs can be downloaded and installed under the [Releases](https://github.com/NB-Blitz/BlitzScouter-Offline/releases) tab
+At the moment, BlitzScouter is only supported on **Android** devices. APKs can be downloaded and installed under the [Releases](https://github.com/NB-Blitz/BlitzScouter/releases) tab.
+
+# Features
+- 🎨 Customizable color palette
+- 📋 Customizable scouting templates
+- 📷 Take and manage robot photos
+- 💾 Take event data from [The Blue Alliance](https://www.thebluealliance.com/) offline
+- 📁 Share data using json, csv, or QR codes
+- 🥊 Quick match summaries for strategy
+- 🧑🤝🧑 Sort and analyze teams
# Building
-Requires [NPM](https://www.npmjs.com/) or [Yarn](https://yarnpkg.com/)
+Requires [Node](https://nodejs.org/en/)
1. Install Expo:
```
npm install --global expo-cli
diff --git a/app.json b/app.json
index 9ce7813..5cfbd76 100644
--- a/app.json
+++ b/app.json
@@ -2,7 +2,7 @@
"expo": {
"name": "Blitz Scouter",
"slug": "BlitzScouter",
- "version": "1.0.3",
+ "version": "1.1.0",
"orientation": "portrait",
"scheme": "blitz",
"icon": "./assets/images/icon.png",
@@ -22,7 +22,7 @@
"ios": {
"supportsTablet": true,
"bundleIdentifier": "com.team5148.blitzscouter",
- "buildNumber": "1.0.3",
+ "buildNumber": "1.1.0",
"icon": "./assets/images/icon.png"
},
"android": {
diff --git a/components/elements/CheckboxElement.tsx b/components/elements/CheckboxElement.tsx
index 0ddb82f..1b3c830 100644
--- a/components/elements/CheckboxElement.tsx
+++ b/components/elements/CheckboxElement.tsx
@@ -29,7 +29,7 @@ export default function CheckboxElement(props: ElementProps) {
// Vibrate
if (isChecked)
- Vibration.vibrate(10);
+ Vibration.vibrate(30);
else
Vibration.vibrate(100);
diff --git a/components/elements/CounterElement.tsx b/components/elements/CounterElement.tsx
index c03a7ca..31d2e22 100644
--- a/components/elements/CounterElement.tsx
+++ b/components/elements/CounterElement.tsx
@@ -34,7 +34,7 @@ export default function CounterElement(props: ElementProps) {
// Vibrate
if (delta > 0)
- Vibration.vibrate(10);
+ Vibration.vibrate(30);
else
Vibration.vibrate(100);
diff --git a/hooks/useCompressedData.ts b/hooks/useCompressedData.ts
index 8ba23e2..78b7265 100644
--- a/hooks/useCompressedData.ts
+++ b/hooks/useCompressedData.ts
@@ -75,21 +75,26 @@ export function useJSONImporter() {
// Check Duplicates
if (importedIDs.includes(decompressedData.exportID)) {
+ ToastAndroid.show("Duplicate JSON Data", ToastAndroid.SHORT);
return;
}
importedIDs.push(decompressedData.exportID);
setImportedIDs(importedIDs);
- // Check Event
- if (decompressedData.eventID !== event.id) {
- ToastAndroid.show("Invalid Event", ToastAndroid.SHORT);
- return;
- }
-
- // Append Data
- scoutingData.push(...decompressedData.scoutingData);
+ /* // Check Event
+ * if (decompressedData.eventID !== event.id) {
+ * ToastAndroid.show("Invalid Event", ToastAndroid.SHORT);
+ * return;
+ * }
+ */
+
+ // Filter & Append Data
+ const uniqueData = decompressedData.scoutingData.filter(scoutA =>
+ scoutingData.findIndex(scoutB => scoutB.id === scoutA.id) === -1
+ );
+ scoutingData.push(...uniqueData);
setScoutingData(scoutingData);
- ToastAndroid.show("Imported " + scoutingData.length + " matches.", ToastAndroid.SHORT);
+ ToastAndroid.show("Imported " + uniqueData.length + " matches.", ToastAndroid.SHORT);
}
catch (e) {
ToastAndroid.show("Invalid Data Import", ToastAndroid.SHORT);
diff --git a/navigation/RootNavigator.tsx b/navigation/RootNavigator.tsx
index acc1127..2901da3 100644
--- a/navigation/RootNavigator.tsx
+++ b/navigation/RootNavigator.tsx
@@ -82,7 +82,7 @@ export default function RootNavigator() {
-
+
diff --git a/navigation/TabNavigator.tsx b/navigation/TabNavigator.tsx
index a77583f..69c004a 100644
--- a/navigation/TabNavigator.tsx
+++ b/navigation/TabNavigator.tsx
@@ -1,7 +1,7 @@
import { MaterialIcons } from '@expo/vector-icons';
import { BottomTabBarButtonProps, createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import * as React from 'react';
-import { PixelRatio, TouchableNativeFeedback, View } from 'react-native';
+import { TouchableNativeFeedback, View } from 'react-native';
import { usePalette } from '../hooks/usePalette';
import MatchesScreen from '../screens/Matches/MatchesScreen';
import SettingsScreen from '../screens/Settings/SettingsScreen';
@@ -13,8 +13,6 @@ const Tab = createBottomTabNavigator();
export default function TabNavigator() {
const [palette] = usePalette();
- const dpi = PixelRatio.get();
-
const buttonNativeFeedback = ({ children, style, ...props }: BottomTabBarButtonProps) => (
{
const index = template.findIndex(e => e.id === element.id);
if (index >= 0)
template[index] = element;
}
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]);
-
- if (navigator.canGoBack())
- navigator.goBack();
- if (navigator.canGoBack())
- navigator.goBack();
- Vibration.vibrate(200);
- Alert.alert("Success", "Data has been saved to storage", [
+ Alert.alert("Are you sure?", "This will save your scouting data to local storage. You won't be able to return to this match in the future", [
{
- text: "Undo",
- onPress: () => {
- getScoutingData().then((data) => {
- if (data) {
- const oldData = data.splice(scoutingData.length - 1, 1);
- setScoutingData(scoutingData);
- Vibration.vibrate(200);
- Alert.alert("Success", "Last round has been cleared");
- }
- });
+ text: "Confirm", onPress: () => {
+ const 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]);
+
+ if (navigator.canGoBack())
+ navigator.goBack();
+ if (navigator.canGoBack())
+ navigator.goBack();
+ Vibration.vibrate(200);
+ Alert.alert("Success", "Data has been saved to storage");
}
},
- {
- text: "OK",
- style: "cancel"
- }
+ { text: "Cancel", style: "cancel" },
], { cancelable: true });
}
@@ -68,11 +61,11 @@ export default function ScoutingScreen({ route }: any) {
{team.mediaPaths.length > 0 ?
{ navigator.navigate("Media", { mediaPath: team.mediaPaths[team.mediaPaths.length - 1] }) }}>
+ onPress={() => { navigator.navigate("Media", { mediaPath: team.mediaPaths[0] }) }}>
+ source={{ uri: team.mediaPaths[0] }}
+ key={team.id + "-0"} />
: null}
@@ -81,6 +74,8 @@ export default function ScoutingScreen({ route }: any) {
+
+
{template.map((element, index) =>
>();
- const [palette] = usePalette();
const [qrHistory, setQRHistory] = useQRHistory();
- const version = React.useState(0);
+ const [version, setVersion] = React.useState(0);
// Sort By Date
React.useEffect(() => {
- qrHistory.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp))
+ qrHistory.sort((a, b) => Date.parse(b.timestamp) - Date.parse(a.timestamp))
}, [qrHistory])
React.useEffect(() => {
- const t = setInterval(() => {
-
- })
+ const interval = setInterval(() => {
+ setVersion(v => v + 1);
+ }, 1000);
+
+ return () => {
+ clearInterval(interval);
+ }
}, [])
return (
QR History
- Recover Previous Scouting Data
+ Share Previous QR Codes
{qrHistory.length > 0 ?
qrHistory.map((scan, index) => {
const date = new Date(scan.timestamp);
@@ -44,7 +46,7 @@ export default function ExportQRHistory({ route }: any) {
return (
{ navigator.push("ExportQR", { scoutIDs: scan.scoutIDs }) }} />
diff --git a/screens/Sharing/ExportQRScreen.tsx b/screens/Sharing/ExportQRScreen.tsx
index a92fe55..4e16126 100644
--- a/screens/Sharing/ExportQRScreen.tsx
+++ b/screens/Sharing/ExportQRScreen.tsx
@@ -64,8 +64,6 @@ export default function ExportQRScreen({ route }: any) {
setQRHistory(qrHistory);
}
-
-
Vibration.vibrate(100);
}
const onHistory = () => {
@@ -91,7 +89,9 @@ export default function ExportQRScreen({ route }: any) {
bgColor="black"
fgColor="white"
/>
- {scoutingChunk.length} / {scoutIDs !== undefined ? scoutingChunk.length : scoutingData.filter((scout) => !(scout.isQRCodeScanned)).length} Matches
+
+ {scoutingChunk.length} out of {scoutIDs !== undefined ? scoutingChunk.length : scoutingData.filter((scout) => !(scout.isQRCodeScanned)).length} Matches
+
diff --git a/screens/Teams/TeamsScreen.tsx b/screens/Teams/TeamsScreen.tsx
index 542ed20..a09aa15 100644
--- a/screens/Teams/TeamsScreen.tsx
+++ b/screens/Teams/TeamsScreen.tsx
@@ -93,7 +93,8 @@ export default function TeamsScreen() {
selectedValue={sortType}
onValueChange={(type) => { onSort(type) }}
dropdownIconColor={palette.background}
- style={{ alignSelf: "flex-end" }}>
+
+ style={{ alignSelf: "flex-end", minWidth: 200, color: palette.background }}>