Massive/SetList.tsx
Brandon Presley 24fd687856 Add custom event for settings being updated - 1.150
When we used navigation params to decide whether
or not to update the SetList, we broke reacting to
settings changes. This is because we used to update settings
whenever the user navigated to the page.
2023-08-22 11:43:35 +12:00

223 lines
5.6 KiB
TypeScript

import {
NavigationProp,
RouteProp,
useNavigation,
useRoute,
} from "@react-navigation/native";
import { useCallback, useEffect, useMemo, useState } from "react";
import { DeviceEventEmitter, FlatList } from "react-native";
import { List } from "react-native-paper";
import { Like } from "typeorm";
import { LIMIT } from "./constants";
import { getNow, setRepo, settingsRepo } from "./db";
import DrawerHeader from "./DrawerHeader";
import GymSet, { defaultSet } from "./gym-set";
import { HomePageParams } from "./home-page-params";
import ListMenu from "./ListMenu";
import Page from "./Page";
import SetItem from "./SetItem";
import Settings, { SETTINGS } from "./settings";
export default function SetList() {
const [refreshing, setRefreshing] = useState(false);
const [sets, setSets] = useState<GymSet[]>([]);
const [offset, setOffset] = useState(0);
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 = 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);
setEnd(false);
};
useEffect(() => {
settingsRepo.findOne({ where: {} }).then(setSettings);
refresh({
take: LIMIT,
value: "",
skip: 0,
});
const description = DeviceEventEmitter.addListener(SETTINGS, () => {
settingsRepo.findOne({ where: {} }).then(setSettings);
});
return description.remove;
/* eslint-disable react-hooks/exhaustive-deps */
}, []);
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 }) => (
<SetItem
settings={settings}
item={item}
key={item.id}
ids={ids}
setIds={setIds}
/>
),
[settings, ids]
);
const next = async () => {
if (end || refreshing) return;
const newOffset = offset + LIMIT;
console.log(`${SetList.name}.next:`, { offset, newOffset, term });
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;
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);
};
const onAdd = useCallback(async () => {
const now = await getNow();
let set = sets[0];
if (!set) set = { ...defaultSet };
set.created = now;
delete set.id;
navigation.navigate("EditSet", { set });
}, [navigation, sets]);
const edit = useCallback(() => {
navigation.navigate("EditSets", { ids });
setIds([]);
}, [ids, navigation]);
const copy = useCallback(async () => {
const set = await setRepo.findOne({
where: { id: ids.pop() },
});
delete set.id;
delete set.created;
navigation.navigate("EditSet", { set });
setIds([]);
}, [ids, navigation]);
const clear = useCallback(() => {
setIds([]);
}, []);
const remove = async () => {
setIds([]);
await setRepo.delete(ids.length > 0 ? ids : {});
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"}>
<ListMenu
onClear={clear}
onCopy={copy}
onDelete={remove}
onEdit={edit}
ids={ids}
onSelect={select}
/>
</DrawerHeader>
<Page onAdd={onAdd} term={term} search={search}>
{content}
</Page>
</>
);
}