From 55e0a9f75ed91a33d59483432555145ed1bef0a8 Mon Sep 17 00:00:00 2001 From: Brandon Presley Date: Thu, 8 Dec 2022 13:05:09 +1300 Subject: [PATCH 1/8] Fix homepage error with default date format --- SetItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SetItem.tsx b/SetItem.tsx index 21030ba..2047b43 100644 --- a/SetItem.tsx +++ b/SetItem.tsx @@ -66,7 +66,7 @@ export default function SetItem({ alignSelf: 'center', color: dark ? '#909090ff' : '#717171ff', }}> - {format(new Date(item.created), settings.date)} + {format(new Date(item.created), settings.date || 'P')} )} Date: Thu, 8 Dec 2022 13:18:41 +1300 Subject: [PATCH 2/8] Add ability to export/import SQLite database --- SettingsPage.tsx | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/SettingsPage.tsx b/SettingsPage.tsx index 1ee0103..6978170 100644 --- a/SettingsPage.tsx +++ b/SettingsPage.tsx @@ -1,4 +1,8 @@ -import {useFocusEffect} from '@react-navigation/native' +import { + NavigationProp, + useFocusEffect, + useNavigation, +} from '@react-navigation/native' import {format} from 'date-fns' import {useCallback, useMemo, useState} from 'react' import { @@ -9,9 +13,13 @@ import { View, } from 'react-native' import DocumentPicker from 'react-native-document-picker' +import {Dirs, FileSystem} from 'react-native-file-access' import {Button, Subheading} from 'react-native-paper' +import ConfirmDialog from './ConfirmDialog' import {ITEM_PADDING, MARGIN} from './constants' +import {AppDataSource} from './data-source' import {settingsRepo} from './db' +import {DrawerParamList} from './drawer-param-list' import DrawerHeader from './DrawerHeader' import Input from './input' import {darkOptions, lightOptions, themeOptions} from './options' @@ -39,6 +47,8 @@ export default function SettingsPage() { const [showDate, setShowDate] = useState(false) const [noSound, setNoSound] = useState(false) const [formatOptions, setFormatOptions] = useState(defaultFormats) + const [importing, setImporting] = useState(false) + const {reset} = useNavigation>() const today = new Date() useFocusEffect( @@ -237,6 +247,21 @@ export default function SettingsPage() { [], ) + const confirmImport = useCallback(async () => { + setImporting(false) + await AppDataSource.destroy() + const result = await DocumentPicker.pickSingle() + await FileSystem.cp(result.uri, Dirs.DatabaseDir + '/massive.db') + await AppDataSource.initialize() + reset({index: 0, routes: [{name: 'Settings'}]}) + }, [reset]) + + const exportDatabase = useCallback(async () => { + const path = Dirs.DatabaseDir + '/massive.db' + await FileSystem.cpExternal(path, 'massive.db', 'downloads') + toast('Database exported. Check downloads.') + }, []) + return ( <> @@ -258,7 +283,24 @@ export default function SettingsPage() { )} + + + + + Importing a database overwrites your current data. This action cannot be + reversed! + ) } From 0b2d4d52e1719b6b73e2d930d915867bcbadf6e8 Mon Sep 17 00:00:00 2001 From: Brandon Presley Date: Thu, 8 Dec 2022 13:22:02 +1300 Subject: [PATCH 3/8] Add export/import database buttons to search --- SettingsPage.tsx | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/SettingsPage.tsx b/SettingsPage.tsx index 6978170..99e9e48 100644 --- a/SettingsPage.tsx +++ b/SettingsPage.tsx @@ -283,14 +283,18 @@ export default function SettingsPage() { )} - - + {'export database'.includes(term.toLowerCase()) && ( + + )} + {'import database'.includes(term.toLowerCase()) && ( + + )} Date: Thu, 8 Dec 2022 14:50:10 +1300 Subject: [PATCH 4/8] Remove csv import/export This is replaced with the backup/restore feature in Settings page. - Not sure anybody is using this besides me for testing purposes - Backing up the entire SQLite database is faster than CSV conversion - This prevents missing data and will work nicely with future plan changes Closes #128 --- DrawerMenu.tsx | 124 +----------------------------------------------- EditSet.tsx | 16 +------ EditWorkout.tsx | 17 +------ StartPlan.tsx | 8 +--- 4 files changed, 7 insertions(+), 158 deletions(-) diff --git a/DrawerMenu.tsx b/DrawerMenu.tsx index 613eb60..8a11afb 100644 --- a/DrawerMenu.tsx +++ b/DrawerMenu.tsx @@ -1,21 +1,11 @@ import {NavigationProp, useNavigation} from '@react-navigation/native' import {useCallback, useState} from 'react' -import DocumentPicker from 'react-native-document-picker' -import {FileSystem} from 'react-native-file-access' -import {Divider, IconButton, Menu} from 'react-native-paper' +import {IconButton, Menu} from 'react-native-paper' import ConfirmDialog from './ConfirmDialog' -import {AppDataSource} from './data-source' -import {planRepo} from './db' +import {planRepo, setRepo} from './db' import {DrawerParamList} from './drawer-param-list' -import GymSet from './gym-set' -import {Plan} from './plan' import {toast} from './toast' import useDark from './use-dark' -import {write} from './write' - -const setFields = 'id,name,reps,weight,created,unit,hidden,sets,minutes,seconds' -const planFields = 'id,days,workouts' -const setRepo = AppDataSource.manager.getRepository(GymSet) export default function DrawerMenu({name}: {name: keyof DrawerParamList}) { const [showMenu, setShowMenu] = useState(false) @@ -23,113 +13,6 @@ export default function DrawerMenu({name}: {name: keyof DrawerParamList}) { const {reset} = useNavigation>() const dark = useDark() - const exportSets = useCallback(async () => { - const sets = await setRepo.find({}) - const data = [setFields] - .concat( - sets.map(set => - setFields - .split(',') - .map(fieldString => { - const field = fieldString as keyof GymSet - if (field === 'unit') return set[field] || 'kg' - return set[field] - }) - .join(','), - ), - ) - .join('\n') - console.log(`${DrawerMenu.name}.exportSets`, {length: sets.length}) - await write('sets.csv', data) - }, []) - - const exportPlans = useCallback(async () => { - const plans = await planRepo.find({}) - const data = [planFields] - .concat(plans.map(set => `"${set.id}","${set.days}","${set.workouts}"`)) - .join('\n') - console.log(`${DrawerMenu.name}.exportPlans`, {length: plans.length}) - await write('plans.csv', data) - }, []) - - const download = useCallback(async () => { - setShowMenu(false) - if (name === 'Home') exportSets() - else if (name === 'Plans') exportPlans() - }, [name, exportSets, exportPlans]) - - const uploadSets = useCallback(async () => { - const result = await DocumentPicker.pickSingle() - const file = await FileSystem.readFile(result.uri) - console.log(`${DrawerMenu.name}.uploadSets:`, file.length) - const lines = file.split('\n') - if (!setFields.includes(lines[0])) return toast('Invalid csv.') - const values = lines - .slice(1) - .filter(line => line) - .map(line => { - let [ - , - setName, - reps, - weight, - created, - unit, - hidden, - sets, - minutes, - seconds, - ] = line.split(',') - const set: GymSet = { - name: setName, - reps: +reps, - weight: +weight, - created, - unit: unit ?? 'kg', - hidden: !!Number(hidden), - sets: +sets, - minutes: +minutes, - seconds: +seconds, - image: '', - } - return set - }) - await setRepo.insert(values) - toast('Data imported.') - reset({index: 0, routes: [{name}]}) - }, [reset, name]) - - const uploadPlans = useCallback(async () => { - const result = await DocumentPicker.pickSingle() - const file = await FileSystem.readFile(result.uri) - console.log(`${DrawerMenu.name}.uploadPlans:`, file.length) - const lines = file.split('\n') - if (lines[0] !== planFields) return toast('Invalid csv.') - const values = file - .split('\n') - .slice(1) - .filter(line => line) - .map(set => { - const [, days, workouts] = set - .split('","') - .map(cell => cell.replace(/"/g, '')) - const plan: Plan = { - days, - workouts, - } - return plan - }) - await planRepo.insert(values) - toast('Data imported.') - }, []) - - const upload = useCallback(async () => { - setShowMenu(false) - if (name === 'Home') await uploadSets() - else if (name === 'Plans') await uploadPlans() - reset({index: 0, routes: [{name}]}) - }, [name, uploadPlans, uploadSets, reset]) - const remove = useCallback(async () => { setShowMenu(false) setShowRemove(false) @@ -151,9 +34,6 @@ export default function DrawerMenu({name}: {name: keyof DrawerParamList}) { icon="more-vert" /> }> - - - setShowRemove(true)} diff --git a/EditSet.tsx b/EditSet.tsx index 11ff70d..004b1b6 100644 --- a/EditSet.tsx +++ b/EditSet.tsx @@ -99,18 +99,6 @@ export default function EditSet() { navigation.goBack() } - const handleName = useCallback((value: string) => { - setName(value.replace(/,|'/g, '')) - if (value.match(/,|'/)) - toast('Commas and single quotes would break CSV exports') - }, []) - - const handleUnit = useCallback((value: string) => { - setUnit(value.replace(/,|'/g, '')) - if (value.match(/,|'/)) - toast('Commas and single quotes would break CSV exports') - }, []) - const changeImage = useCallback(async () => { const {fileCopyUri} = await DocumentPicker.pickSingle({ type: DocumentPicker.types.images, @@ -133,7 +121,7 @@ export default function EditSet() { repsRef.current?.focus()} @@ -165,7 +153,7 @@ export default function EditSet() { autoCapitalize="none" label="Unit" value={unit} - onChangeText={handleUnit} + onChangeText={setUnit} innerRef={unitRef} /> )} diff --git a/EditWorkout.tsx b/EditWorkout.tsx index d08601d..2befcc8 100644 --- a/EditWorkout.tsx +++ b/EditWorkout.tsx @@ -15,7 +15,6 @@ import {defaultSet} from './gym-set' import MassiveInput from './MassiveInput' import Settings from './settings' import StackHeader from './StackHeader' -import {toast} from './toast' import {WorkoutsPageParams} from './WorkoutsPage' export default function EditWorkout() { @@ -101,18 +100,6 @@ export default function EditWorkout() { setShowRemove(false) }, []) - const handleName = (value: string) => { - setName(value.replace(/,|'/g, '')) - if (value.match(/,|'/)) - toast('Commas and single quotes would break CSV exports') - } - - const handleSteps = (value: string) => { - setSteps(value.replace(/,|'/g, '')) - if (value.match(/,|'/)) - toast('Commas and single quotes would break CSV exports') - } - const submitName = () => { if (settings.steps) stepsRef.current?.focus() else setsRef.current?.focus() @@ -127,7 +114,7 @@ export default function EditWorkout() { autoFocus label="Name" value={name} - onChangeText={handleName} + onChangeText={setName} onSubmitEditing={submitName} /> {settings?.steps && ( @@ -135,7 +122,7 @@ export default function EditWorkout() { innerRef={stepsRef} selectTextOnFocus={false} value={steps} - onChangeText={handleSteps} + onChangeText={setSteps} label="Steps" multiline onSubmitEditing={() => setsRef.current?.focus()} diff --git a/StartPlan.tsx b/StartPlan.tsx index d592c74..fc83330 100644 --- a/StartPlan.tsx +++ b/StartPlan.tsx @@ -111,12 +111,6 @@ export default function StartPlan() { NativeModules.AlarmModule.timer(...args) } - const handleUnit = useCallback((value: string) => { - setUnit(value.replace(/,|'/g, '')) - if (value.match(/,|'/)) - toast('Commas and single quotes would break CSV exports') - }, []) - return ( <> @@ -146,7 +140,7 @@ export default function StartPlan() { autoCapitalize="none" label="Unit" value={unit} - onChangeText={handleUnit} + onChangeText={setUnit} innerRef={unitRef} /> )} From 216fc43a81d9fd99cba24260c4c6dfc39d5dbdaf Mon Sep 17 00:00:00 2001 From: Brandon Presley Date: Thu, 8 Dec 2022 14:53:28 +1300 Subject: [PATCH 5/8] Set versionCode=36114 --- android/app/build.gradle | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 32a5f3e..f4902ca 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -41,8 +41,8 @@ android { missingDimensionStrategy "RNNotifications.reactNativeVersion", "reactNative60" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 36113 - versionName "1.87" + versionCode 36114 + versionName "1.88" buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() if (isNewArchitectureEnabled()) { diff --git a/package.json b/package.json index 3f0a568..b7e2d16 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "massive", - "version": "1.87", + "version": "1.88", "private": true, "license": "GPL-3.0-only", "scripts": { From f6eb7959e189625e65606929b6de16249c0840ef Mon Sep 17 00:00:00 2001 From: Brandon Presley Date: Thu, 8 Dec 2022 15:40:26 +1300 Subject: [PATCH 6/8] Add missing set statement for dark color --- App.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/App.tsx b/App.tsx index a6dc31b..1d6f3ba 100644 --- a/App.tsx +++ b/App.tsx @@ -58,6 +58,7 @@ const App = () => { console.log(`${App.name}.useEffect:`, {gotSettings: settings}) setTheme(settings.theme) if (settings.lightColor) setLightColor(settings.lightColor) + if (settings.darkColor) setDarkColor(settings.darkColor) setInitialized(true) } init() From 6df9bba2ae1926b818492fb890202ccd2ca4a591 Mon Sep 17 00:00:00 2001 From: Brandon Presley Date: Thu, 8 Dec 2022 15:42:02 +1300 Subject: [PATCH 7/8] Set versionCode=36115 --- android/app/build.gradle | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index f4902ca..6433b94 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -41,8 +41,8 @@ android { missingDimensionStrategy "RNNotifications.reactNativeVersion", "reactNative60" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 36114 - versionName "1.88" + versionCode 36115 + versionName "1.89" buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() if (isNewArchitectureEnabled()) { diff --git a/package.json b/package.json index b7e2d16..56e1239 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "massive", - "version": "1.88", + "version": "1.89", "private": true, "license": "GPL-3.0-only", "scripts": { From 9c184c5924b80968db26f9ce0d857a8a62be21e9 Mon Sep 17 00:00:00 2001 From: Brandon Presley Date: Thu, 8 Dec 2022 15:56:09 +1300 Subject: [PATCH 8/8] Add log when alarm finishes --- android/app/src/main/java/com/massive/AlarmModule.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/android/app/src/main/java/com/massive/AlarmModule.kt b/android/app/src/main/java/com/massive/AlarmModule.kt index ee86264..ad5b1b0 100644 --- a/android/app/src/main/java/com/massive/AlarmModule.kt +++ b/android/app/src/main/java/com/massive/AlarmModule.kt @@ -156,6 +156,7 @@ class AlarmModule constructor(context: ReactApplicationContext?) : val manager = getManager() manager.notify(NOTIFICATION_ID_DONE, builder.build()) manager.cancel(NOTIFICATION_ID_PENDING) + Log.d("AlarmModule", "Finished: vibrate=$vibrate,sound=$sound,noSound=$noSound") val alarmIntent = Intent(context, AlarmService::class.java).apply { putExtra("vibrate", vibrate) putExtra("sound", sound)