Remove loading indicator where load times are fast

If the load time is too fast (on a mid-tier device),
then for most people they will see a flicker of a
loading spinner (which looks like a bug). These indicators
would only marginally improve the experience of people with
the slowest devices, but for most people this will just look
like a bug.

I left the indicators in the InsightsPage since those queries
actually do take >=300ms on a mid-tier device.
This commit is contained in:
Brandon Presley 2023-11-15 15:53:32 +13:00
parent b44cbae131
commit 1b164aaaf1
5 changed files with 38 additions and 42 deletions

View File

@ -6,12 +6,7 @@ import {
} from "@react-navigation/native"; } from "@react-navigation/native";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { Pressable, StyleSheet, View } from "react-native"; import { Pressable, StyleSheet, View } from "react-native";
import { import { IconButton, Switch as PaperSwitch, Text } from "react-native-paper";
ActivityIndicator,
IconButton,
Switch as PaperSwitch,
Text,
} from "react-native-paper";
import ReorderableList, { import ReorderableList, {
ReorderableListRenderItemInfo, ReorderableListRenderItemInfo,
} from "react-native-reorderable-list"; } from "react-native-reorderable-list";
@ -171,9 +166,7 @@ export default function EditPlan() {
{DAYS.map((day) => renderDay(day))} {DAYS.map((day) => renderDay(day))}
<Text style={[styles.title, { marginTop: MARGIN }]}>Exercises</Text> <Text style={[styles.title, { marginTop: MARGIN }]}>Exercises</Text>
{names === undefined ? ( {names !== undefined && (
<ActivityIndicator />
) : (
<ReorderableList <ReorderableList
data={names} data={names}
ListEmptyComponent={<Text>No exercises yet</Text>} ListEmptyComponent={<Text>No exercises yet</Text>}

View File

@ -25,6 +25,8 @@ interface HourCount {
export default function InsightsPage() { export default function InsightsPage() {
const [weekCounts, setWeekCounts] = useState<WeekCount[]>(); const [weekCounts, setWeekCounts] = useState<WeekCount[]>();
const [hourCounts, setHourCounts] = useState<HourCount[]>(); const [hourCounts, setHourCounts] = useState<HourCount[]>();
const [loadingWeeks, setLoadingWeeks] = useState(true);
const [loadingHours, setLoadingHours] = useState(true);
const [period, setPeriod] = useState(Periods.Monthly); const [period, setPeriod] = useState(Periods.Monthly);
const [showWeek, setShowWeek] = useState(false); const [showWeek, setShowWeek] = useState(false);
const [showHour, setShowHour] = useState(false); const [showHour, setShowHour] = useState(false);
@ -53,14 +55,22 @@ export default function InsightsPage() {
ORDER BY hour ORDER BY hour
`; `;
setLoadingWeeks(true);
setLoadingHours(true);
setTimeout( setTimeout(
() => () =>
AppDataSource.manager AppDataSource.manager
.query(selectWeeks) .query(selectWeeks)
.then(setWeekCounts) .then(setWeekCounts)
.then(() => setLoadingWeeks(false))
.then(() => .then(() =>
AppDataSource.manager.query(selectHours).then(setHourCounts) AppDataSource.manager.query(selectHours).then(setHourCounts)
), )
.then(() => setLoadingHours(false))
.finally(() => {
setLoadingWeeks(false);
setLoadingHours(false);
}),
400 400
); );
}, [period]) }, [period])
@ -126,9 +136,9 @@ export default function InsightsPage() {
/> />
</View> </View>
{weekCounts === undefined && <ActivityIndicator />} {loadingWeeks ? (
<ActivityIndicator />
{weekCounts?.length > 0 && ( ) : (
<AppPieChart <AppPieChart
options={weekCounts.map((weekCount) => ({ options={weekCounts.map((weekCount) => ({
label: DAYS[weekCount.week], label: DAYS[weekCount.week],
@ -136,6 +146,7 @@ export default function InsightsPage() {
}))} }))}
/> />
)} )}
{weekCounts?.length === 0 && ( {weekCounts?.length === 0 && (
<Text style={{ marginBottom: MARGIN }}> <Text style={{ marginBottom: MARGIN }}>
No entries yet! Start recording sets to see your most active days of No entries yet! Start recording sets to see your most active days of
@ -166,14 +177,15 @@ export default function InsightsPage() {
/> />
</View> </View>
{hourCounts === undefined && <ActivityIndicator />} {loadingHours ? (
<ActivityIndicator />
{hourCounts?.length > 0 && ( ) : (
<Chart <Chart
data={hourCounts.map((hc) => hc.count)} data={hourCounts.map((hc) => hc.count)}
labels={hourCounts.map((hc) => hourLabel(hc.hour))} labels={hourCounts.map((hc) => hourLabel(hc.hour))}
/> />
)} )}
{hourCounts?.length === 0 && ( {hourCounts?.length === 0 && (
<Text> <Text>
No entries yet! Start recording sets to see your most active hours No entries yet! Start recording sets to see your most active hours

View File

@ -5,16 +5,16 @@ import {
} from "@react-navigation/native"; } from "@react-navigation/native";
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
import { FlatList } from "react-native"; import { FlatList } from "react-native";
import { ActivityIndicator, List } from "react-native-paper"; import { List } from "react-native-paper";
import { Like } from "typeorm"; import { Like } from "typeorm";
import { StackParams } from "./AppStack"; import { StackParams } from "./AppStack";
import { LIMIT } from "./constants";
import { getNow, setRepo, settingsRepo } from "./db";
import DrawerHeader from "./DrawerHeader"; import DrawerHeader from "./DrawerHeader";
import GymSet, { defaultSet } from "./gym-set";
import ListMenu from "./ListMenu"; import ListMenu from "./ListMenu";
import Page from "./Page"; import Page from "./Page";
import SetItem from "./SetItem"; import SetItem from "./SetItem";
import { LIMIT } from "./constants";
import { getNow, setRepo, settingsRepo } from "./db";
import GymSet, { defaultSet } from "./gym-set";
import Settings from "./settings"; import Settings from "./settings";
export default function SetList() { export default function SetList() {
@ -134,7 +134,7 @@ export default function SetList() {
}, [sets, ids]); }, [sets, ids]);
const getContent = () => { const getContent = () => {
if (!settings || sets === undefined) return <ActivityIndicator />; if (!settings || sets === undefined) return null;
if (sets.length === 0) if (sets.length === 0)
return ( return (
<List.Item <List.Item

View File

@ -7,16 +7,14 @@ import {
} from "@react-navigation/native"; } from "@react-navigation/native";
import { useCallback, useMemo, useRef, useState } from "react"; import { useCallback, useMemo, useRef, useState } from "react";
import { FlatList, NativeModules, TextInput, View } from "react-native"; import { FlatList, NativeModules, TextInput, View } from "react-native";
import { import { IconButton, ProgressBar } from "react-native-paper";
ActivityIndicator, import { PERMISSIONS, RESULTS, check, request } from "react-native-permissions";
Button,
IconButton,
ProgressBar,
useTheme,
} from "react-native-paper";
import { check, PERMISSIONS, request, RESULTS } from "react-native-permissions";
import AppInput from "./AppInput"; import AppInput from "./AppInput";
import { StackParams } from "./AppStack"; import { StackParams } from "./AppStack";
import PrimaryButton from "./PrimaryButton";
import Select from "./Select";
import StackHeader from "./StackHeader";
import StartPlanItem from "./StartPlanItem";
import { getBestSet } from "./best.service"; import { getBestSet } from "./best.service";
import { PADDING } from "./constants"; import { PADDING } from "./constants";
import { convert } from "./conversions"; import { convert } from "./conversions";
@ -25,12 +23,8 @@ import { AppDataSource } from "./data-source";
import { getNow, setRepo, settingsRepo } from "./db"; import { getNow, setRepo, settingsRepo } from "./db";
import { fixNumeric } from "./fix-numeric"; import { fixNumeric } from "./fix-numeric";
import GymSet from "./gym-set"; import GymSet from "./gym-set";
import Select from "./Select";
import Settings from "./settings"; import Settings from "./settings";
import StackHeader from "./StackHeader";
import StartPlanItem from "./StartPlanItem";
import { toast } from "./toast"; import { toast } from "./toast";
import PrimaryButton from "./PrimaryButton";
export default function StartPlan() { export default function StartPlan() {
const { params } = useRoute<RouteProp<StackParams, "StartPlan">>(); const { params } = useRoute<RouteProp<StackParams, "StartPlan">>();
@ -40,7 +34,6 @@ export default function StartPlan() {
const [selected, setSelected] = useState(0); const [selected, setSelected] = useState(0);
const [settings, setSettings] = useState<Settings>(); const [settings, setSettings] = useState<Settings>();
const [counts, setCounts] = useState<CountMany[]>(); const [counts, setCounts] = useState<CountMany[]>();
const { colors } = useTheme();
const weightRef = useRef<TextInput>(null); const weightRef = useRef<TextInput>(null);
const repsRef = useRef<TextInput>(null); const repsRef = useRef<TextInput>(null);
const exercises = useMemo(() => params.plan.exercises.split(","), [params]); const exercises = useMemo(() => params.plan.exercises.split(","), [params]);
@ -222,9 +215,7 @@ export default function StartPlan() {
label="Unit" label="Unit"
/> />
)} )}
{counts === undefined ? ( {counts !== undefined && (
<ActivityIndicator />
) : (
<FlatList <FlatList
data={counts} data={counts}
keyExtractor={(count) => count.name} keyExtractor={(count) => count.name}

View File

@ -3,18 +3,18 @@ import { format } from "date-fns";
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { View } from "react-native"; import { View } from "react-native";
import { FileSystem } from "react-native-file-access"; import { FileSystem } from "react-native-file-access";
import { ActivityIndicator, IconButton, List } from "react-native-paper"; import { IconButton, List } from "react-native-paper";
import Share from "react-native-share"; import Share from "react-native-share";
import { captureScreen } from "react-native-view-shot"; import { captureScreen } from "react-native-view-shot";
import { StackParams } from "./AppStack"; import { StackParams } from "./AppStack";
import Chart from "./Chart"; import Chart from "./Chart";
import Select from "./Select";
import StackHeader from "./StackHeader";
import { PADDING } from "./constants"; import { PADDING } from "./constants";
import { setRepo } from "./db"; import { setRepo } from "./db";
import GymSet from "./gym-set"; import GymSet from "./gym-set";
import { Metrics } from "./metrics"; import { Metrics } from "./metrics";
import { Periods } from "./periods"; import { Periods } from "./periods";
import Select from "./Select";
import StackHeader from "./StackHeader";
import Volume from "./volume"; import Volume from "./volume";
export default function ViewGraph() { export default function ViewGraph() {
@ -76,7 +76,7 @@ export default function ViewGraph() {
}, [params.name, metric, period]); }, [params.name, metric, period]);
const weightChart = useMemo(() => { const weightChart = useMemo(() => {
if (weights === undefined) return <ActivityIndicator />; if (weights === undefined) return null;
let periodFormat = "do"; let periodFormat = "do";
if (period === Periods.Weekly) periodFormat = "iii"; if (period === Periods.Weekly) periodFormat = "iii";
@ -95,7 +95,7 @@ export default function ViewGraph() {
}, [weights, period]); }, [weights, period]);
const volumeChart = useMemo(() => { const volumeChart = useMemo(() => {
if (volumes === undefined) return <ActivityIndicator />; if (volumes === undefined) return null;
let periodFormat = "do"; let periodFormat = "do";
if (period === Periods.Weekly) periodFormat = "iii"; if (period === Periods.Weekly) periodFormat = "iii";