Skip to content

Commit 827b940

Browse files
committed
feat: Add ability to expand and collapse notes
The exercise notes can take up significant screen real estate. This change adds an expand/collapse button to the notes. The notes start out collapsed.
1 parent bbb5a89 commit 827b940

6 files changed

Lines changed: 94 additions & 30 deletions

File tree

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"typescript.preferences.autoImportSpecifierExcludeRegexes": [
99
"vitest/dist/chunks.*",
1010
"react-native/Libraries/TurboModule/TurboModuleRegistry",
11-
"react-native-reanimated/lib/typescript/Animated"
11+
"react-native-reanimated/lib/typescript/Animated",
12+
"^@material-symbols-react-native/outlined-400$"
1213
]
1314
}

app/.eslintrc.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ module.exports = {
2020
importNames: ['useSelector'],
2121
message: 'Use useSelector from @/store',
2222
},
23+
{
24+
name: '@material-symbols-react-native/outlined-400',
25+
message: 'This import does not work on android -- too big. We need to use the individual imports',
26+
},
2327
],
2428
},
2529
],
Lines changed: 70 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import { SurfaceText } from '@/components/presentation/surface-text';
1+
import { AccordionItem } from '@/components/presentation/accordion-item';
22
import { spacing } from '@/hooks/useAppTheme';
33
import { RecordedExercise } from '@/models/session-models';
4+
import { useState } from 'react';
45
import { View } from 'react-native';
5-
import { Card, Divider, Icon } from 'react-native-paper';
6+
import { Card, Divider, IconButton, Text } from 'react-native-paper';
7+
import Animated, { FadeInDown, FadeOutUp } from 'react-native-reanimated';
68

