Add settings for default fields on Exercise - 2.1 🚀

Closes #188
This commit is contained in:
Brandon Presley 2023-11-15 14:03:43 +13:00
parent f6a75d89cd
commit 608bb3e97a
10 changed files with 151 additions and 32 deletions

View File

@ -8,7 +8,7 @@ import {
import { useCallback, useRef, useState } from "react";
import { ScrollView, TextInput, View } from "react-native";
import DocumentPicker from "react-native-document-picker";
import { Button, Card, TouchableRipple } from "react-native-paper";
import { Button, Card, IconButton, TouchableRipple } from "react-native-paper";
import AppInput from "./AppInput";
import { StackParams } from "./AppStack";
import ConfirmDialog from "./ConfirmDialog";
@ -25,7 +25,8 @@ import { toast } from "./toast";
export default function EditExercise() {
const { params } = useRoute<RouteProp<StackParams, "EditExercise">>();
const [removeImage, setRemoveImage] = useState(false);
const [showRemove, setShowRemove] = useState(false);
const [showRemoveImage, setShowRemoveImage] = useState(false);
const [showDelete, setShowDelete] = useState(false);
const [name, setName] = useState(params.gymSet.name);
const [steps, setSteps] = useState(params.gymSet.steps);
const [uri, setUri] = useState(params.gymSet.image);
@ -45,16 +46,22 @@ export default function EditExercise() {
useFocusEffect(
useCallback(() => {
settingsRepo.findOne({ where: {} }).then(setSettings);
}, [])
settingsRepo.findOne({ where: {} }).then((gotSettings) => {
setSettings(gotSettings);
if (params.gymSet.id) return;
setSets(gotSettings.defaultSets?.toString() ?? "3");
setMinutes(gotSettings.defaultMinutes?.toString() ?? "3");
setSeconds(gotSettings.defaultSeconds?.toString() ?? "30");
});
}, [params.gymSet.id])
);
const update = async () => {
const newExercise = {
name: name || params.gymSet.name,
sets: Number(sets),
minutes: +minutes,
seconds: +seconds,
minutes: Number(minutes),
seconds: Number(seconds),
steps,
image: removeImage ? "" : uri,
} as GymSet;
@ -75,15 +82,20 @@ export default function EditExercise() {
name,
hidden: true,
image: uri,
minutes: minutes ? +minutes : 3,
seconds: seconds ? +seconds : 30,
sets: sets ? +sets : 3,
minutes: minutes ? Number(minutes) : 3,
seconds: seconds ? Number(seconds) : 30,
sets: sets ? Number(sets) : 3,
steps,
created: now,
});
navigate("Exercises");
};
const remove = async () => {
await setRepo.delete({ name: params.gymSet.name });
navigate("Exercises");
};
const save = async () => {
if (params.gymSet.name) return update();
return add();
@ -100,7 +112,7 @@ export default function EditExercise() {
const handleRemove = useCallback(async () => {
setUri("");
setRemoveImage(true);
setShowRemove(false);
setShowRemoveImage(false);
}, []);
const submitName = () => {
@ -112,7 +124,11 @@ export default function EditExercise() {
<>
<StackHeader
title={params.gymSet.name ? "Edit exercise" : "Add exercise"}
/>
>
{typeof params.gymSet.id === "number" ? (
<IconButton onPress={() => setShowDelete(true)} icon="delete" />
) : null}
</StackHeader>
<View style={{ padding: PADDING, flex: 1 }}>
<ScrollView style={{ flex: 1 }}>
<AppInput
@ -175,7 +191,7 @@ export default function EditExercise() {
<TouchableRipple
style={{ marginBottom: MARGIN }}
onPress={changeImage}
onLongPress={() => setShowRemove(true)}
onLongPress={() => setShowRemoveImage(true)}
>
<Card.Cover source={{ uri }} />
</TouchableRipple>
@ -193,14 +209,24 @@ export default function EditExercise() {
<PrimaryButton disabled={!name} icon="content-save" onPress={save}>
Save
</PrimaryButton>
<ConfirmDialog
title="Remove image"
onOk={handleRemove}
show={showRemove}
setShow={setShowRemove}
show={showRemoveImage}
setShow={setShowRemoveImage}
>
Are you sure you want to remove the image?
</ConfirmDialog>
<ConfirmDialog
title="Delete set"
show={showDelete}
onOk={remove}
setShow={setShowDelete}
>
<>Are you sure you want to delete {name}</>
</ConfirmDialog>
</View>
</>
);

View File

@ -49,7 +49,7 @@ export default function EditSet() {
set.created ? new Date(set.created) : new Date()
);
const [createdDirty, setCreatedDirty] = useState(false);
const [showRemove, setShowRemove] = useState(false);
const [showRemoveImage, setShowRemoveImage] = useState(false);
const [removeImage, setRemoveImage] = useState(false);
const [setOptions, setSets] = useState<GymSet[]>([]);
const weightRef = useRef<TextInput>(null);
@ -145,7 +145,7 @@ export default function EditSet() {
const handleRemove = useCallback(async () => {
setNewImage("");
setRemoveImage(true);
setShowRemove(false);
setShowRemoveImage(false);
}, []);
const pickDate = useCallback(() => {
@ -202,14 +202,6 @@ export default function EditSet() {
<IconButton onPress={() => setShowDelete(true)} icon="delete" />
) : null}
</StackHeader>
<ConfirmDialog
title="Delete set"
show={showDelete}
onOk={remove}
setShow={setShowDelete}
>
<>Are you sure you want to delete {name}</>
</ConfirmDialog>
<View style={{ padding: PADDING, flex: 1 }}>
<View>
@ -324,7 +316,7 @@ export default function EditSet() {
<TouchableRipple
style={{ marginBottom: MARGIN }}
onPress={changeImage}
onLongPress={() => setShowRemove(true)}
onLongPress={() => setShowRemoveImage(true)}
>
<Card.Cover source={{ uri: newImage }} />
</TouchableRipple>
@ -353,11 +345,20 @@ export default function EditSet() {
<ConfirmDialog
title="Remove image"
onOk={handleRemove}
show={showRemove}
setShow={setShowRemove}
show={showRemoveImage}
setShow={setShowRemoveImage}
>
Are you sure you want to remove the image?
</ConfirmDialog>
<ConfirmDialog
title="Delete set"
show={showDelete}
onOk={remove}
setShow={setShowDelete}
>
<>Are you sure you want to delete {name}</>
</ConfirmDialog>
</>
);
}

View File

@ -269,6 +269,60 @@ export default function SettingsPage() {
/>
),
},
{
name: "Default sets",
renderItem: (name: string) => (
<AppInput
value={settings.defaultSets?.toString() ?? "3"}
label={name}
onChangeText={(value) => setValue("defaultSets", Number(value))}
onSubmitEditing={async (e) => {
const value = e.nativeEvent.text;
setValue("defaultSets", Number(value));
await update("defaultSets", value);
toast(`New exercises now have ${value} sets by default.`);
}}
keyboardType="numeric"
blurOnSubmit
/>
),
},
{
name: "Default minutes",
renderItem: (name: string) => (
<AppInput
value={settings.defaultMinutes?.toString() ?? "3"}
label={name}
onChangeText={(value) => setValue("defaultMinutes", Number(value))}
onSubmitEditing={async (e) => {
const value = e.nativeEvent.text;
setValue("defaultMinutes", Number(value));
await update("defaultMinutes", value);
toast(`New exercises now wait ${value} minutes by default.`);
}}
keyboardType="numeric"
blurOnSubmit
/>
),
},
{
name: "Default seconds",
renderItem: (name: string) => (
<AppInput
value={settings.defaultSeconds?.toString() ?? "30"}
label={name}
onChangeText={(value) => setValue("defaultSeconds", Number(value))}
onSubmitEditing={async (e) => {
const value = e.nativeEvent.text;
setValue("defaultSeconds", Number(value));
await update("defaultSeconds", value);
toast(`New exercises now wait ${value} seconds by default.`);
}}
keyboardType="numeric"
blurOnSubmit
/>
),
},
{
name: "Rest timers",
renderItem: (name: string) => (

View File

@ -125,8 +125,8 @@ export default function StartPlan() {
await refresh();
if (
settings.notify &&
(+weight > best.weight ||
(Number(reps) > best.reps && +weight === best.weight))
(Number(weight) > best.weight ||
(Number(reps) > best.reps && Number(weight) === best.weight))
) {
toast("Great work King! That's a new record.");
}

View File

@ -85,8 +85,8 @@ android {
applicationId "com.massive"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 36214
versionName "1.188"
versionCode 36216
versionName "2.1"
}
signingConfigs {
release {

View File

@ -37,6 +37,8 @@ import { autoConvert1699948105001 } from "./migrations/1699948105001-auto-conver
import { Plan } from "./plan";
import Settings from "./settings";
import Weight from "./weight";
import { settingsDefaultSets1700009253976 } from "./migrations/1700009253976-settings-default-sets";
import { settingsDefaults1700009729468 } from "./migrations/1700009729468-settings-defaults";
export const AppDataSource = new DataSource({
type: "react-native",
@ -80,5 +82,7 @@ export const AppDataSource = new DataSource({
settingsBackupDir1699839054226,
homeHistoryStartup1699853245534,
autoConvert1699948105001,
settingsDefaultSets1700009253976,
settingsDefaults1700009729468,
],
});

View File

@ -0,0 +1,11 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class settingsDefaultSets1700009253976 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
"ALTER TABLE settings ADD COLUMN defaultSets INTEGER"
);
}
public async down(queryRunner: QueryRunner): Promise<void> {}
}

View File

@ -0,0 +1,14 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class settingsDefaults1700009729468 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
"ALTER TABLE settings ADD COLUMN defaultMinutes INTEGER"
);
await queryRunner.query(
"ALTER TABLE settings ADD COLUMN defaultSeconds INTEGER"
);
}
public async down(queryRunner: QueryRunner): Promise<void> {}
}

View File

@ -1,6 +1,6 @@
{
"name": "massive",
"version": "1.188",
"version": "2.1",
"private": true,
"license": "GPL-3.0-only",
"scripts": {

View File

@ -55,4 +55,13 @@ export default class Settings {
@Column("text")
autoConvert: string | null;
@Column("int")
defaultSets: number | null;
@Column("int")
defaultMinutes: number | null;
@Column("int")
defaultSeconds: number | null;
}