Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sfr fixes part 2 #52

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
2 changes: 2 additions & 0 deletions app/src/FormHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ class FormHelper {
static LATEST_FORM = 'current-form';
static ASYNCSTORAGE_COMPETITION_KEY = 'current-competition';
static ASYNCSTORAGE_MATCHES_KEY = 'current-matches';
// used to save progress on a current/wip scouting report
static ASYNCSTORAGE_CURRENT_REPORT_KEY = 'current-report';
static SCOUTING_STYLE = 'scoutingStyle';
static THEME = 'themePreference';
static OLED = 'oled';
Expand Down
59 changes: 52 additions & 7 deletions app/src/components/NoteList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
Alert,
FlatList,
Modal,
Pressable,
Expand All @@ -11,6 +12,7 @@ import React, {useEffect, useState} from 'react';
import {useTheme} from '@react-navigation/native';
import {NoteStructureWithMatchNumber, OfflineNote} from '../database/Notes';
import Svg, {Path} from 'react-native-svg';
import {EditNoteModal} from './modals/EditNoteModal';

export enum FilterType {
// todo: allow note list to accept and display team #
Expand All @@ -29,17 +31,32 @@ export const NoteList = ({
}) => {
const {colors} = useTheme();
const [searchTerm, setSearchTerm] = useState<string>('');
const [filteredNotes, setFilteredNotes] =
const [notesCopy, setNotesCopy] =
useState<(NoteStructureWithMatchNumber | OfflineNote)[]>(notes);
const [filteredNotes, setFilteredNotes] = useState<
{
note: NoteStructureWithMatchNumber | OfflineNote;
index: number;
}[]
>(notes.map((note, i) => ({note, index: i})));
const [filterBy, setFilterBy] = useState<FilterType>(FilterType.TEXT);
const [filterModalVisible, setFilterModalVisible] = useState<boolean>(false);

const [editModalVisible, setEditModalVisible] = useState<boolean>(false);
const [currentNote, setCurrentNote] =
useState<NoteStructureWithMatchNumber | null>(null);
const [currentNoteIndex, setCurrentNoteIndex] = useState<number>(-1);

useEffect(() => {
const mapped = notesCopy.map((note, i) => ({
note,
index: i,
}));
if (searchTerm === '') {
setFilteredNotes(notes);
setFilteredNotes(mapped);
return;
}
const filtered = notes.filter(note => {
const filtered = mapped.filter(({note}) => {
if (filterBy === FilterType.MATCH_NUMBER) {
return (
note.match_number?.toString().includes(searchTerm) ||
Expand All @@ -49,7 +66,7 @@ export const NoteList = ({
return note.content.toLowerCase().includes(searchTerm.toLowerCase());
});
setFilteredNotes(filtered);
}, [searchTerm]);
}, [searchTerm, filterBy, notesCopy]);

const styles = StyleSheet.create({
container: {
Expand Down Expand Up @@ -110,8 +127,8 @@ export const NoteList = ({
}}>
<Svg fill="currentColor" viewBox="0 0 16 16" width="20" height="20">
<Path
fill="gray"
d="M11.5 15a.5.5 0 0 0 .5-.5V2.707l3.146 3.147a.5.5 0 0 0 .708-.708l-4-4a.5.5 0 0 0-.708 0l-4 4a.5.5 0 1 0 .708.708L11 2.707V14.5a.5.5 0 0 0 .5.5m-7-14a.5.5 0 0 1 .5.5v11.793l3.146-3.147a.5.5 0 0 1 .708.708l-4 4a.5.5 0 0 1-.708 0l-4-4a.5.5 0 0 1 .708-.708L4 13.293V1.5a.5.5 0 0 1 .5-.5"
fill={'gray'}
d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5m0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5m0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5"
/>
</Svg>
</Pressable>
Expand All @@ -134,7 +151,7 @@ export const NoteList = ({
{filteredNotes.length > 0 && (
<FlatList
data={filteredNotes}
renderItem={({item}) => (
renderItem={({item: {note: item, index}}) => (
<Pressable
style={{
backgroundColor: colors.card,
Expand All @@ -143,6 +160,15 @@ export const NoteList = ({
borderColor: colors.border,
borderRadius: 10,
margin: '5%',
}}
onLongPress={() => {
if (!('id' in item)) {
Alert.alert('You cannot edit an offline note!');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can you not edit an offline note?

return;
}
setCurrentNote(item);
setCurrentNoteIndex(index);
setEditModalVisible(true);
}}>
<View style={{flexDirection: 'row'}}>
<Text style={{color: colors.text, fontWeight: 'bold'}}>
Expand Down Expand Up @@ -236,6 +262,25 @@ export const NoteList = ({
</View>
</Modal>
)}
{editModalVisible && currentNote && (
<EditNoteModal
visible={editModalVisible}
note={currentNote}
onSave={note => {
setEditModalVisible(false);
setCurrentNote(null);
setCurrentNoteIndex(-1);
const newNotes = [...notesCopy];
newNotes[currentNoteIndex] = note;
setNotesCopy(newNotes);
}}
onCancel={() => {
setEditModalVisible(false);
setCurrentNote(null);
setCurrentNoteIndex(-1);
}}
/>
)}
</View>
);
};
9 changes: 9 additions & 0 deletions app/src/components/form/Stepper.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {View, StyleSheet, TouchableOpacity} from 'react-native';
import {Text} from 'react-native';
import Question from './Question';
import {useTheme} from '@react-navigation/native';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';

function Stepper(props) {
const {colors} = useTheme();
Expand All @@ -12,9 +13,17 @@ function Stepper(props) {
props.onValueChange(
props.value === '' ? 0 : Number.parseInt(props.value, 10) + 1,
);
ReactNativeHapticFeedback.trigger('impactMedium', {
enableVibrateFallback: true,
ignoreAndroidSystemSettings: false,
});
} else {
if (props.value > 0) {
props.onValueChange(props.value - 1);
ReactNativeHapticFeedback.trigger('impactSoft', {
enableVibrateFallback: true,
ignoreAndroidSystemSettings: false,
});
}
}
};
Expand Down
152 changes: 152 additions & 0 deletions app/src/components/modals/EditNoteModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import {
Modal,
Pressable,
StyleSheet,
Text,
TextInput,
View,
} from 'react-native';
import {useTheme} from '@react-navigation/native';
import {useState} from 'react';
import {NoteStructureWithMatchNumber} from '../../database/Notes';
import Svg, {Path} from 'react-native-svg';
import {supabase} from '../../lib/supabase';

export const EditNoteModal = ({
alaninnovates marked this conversation as resolved.
Show resolved Hide resolved
visible,
note,
onSave,
onCancel,
}: {
visible: boolean;
note: NoteStructureWithMatchNumber;
onSave: (note: NoteStructureWithMatchNumber) => void;
onCancel: () => void;
}) => {
const {colors} = useTheme();
const [content, setContent] = useState<string>(note.content);

const saveNote = async () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: not actually saving to DB. Judging by the RLS, I would guess it's because you need a policy allowing authenticated users to update their notes.

Screenshot 2024-04-10 at 10 55 22 PM

await supabase.from('notes').update({content}).eq('id', note.id);
// await supabase.from('notes_edits').insert({
// note_id: note.id,
// content: note.content,
// new_content: content,
// });
onSave({...note, content});
};

const styles = StyleSheet.create({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a huge fan of how much space there is underneath the save button. maybe rewrite using KeyboardAvoidingView?
Screenshot 2024-04-10 at 10 56 50 PM

container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
modal: {
backgroundColor: colors.card,
padding: 20,
borderRadius: 10,
width: '80%',
},
backgroundCovering: {
backgroundColor: 'rgba(0, 0, 0, 0.5)',
position: 'absolute',
top: 0,
right: 0,
bottom: 0,
left: 0,
},
input: {
backgroundColor: colors.background,
padding: 10,
borderRadius: 5,
marginVertical: 10,
height: '30%',
color: colors.text,
},
button: {
backgroundColor: colors.primary,
padding: 10,
borderRadius: 5,
marginVertical: 10,
alignItems: 'center',
},
disabledButton: {
backgroundColor: 'gray',
padding: 10,
borderRadius: 5,
marginVertical: 10,
alignItems: 'center',
},
buttonText: {
color: colors.background,
},
});

return (
<Modal animationType="slide" transparent={true} visible={visible}>
<View style={styles.backgroundCovering} />
<View style={styles.container}>
<View style={styles.modal}>
<View
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
}}>
<Text
style={{color: colors.text, fontWeight: 'bold', fontSize: 24}}>
Edit Note
</Text>
<View
style={{
flex: 1,
}}
/>
<Pressable
onPress={onCancel}
style={{
backgroundColor: colors.notification,
padding: 10,
borderRadius: 100,
alignItems: 'center',
}}>
<Svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<Path
d="M6 18L18 6M6 6l12 12"
stroke="white"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</Svg>
</Pressable>
</View>
<Text style={{color: colors.text, fontSize: 18}}>
Match: {note.match_number}
</Text>
<Text style={{color: colors.text, fontSize: 18}}>
Team: {note.team_number}
</Text>
<TextInput
style={styles.input}
alaninnovates marked this conversation as resolved.
Show resolved Hide resolved
multiline={true}
value={content}
onChange={e => setContent(e.nativeEvent.text)}
/>
<Pressable
style={
!content || content === note.content
? styles.button
: styles.disabledButton
}
onPress={saveNote}
disabled={!content || content === note.content}>
<Text style={styles.buttonText}>Save</Text>
</Pressable>
</View>
</View>
</Modal>
);
};
3 changes: 2 additions & 1 deletion app/src/screens/home-flow/components/NoteInputModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ export const NoteInputModal = ({
<TextInput
multiline={true}
style={{flex: 1, color: colors.text, fontSize: 20}}
onChangeText={text => {
onChange={evt => {
const text = evt.nativeEvent.text;
setLocalContent(text);
setNoteContents({
...noteContents,
Expand Down
29 changes: 28 additions & 1 deletion app/src/screens/scouting-flow/ScoutingFlow.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,18 @@ function ScoutingFlow({navigation, route, resetTimer}) {
setFormId(comp.formId);
setFormStructure(comp.form);
setCompetition(comp);
initForm(comp.form);
const storedFormData = await AsyncStorage.getItem(
alaninnovates marked this conversation as resolved.
Show resolved Hide resolved
FormHelper.ASYNCSTORAGE_CURRENT_REPORT_KEY,
);
console.log('storedFormData: ', storedFormData);
if (storedFormData != null) {
const data = JSON.parse(storedFormData);
setArrayData(data.arrayData);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a toast or some other indicator to the user that the form was loaded from offline storage / cache (use a more user-friendly term for this).

setMatch(data.match);
setTeam(data.team);
} else {
initForm(comp.form);
}
} else {
setIsCompetitionHappening(false);
}
Expand Down Expand Up @@ -357,6 +368,22 @@ function ScoutingFlow({navigation, route, resetTimer}) {
//console.log('dict: ', dict);
}, [formStructure]);

useEffect(() => {
if (arrayData == null) {
return;
}
(async () => {
await AsyncStorage.setItem(
FormHelper.ASYNCSTORAGE_CURRENT_REPORT_KEY,
JSON.stringify({
arrayData,
match,
team,
}),
);
})();
}, [arrayData]);

const styles = StyleSheet.create({
textInput: {
borderColor: 'gray',
Expand Down
Loading