Only reset SetList in certain situations

This reduces the jitter in the homepage
when you have scrolled down a significant
amount.

Related to #165. Still need to do other
list pages.
This commit is contained in:
Brandon Presley 2023-08-22 09:49:09 +12:00
parent 8e42e9c3e4
commit 672931746b
4 changed files with 154 additions and 72 deletions

View File

@ -1,5 +1,6 @@
import { DateTimePickerAndroid } from "@react-native-community/datetimepicker";
import {
NavigationProp,
RouteProp,
useFocusEffect,
useNavigation,
@ -24,7 +25,7 @@ import { fixNumeric } from "./fix-numeric";
export default function EditSet() {
const { params } = useRoute<RouteProp<HomePageParams, "EditSet">>();
const { set } = params;
const navigation = useNavigation();
const { navigate } = useNavigation<NavigationProp<HomePageParams>>();
const [settings, setSettings] = useState<Settings>({} as Settings);
const [name, setName] = useState(set.name);
const [reps, setReps] = useState(set.reps?.toString());
@ -63,20 +64,18 @@ export default function EditSet() {
[settings]
);
const added = useCallback(
async (value: GymSet) => {
startTimer(value.name);
console.log(`${EditSet.name}.add`, { set: value });
if (!settings.notify) return;
if (
value.weight > set.weight ||
(value.reps > set.reps && value.weight === set.weight)
) {
toast("Great work King! That's a new record.");
}
},
[startTimer, set, settings]
);
const added = async (value: GymSet) => {
startTimer(value.name);
console.log(`${EditSet.name}.add`, { set: value });
if (!settings.notify) return;
if (
value.weight > set.weight ||
(value.reps > set.reps && value.weight === set.weight)
) {
toast("Great work King! That's a new record.");
}
navigate("Sets", { added: value.id });
};
const handleSubmit = async () => {
if (!name) return;
@ -104,8 +103,9 @@ export default function EditSet() {
if (typeof set.id !== "number") newSet.created = await getNow();
const saved = await setRepo.save(newSet);
if (typeof set.id !== "number") added(saved);
navigation.goBack();
if (typeof set.id !== "number") return added(saved);
if (createdDirty) navigate("Sets", { reset: saved.id });
else navigate("Sets", { refresh: saved.id });
};
const changeImage = useCallback(async () => {

View File

@ -1,9 +1,10 @@
import {
NavigationProp,
useFocusEffect,
RouteProp,
useNavigation,
useRoute,
} from "@react-navigation/native";
import { useCallback, useState } from "react";
import { useCallback, useEffect, useMemo, useState } from "react";
import { FlatList } from "react-native";
import { List } from "react-native-paper";
import { Like } from "typeorm";
@ -18,33 +19,77 @@ import SetItem from "./SetItem";
import Settings from "./settings";
export default function SetList() {
const [refreshing, setRefreshing] = useState(false);
const [sets, setSets] = useState<GymSet[]>([]);
const [offset, setOffset] = useState(0);
const [term, setTerm] = useState("");
const [end, setEnd] = useState(false);
const [settings, setSettings] = useState<Settings>();
const [ids, setIds] = useState<number[]>([]);
const navigation = useNavigation<NavigationProp<HomePageParams>>();
const { params } = useRoute<RouteProp<HomePageParams, "Sets">>();
const [term, setTerm] = useState(params?.search || "");
const refresh = useCallback(async (value: string) => {
const newSets = await setRepo.find({
where: { name: Like(`%${value.trim()}%`), hidden: 0 as any },
take: LIMIT,
skip: 0,
order: { created: "DESC" },
});
console.log(`${SetList.name}.refresh:`, { value });
const refresh = async ({
value,
take,
skip,
}: {
value: string;
take: number;
skip: number;
}) => {
setRefreshing(true);
const newSets = await setRepo
.find({
where: { name: Like(`%${value.trim()}%`), hidden: 0 as any },
take,
skip,
order: { created: "DESC" },
})
.finally(() => setRefreshing(false));
console.log(`${SetList.name}.refresh:`, { value, take, offset });
setSets(newSets);
setOffset(0);
setEnd(false);
};
useEffect(() => {
settingsRepo.findOne({ where: {} }).then(setSettings);
refresh({
take: LIMIT,
value: "",
skip: 0,
});
/* eslint-disable react-hooks/exhaustive-deps */
}, []);
useFocusEffect(
useCallback(() => {
refresh(term);
settingsRepo.findOne({ where: {} }).then(setSettings);
}, [refresh, term])
);
const search = (value: string) => {
setTerm(value);
setOffset(0);
refresh({
skip: 0,
take: LIMIT,
value,
});
};
useEffect(() => {
if (!params) return;
console.log({ params });
if (params.search) search(params.search);
else if (params.refresh)
refresh({
skip: 0,
take: offset,
value: term,
});
else if (params.reset)
refresh({
skip: 0,
take: LIMIT,
value: term,
});
/* eslint-disable react-hooks/exhaustive-deps */
}, [params]);
const renderItem = useCallback(
({ item }: { item: GymSet }) => (
@ -59,22 +104,29 @@ export default function SetList() {
[settings, ids]
);
const next = useCallback(async () => {
if (end) return;
const next = async () => {
if (end || refreshing) return;
const newOffset = offset + LIMIT;
console.log(`${SetList.name}.next:`, { offset, newOffset, term });
const newSets = await setRepo.find({
where: { name: Like(`%${term}%`), hidden: 0 as any },
take: LIMIT,
skip: newOffset,
order: { created: "DESC" },
});
setRefreshing(true);
const newSets = await setRepo
.find({
where: { name: Like(`%${term}%`), hidden: 0 as any },
take: LIMIT,
skip: newOffset,
order: { created: "DESC" },
})
.finally(() => setRefreshing(false));
if (newSets.length === 0) return setEnd(true);
if (!sets) return;
setSets([...sets, ...newSets]);
const map = new Map<number, GymSet>();
for (const set of sets) map.set(set.id, set);
for (const set of newSets) map.set(set.id, set);
const unique = Array.from(map.values());
setSets(unique);
if (newSets.length < LIMIT) return setEnd(true);
setOffset(newOffset);
}, [term, end, offset, sets]);
};
const onAdd = useCallback(async () => {
const now = await getNow();
@ -85,14 +137,6 @@ export default function SetList() {
navigation.navigate("EditSet", { set });
}, [navigation, sets]);
const search = useCallback(
(value: string) => {
setTerm(value);
refresh(value);
},
[refresh]
);
const edit = useCallback(() => {
navigation.navigate("EditSets", { ids });
setIds([]);
@ -112,16 +156,47 @@ export default function SetList() {
setIds([]);
}, []);
const remove = useCallback(async () => {
const remove = async () => {
setIds([]);
await setRepo.delete(ids.length > 0 ? ids : {});
await refresh(term);
}, [ids, refresh, term]);
return refresh({
skip: 0,
take: LIMIT,
value: term,
});
};
const select = useCallback(() => {
setIds(sets.map((set) => set.id));
}, [sets]);
const content = useMemo(() => {
if (!settings) return null;
if (sets?.length === 0)
return (
<List.Item
title="No sets yet"
description="A set is a group of repetitions. E.g. 8 reps of Squats."
/>
);
return (
<FlatList
data={sets}
style={{ flex: 1 }}
renderItem={renderItem}
onEndReached={next}
refreshing={false}
onRefresh={() =>
refresh({
skip: 0,
take: LIMIT,
value: term,
})
}
/>
);
}, [sets, settings, term]);
return (
<>
<DrawerHeader name={ids.length > 0 ? `${ids.length} selected` : "Home"}>
@ -136,21 +211,7 @@ export default function SetList() {
</DrawerHeader>
<Page onAdd={onAdd} term={term} search={search}>
{sets?.length === 0 ? (
<List.Item
title="No sets yet"
description="A set is a group of repetitions. E.g. 8 reps of Squats."
/>
) : (
settings && (
<FlatList
data={sets}
style={{ flex: 1 }}
renderItem={renderItem}
onEndReached={next}
/>
)
)}
{content}
</Page>
</>
);

View File

@ -5,6 +5,7 @@ import { List, Menu, RadioButton, useTheme } from "react-native-paper";
import { Like } from "typeorm";
import CountMany from "./count-many";
import { getNow, setRepo } from "./db";
import { HomePageParams } from "./home-page-params";
import { PlanPageParams } from "./plan-page-params";
import { toast } from "./toast";
@ -20,6 +21,8 @@ export default function StartPlanItem(props: Props) {
const [anchor, setAnchor] = useState({ x: 0, y: 0 });
const [showMenu, setShowMenu] = useState(false);
const { navigate } = useNavigation<NavigationProp<PlanPageParams>>();
const { navigate: navigateHome } =
useNavigation<NavigationProp<HomePageParams>>();
const undo = useCallback(async () => {
const now = await getNow();
@ -62,6 +65,11 @@ export default function StartPlanItem(props: Props) {
navigate("EditSet", { set: first });
}, [item.name, navigate]);
const view = () => {
setShowMenu(false);
navigateHome("Sets", { search: item.name });
};
const left = useCallback(
() => (
<View style={{ alignItems: "center", justifyContent: "center" }}>
@ -89,6 +97,7 @@ export default function StartPlanItem(props: Props) {
visible={showMenu}
onDismiss={() => setShowMenu(false)}
>
<Menu.Item leadingIcon="visibility" onPress={view} title="View" />
<Menu.Item leadingIcon="edit" onPress={edit} title="Edit" />
<Menu.Item leadingIcon="undo" onPress={undo} title="Undo" />
</Menu>

View File

@ -1,7 +1,19 @@
import GymSet from "./gym-set";
export type HomePageParams = {
Sets: {};
Sets: {
search?: string;
/**
* Reload the current list with limit = offset
*/
refresh?: number;
/**
* Reload the list with limit = 0
*/
reset?: number;
};
EditSet: {
set: GymSet;
};