79
interface ExerciseNotesDisplayProps {
810
exercise: RecordedExercise;
@@ -11,36 +13,79 @@ interface ExerciseNotesDisplayProps {
1113
export default function ExerciseNotesDisplay(props: ExerciseNotesDisplayProps) {
1214
const notes = props.exercise.notes ?? '';
1315
const blueprintNotes = props.exercise.blueprint.notes ?? '';
14-
const previousNotes = props.previousExercise?.notes ?? '';
16+
const previousNotes = props.previousExercise?.notes
17+
? 'Last time: ' + props.previousExercise.notes
18+
: '';
19+
const [expanded, setExpanded] = useState(false);
20+
const unexpandedNotes = notes || previousNotes || blueprintNotes;
1521

16-
if (!notes && !blueprintNotes && !previousNotes) {
22+
const hasNotes = !(!notes && !blueprintNotes && !previousNotes);
23+
if (!hasNotes) {
1724
return undefined;
1825
}
1926
return (
20-
<Card mode="contained" style={{ marginTop: spacing[4] }}>
21-
<Card.Content>
22-
<View style={{ flexDirection: 'row', gap: spacing[2] }}>
23-
<Icon source={'notes'} size={24} />
24-
<View style={{ gap: spacing[2], flex: 1, paddingRight: spacing[2] }}>
25-
{blueprintNotes && (
26-
<Notes value={blueprintNotes} testID="exercise-blueprint-notes" />
27-
)}
28-
{previousNotes && blueprintNotes && <Divider />}
29-
{previousNotes && (
30-
<Notes
31-
value={'Last time: ' + previousNotes}
32-
testID="exercise-previous-notes"
33-
/>
34-
)}
35-
{notes && (previousNotes || blueprintNotes) && <Divider />}
36-
{notes && <Notes value={notes} testID="exercise-notes" />}
37-
</View>
38-
</View>
39-
</Card.Content>
27+
<Card mode="contained" style={[{ marginTop: spacing[4] }]}>
28+
{hasNotes && (
29+
<AccordionItem isExpanded={expanded}>
30+
<Card.Content>
31+
<View
32+
style={{
33+
flexDirection: 'row',
34+
gap: spacing[2],
35+
marginTop: spacing[4],
36+
}}
37+
>
38+
<View
39+
style={{ gap: spacing[2], flex: 1, paddingRight: spacing[2] }}
40+
>
41+
{blueprintNotes && (
42+
<Notes
43+
value={blueprintNotes}
44+
testID="exercise-blueprint-notes"
45+
/>
46+
)}
47+
{previousNotes && blueprintNotes && <Divider />}
48+
{previousNotes && (
49+
<Notes
50+
value={previousNotes}
51+
testID="exercise-previous-notes"
52+
/>
53+
)}
54+
{notes && (previousNotes || blueprintNotes) && <Divider />}
55+
{notes && <Notes value={notes} testID="exercise-notes" />}
56+
</View>
57+
</View>
58+
</Card.Content>
59+
</AccordionItem>
60+
)}
61+
<Card.Actions>
62+
{!expanded && (
63+
<Animated.View
64+
entering={FadeInDown}
65+
exiting={FadeOutUp}
66+
style={{
67+
flex: 1,
68+
alignItems: 'center',
69+
flexDirection: 'row',
70+
overflow: 'hidden',
71+
}}
72+
>
73+
<Text style={{ flex: 1 }} numberOfLines={1} ellipsizeMode="tail">
74+
{unexpandedNotes}
75+
</Text>
76+
</Animated.View>
77+
)}
78+
79+
<IconButton
80+
icon={expanded ? 'unfoldLess' : 'unfoldMore'}
81+
animated
82+
onPress={() => setExpanded((e) => !e)}
83+
/>
84+
</Card.Actions>
4085
</Card>
4186
);
4287
}
4388

4489
function Notes(props: { value: string; testID: string }) {
45-
return <SurfaceText testID={props.testID}>{props.value}</SurfaceText>;
90+
return <Text testID={props.testID}>{props.value}</Text>;
4691
}

app/components/presentation/ms-icon-source.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ import { msExpandCircleUp } from '@material-symbols-react-native/outlined-400/ms
9191
import { msSearch } from '@material-symbols-react-native/outlined-400/msSearch';
9292
import { msDirectionsRun } from '@material-symbols-react-native/outlined-400/msDirectionsRun';
9393
import { msLanguage } from '@material-symbols-react-native/outlined-400/msLanguage';
94+
import { msUnfoldLess } from '@material-symbols-react-native/outlined-400/msUnfoldLess';
95+
import { msUnfoldMore } from '@material-symbols-react-native/outlined-400/msUnfoldMore';
9496

9597
// Importing these icons using the below methods causes android app to crash
9698
// import { msAdd, msArrowDownward } from '@material-symbols-react-native/outlined-400';
@@ -124,6 +126,8 @@ const MaterialSymbols = {
124126
notifications: msNotifications,
125127
plus: msAdd,
126128
star: msStar,
129+
unfoldLess: msUnfoldLess,
130+
unfoldMore: msUnfoldMore,
127131
openInBrowser: msOpenInBrowser,
128132
promptSuggestion: msPromptSuggestion,
129133
send: msSend,

app/components/presentation/weighted-exercise.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,15 @@ export default function WeightedExercise(props: WeightedExerciseProps) {
8383
/>
8484
</Tooltip>
8585
) : null}
86+
{!props.isReadonly ? (
87+
<Tooltip title={t('Notes')}>
88+
<IconButton
89+
testID="exercise-notes-btn"
90+
icon={'notes'}
91+
onPress={() => setNotesDialogOpen(true)}
92+
/>
93+
</Tooltip>
94+
) : null}
8695

8796
<Menu
8897
visible={menuVisible}
@@ -105,7 +114,7 @@ export default function WeightedExercise(props: WeightedExerciseProps) {
105114
title={t('Edit')}
106115
/>
107116
<Menu.Item
108-
testID="exercise-notes-btn"
117+
testID="exercise-notes-more-btn"
109118
title={t('Notes')}
110119
leadingIcon={'notes'}
111120
onPress={() => {
@@ -210,7 +219,10 @@ export default function WeightedExercise(props: WeightedExerciseProps) {
210219
pointerEvents: notesDialogOpen ? 'box-none' : 'none',
211220
}}
212221
>
213-
<Dialog visible={notesDialogOpen}>
222+
<Dialog
223+
visible={notesDialogOpen}
224+
onDismiss={() => setNotesDialogOpen(false)}
225+
>
214226
<Dialog.Title>
215227
<T
216228
keyName="SessionNotesFor{name}"

tests/cypress-tests/cypress/e2e/completing-a-session.cy.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,6 @@ describe('Completing a session', () => {
199199
it('can add notes to an exercise and see them the next time they do that exercise', () => {
200200
cy.contains('Start workout').click()
201201

202-
cy.getByTestId('more-exercise-btn').first().click()
203202
cy.getByTestId('exercise-notes-btn').first().click()
204203
cy.dialog().find('textarea').first().click().type('I am NoteTaker, master of notes')
205204
cy.dialog().findByTestId('save-notes').click()
@@ -214,7 +213,6 @@ describe('Completing a session', () => {
214213
cy.getByTestId('history-edit-workout').click()
215214

216215

217-
cy.getByTestId('more-exercise-btn').first().click()
218216
cy.getByTestId('exercise-notes-btn').first().click()
219217
cy.dialog().find('textarea')
220218
.first()

0 commit comments

Comments
 (0)