Compare commits

..

No commits in common. "master" and "use_alarm_manager" have entirely different histories.

22 changed files with 49 additions and 127 deletions

View File

@ -23,14 +23,13 @@ Massive tracks your reps and sets at the gym. No internet connectivity or high s
<img src="metadata/en-US/images/phoneScreenshots/home.png" width="318"/>
<img src="metadata/en-US/images/phoneScreenshots/edit.png" width="318"/>
<img src="metadata/en-US/images/phoneScreenshots/timer.png" width="318"/>
<img src="metadata/en-US/images/phoneScreenshots/plans.png" width="318"/>
<img src="metadata/en-US/images/phoneScreenshots/plan-edit.png" width="318"/>
<img src="metadata/en-US/images/phoneScreenshots/plan-start.png" width="318"/>
<img src="metadata/en-US/images/phoneScreenshots/best-view.png" width="318"/>
<img src="metadata/en-US/images/phoneScreenshots/settings.png" width="318"/>
<img src="metadata/en-US/images/phoneScreenshots/drawer.png" width="318"/>
<img src="metadata/en-US/images/phoneScreenshots/exercises.png" width="318"/>
<img src="metadata/en-US/images/phoneScreenshots/exercise-edit.png" width="318"/>
# Building from Source

View File

@ -511,7 +511,7 @@ export default function SettingsPage() {
style={{ alignSelf: "flex-start" }}
onPress={async () => {
const result = await DocumentPicker.pickDirectory();
await NativeModules.BackupModule.exportSets(result.uri);
await NativeModules.BackupModule.exportToCSV(result.uri);
toast("Exported sets as CSV.");
}}
>
@ -519,21 +519,6 @@ export default function SettingsPage() {
</Button>
),
},
{
name: "Export plans as CSV",
renderItem: (name: string) => (
<Button
style={{ alignSelf: "flex-start" }}
onPress={async () => {
const result = await DocumentPicker.pickDirectory();
await NativeModules.BackupModule.exportPlans(result.uri);
toast("Exported plans as CSV.");
}}
>
{name}
</Button>
),
},
{
name: "Import database",
renderItem: (name: string) => (

View File

@ -1,4 +1,3 @@
import { DateTimePickerAndroid } from "@react-native-community/datetimepicker";
import { RouteProp, useFocusEffect, useRoute } from "@react-navigation/native";
import { format } from "date-fns";
import { useCallback, useEffect, useMemo, useState } from "react";
@ -7,9 +6,8 @@ import { FileSystem } from "react-native-file-access";
import { IconButton, List } from "react-native-paper";
import Share from "react-native-share";
import { captureScreen } from "react-native-view-shot";
import AppInput from "./AppInput";
import AppLineChart from "./AppLineChart";
import { StackParams } from "./AppStack";
import AppLineChart from "./AppLineChart";
import Select from "./Select";
import StackHeader from "./StackHeader";
import { MARGIN, PADDING } from "./constants";
@ -17,9 +15,10 @@ import { setRepo, settingsRepo } from "./db";
import GymSet from "./gym-set";
import { Metrics } from "./metrics";
import { Periods } from "./periods";
import Settings from "./settings";
import Volume from "./volume";
import { convert } from "./conversions";
import AppInput from "./AppInput";
import { DateTimePickerAndroid } from "@react-native-community/datetimepicker";
import Settings from "./settings";
export default function ViewGraph() {
const { params } = useRoute<RouteProp<StackParams, "ViewGraph">>();
@ -27,16 +26,20 @@ export default function ViewGraph() {
const [volumes, setVolumes] = useState<Volume[]>();
const [metric, setMetric] = useState(Metrics.OneRepMax);
const [period, setPeriod] = useState(Periods.Monthly);
const [unit, setUnit] = useState("kg");
const [start, setStart] = useState<Date | null>(null);
const [end, setEnd] = useState<Date | null>(null);
const [unit, setUnit] = useState('kg');
const [start, setStart] = useState<Date | null>(null)
const [end, setEnd] = useState<Date | null>(null)
const [settings, setSettings] = useState<Settings>({} as Settings);
useFocusEffect(
useCallback(() => {
settingsRepo.findOne({ where: {} }).then(setSettings);
}, [])
);
useFocusEffect(useCallback(() => {
settingsRepo.findOne({ where: {} }).then(setSettings)
}, []))
const convertWeight = (weight: number, unitFrom: string, unitTo: string) => {
if (unitFrom === unitTo) return weight
if (unitFrom === 'lb' && unitTo === 'kg') return weight * 0.453592;
if (unitFrom === 'kg' && unitTo === 'lb') return weight * 2.20462;
};
useEffect(() => {
let difference = "-7 days";
@ -55,50 +58,34 @@ export default function ViewGraph() {
.select("STRFTIME('%Y-%m-%d', created)", "created")
.addSelect("unit")
.where("name = :name", { name: params.name })
.andWhere("NOT hidden");
.andWhere("NOT hidden")
if (start) builder.andWhere("DATE(created) >= :start", { start });
if (end) builder.andWhere("DATE(created) <= :end", { end });
if (start)
builder.andWhere("DATE(created) >= :start", { start });
if (end)
builder.andWhere("DATE(created) <= :end", { end });
if (difference)
builder.andWhere(
"DATE(created) >= DATE('now', 'weekday 0', :difference)",
{
difference,
}
);
builder.andWhere("DATE(created) >= DATE('now', 'weekday 0', :difference)", {
difference,
});
builder.groupBy("name").addGroupBy(`STRFTIME('${group}', created)`);
builder
.groupBy("name")
.addGroupBy(`STRFTIME('${group}', created)`);
switch (metric) {
case Metrics.Best:
builder
.addSelect("ROUND(MAX(weight), 2)", "weight")
.getRawMany()
.then((newWeights) =>
newWeights.map((set) => {
let weight = convert(set.weight, set.unit, unit);
if (isNaN(weight)) weight = 0;
return ({
...set,
weight: weight
});
})
)
.then(newWeights => newWeights.map(set => ({ ...set, weight: convertWeight(set.weight, set.unit, unit) })))
.then(setWeights);
break;
case Metrics.Volume:
builder
.addSelect("ROUND(SUM(weight * reps), 2)", "value")
.getRawMany()
.then((newWeights) =>
newWeights.map((set) => {
let weight = convert(set.value, set.unit, unit);
if (isNaN(weight)) weight = 0;
return ({
...set,
value: weight
});
})
)
.then(newWeights => newWeights.map(set => ({ ...set, value: convertWeight(set.value, set.unit, unit) })))
.then(setVolumes);
break;
default:
@ -109,20 +96,9 @@ export default function ViewGraph() {
"weight"
)
.getRawMany()
.then((newWeights) =>
newWeights.map((set) => {
let weight = convert(set.weight, set.unit, unit);
if (isNaN(weight)) weight = 0;
return ({
...set,
weight: weight,
});
})
)
.then(newWeights => newWeights.map(set => ({ ...set, weight: convertWeight(set.weight, set.unit, unit) })))
.then((newWeights) => {
console.log(`${ViewGraph.name}.oneRepMax:`, {
weights: newWeights,
});
console.log(`${ViewGraph.name}.oneRepMax:`, { weights: newWeights });
setWeights(newWeights);
});
}
@ -161,7 +137,7 @@ export default function ViewGraph() {
DateTimePickerAndroid.open({
value: start || new Date(),
onChange: (event, date) => {
if (event.type === "dismissed") return;
if (event.type === 'dismissed') return;
if (date === start) return;
setStart(date);
setPeriod(Periods.AllTime);
@ -175,7 +151,7 @@ export default function ViewGraph() {
DateTimePickerAndroid.open({
value: end || new Date(),
onChange: (event, date) => {
if (event.type === "dismissed") return;
if (event.type === 'dismissed') return;
if (date === end) return;
setEnd(date);
setPeriod(Periods.AllTime);
@ -233,7 +209,7 @@ export default function ViewGraph() {
value={period}
/>
<View style={{ flexDirection: "row", marginBottom: MARGIN }}>
<View style={{ flexDirection: 'row', marginBottom: MARGIN }}>
<AppInput
label="Start date"
value={start ? format(start, settings.date || "Pp") : null}
@ -253,9 +229,9 @@ export default function ViewGraph() {
value={unit}
onChange={setUnit}
items={[
{ label: "Pounds (lb)", value: "lb" },
{ label: "Kilograms (kg)", value: "kg" },
{ label: "Stone", value: "stone" },
{ label: 'Pounds (lb)', value: 'lb' },
{ label: 'Kilograms (kg)', value: 'kg' },
{ label: 'Stone', value: 'stone' },
]}
/>
<View style={{ paddingTop: PADDING }}>

View File

@ -87,8 +87,8 @@ android {
applicationId "com.massive"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 36249
versionName "2.34"
versionCode 36246
versionName "2.31"
}
signingConfigs {
release {

View File

@ -95,22 +95,10 @@ class BackupModule(context: ReactApplicationContext?) :
}
@ReactMethod
fun exportPlans(target: String, promise: Promise) {
fun exportToCSV(target: String, promise: Promise) {
try {
val db = DatabaseHelper(reactApplicationContext)
db.exportPlans(target, reactApplicationContext)
promise.resolve("Export successful!")
}
catch (e: Exception) {
promise.reject("ERROR", e)
}
}
@ReactMethod
fun exportSets(target: String, promise: Promise) {
try {
val db = DatabaseHelper(reactApplicationContext)
db.exportSets(target, reactApplicationContext)
db.exportToCSV(target, reactApplicationContext)
promise.resolve("Export successful!")
}
catch (e: Exception) {

View File

@ -18,8 +18,8 @@ class DatabaseHelper(context: Context) :
private const val DATABASE_VERSION = 1
}
fun exportSets(target: String, context: Context) {
Log.d("DatabaseHelper", "exportSets $target")
fun exportToCSV(target: String, context: Context) {
Log.d("DatabaseHelper", "exportToCSV $target")
val treeUri: Uri = Uri.parse(target)
val documentFile = context.let { DocumentFile.fromTreeUri(it, treeUri) }
val file = documentFile?.createFile("application/octet-stream", "sets.csv") ?: return
@ -43,31 +43,6 @@ class DatabaseHelper(context: Context) :
}
}
fun exportPlans(target: String, context: Context) {
Log.d("DatabaseHelper", "exportPlans $target")
val treeUri: Uri = Uri.parse(target)
val documentFile = context.let { DocumentFile.fromTreeUri(it, treeUri) }
val file = documentFile?.createFile("application/octet-stream", "plans.csv") ?: return
context.contentResolver.openOutputStream(file.uri).use { outputStream ->
val csvWrite = CSVWriter(outputStream?.writer())
val db = this.readableDatabase
val cursor = db.rawQuery("SELECT * FROM plans", null)
csvWrite.writeNext(cursor.columnNames)
while(cursor.moveToNext()) {
val arrStr = arrayOfNulls<String>(cursor.columnCount)
for(i in 0 until cursor.columnCount) {
arrStr[i] = cursor.getString(i)
}
csvWrite.writeNext(arrStr)
}
csvWrite.close()
cursor.close()
}
}
override fun onCreate(db: SQLiteDatabase) {
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 KiB

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "massive",
"version": "2.33",
"version": "2.28",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "massive",
"version": "2.33",
"version": "2.28",
"license": "GPL-3.0-only",
"dependencies": {
"@babel/plugin-proposal-class-properties": "^7.18.6",

View File

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

View File

@ -1 +0,0 @@
No user data is collected.