Compare commits

..

29 commits
alpha ... main

Author SHA1 Message Date
e3712d3f63 Update latest-releases.json 2025-06-14 02:08:41 +00:00
f4f75da847 Update latest-releases.json 2025-06-07 22:45:29 +00:00
2a5f141aa8 Update latest-releases.json 2025-06-06 22:03:27 +00:00
ceb28c1054 Update latest-releases.json 2025-05-27 13:58:07 -07:00
ed4599657c Update latest-releases.json 2025-05-25 16:30:07 -07:00
6b430b163f Update latest-releases.json 2025-05-25 16:15:34 -07:00
15b5efed49 Update latest-releases.json 2025-05-25 15:46:54 -07:00
30159bd217 Update latest-releases.json 2025-05-25 15:14:58 -07:00
c1c4a6e128 Update latest-releases.json 2025-05-25 15:01:27 -07:00
72d94376e0 Update latest-releases.json 2025-05-25 13:45:59 -07:00
3fe877e12c Update latest-releases.json 2025-05-25 13:34:12 -07:00
a30911b56e Update latest-releases.json 2025-05-25 12:56:37 -07:00
b22d907d68 Update latest-releases.json 2025-05-25 01:25:20 -07:00
d2a8e934c0 Update latest-releases.json 2025-05-24 19:21:01 -07:00
96055bb155 Update latest-releases.json 2025-05-20 09:23:19 -07:00
39e32d9f5d Update latest-releases.json 2025-05-19 01:55:47 -07:00
3a0858d3b5 Update latest-releases.json 2025-05-18 10:38:29 -07:00
727f43f943 Update latest-releases.json 2025-05-17 12:36:52 -07:00
0fcce433a5 Update latest-releases.json 2025-05-17 12:23:00 -07:00
98294c7ee3 Update latest-releases.json 2025-05-17 02:55:55 -07:00
04f861a9b7 Update latest-releases.json 2025-05-17 02:34:40 -07:00
87beb6944f Update latest-releases.json 2025-05-17 02:12:55 -07:00
26a768eced Update latest-releases.json
Signed-off-by: zontreck <zontreck@noreply>
2025-05-17 00:34:30 -07:00
24bb8f49ec Merge pull request 'Merge current work tree' (#1) from beta into main
Reviewed-on: #1
2025-05-16 13:30:33 -07:00
9b50945e3b ci: Add web build task for web.tgz 2025-05-16 11:46:33 -07:00
0b83eaaf3c Begin to add the web interface page 2025-05-16 10:09:41 -07:00
37e5688842 Add ability to tap to copy the session ID 2025-05-16 02:21:25 -07:00
7c5e3360a5 Make some changes to the PHP URL to make it channel specific 2025-05-16 01:38:40 -07:00
762be79df6 php: Add a disclaimer about ai content 2025-05-15 23:14:41 -07:00
11 changed files with 234 additions and 16 deletions

28
Jenkinsfile vendored
View file

@ -44,5 +44,33 @@ pipeline {
}
}
}
stage("Build Web App") {
agent {
label 'linux'
}
steps {
script {
sh '''
#!/bin/bash
flutter build web
cd build/web
tar -cvf ../../web.tgz .
cd ../..
'''
}
}
post {
always {
archiveArtifacts artifacts: "web.tgz"
cleanWs()
}
}
}
}
}

View file

@ -1,3 +1,4 @@
{
"alpha": "1.0.0-dev.10"
"alpha": "1.0.0-dev.10",
"beta": "1.0.0-beta.31"
}

View file

@ -1,5 +1,4 @@
import 'dart:convert';
import 'dart:ui';
import 'package:dio/dio.dart';
import 'package:geolocator/geolocator.dart';
@ -7,10 +6,13 @@ import 'package:geolocator/geolocator.dart';
class TTConsts {
static get UPDATE_URL =>
"https://git.zontreck.com/AriasCreations/TimeTracker/raw/branch/main/latest-releases.json";
static const VERSION = "1.0.0-dev.10";
static get SESSION_SERVER =>
"https://api.zontreck.com/timetrack/${UPDATE_CHANNEL}/timetrack.php";
static const VERSION = "1.0.0-beta.3";
static bool UPDATE_AVAILABLE = false;
static UpdateChannel UPDATE_CHANNEL = UpdateChannel.alpha;
static UpdateChannel UPDATE_CHANNEL = UpdateChannel.beta;
static final LocationSettings LOCATION_SETTINGS = LocationSettings(
accuracy: LocationAccuracy.bestForNavigation,
distanceFilter: 15,

View file

@ -3,6 +3,8 @@ import 'dart:convert';
import 'dart:math' as math;
import 'dart:ui';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:libac_dart/nbt/Stream.dart';
import 'package:timetrack/consts.dart';
@ -19,6 +21,8 @@ class SessionData {
static List<Position> positions = [];
static late StreamSubscription<Position> _listener;
static Callbacks Calls = Callbacks();
static String LastSessionID = "";
static String DisplayError = "";
/// This flag is usually set when data is loaded from a saved state. Or when accessed using the Web version of the app.
static bool IsReadOnly = false;
@ -185,10 +189,22 @@ class SessionData {
Trips = [];
positions = [];
// TODO: Upload to the server.
Dio dio = Dio();
Map<String, dynamic> payload = {"cmd": "create", "data": saveData};
var reply = await dio.post(
TTConsts.SESSION_SERVER,
data: json.encode(payload),
);
Map<String, dynamic> replyJs = json.decode(reply.data as String);
if (replyJs["status"] == "ok") {
print("Successful upload");
LastSessionID = replyJs['session'] as String;
Calls.dispatch();
}
}
static String SaveData() {
static Map<String, dynamic> SaveData() {
Map<String, dynamic> saveData = {};
List<Map<String, dynamic>> _trips = [];
@ -204,11 +220,28 @@ class SessionData {
saveData["trips"] = _trips;
saveData["positions"] = _pos;
return json.encode(saveData);
return saveData;
}
void LoadData(String js) {
static Future<void> DownloadData() async {
Dio dio = Dio();
Map<String, dynamic> payload = {"cmd": "get", "id": LastSessionID};
// Send the data, and get the response
var reply = await dio.post(
TTConsts.SESSION_SERVER,
data: json.encode(payload),
);
LoadData(reply.data as String);
}
static void LoadData(String js) {
Map<String, dynamic> _js = json.decode(js);
if (_js.containsKey("error")) {
LastSessionID = "";
return;
}
List<Map<String, dynamic>> _trips =
_js['trips'] as List<Map<String, dynamic>>;
List<Map<String, dynamic>> _pos =

View file

@ -1,9 +1,19 @@
import 'package:flutter/material.dart';
import 'package:timetrack/consts.dart';
import 'package:timetrack/data.dart';
import 'package:timetrack/pages/MainApp.dart';
Future<void> main() async {
await TTConsts.checkUpdate();
var sess = Uri.base.queryParameters["code"] ?? "";
SessionData.LastSessionID = sess;
if (SessionData.LastSessionID.isNotEmpty) {
await SessionData.DownloadData();
if (SessionData.LastSessionID.isEmpty) {
// Invalid session token
SessionData.DisplayError = "The URL and or session token is invalid";
}
}
runApp(MainApp());
}

View file

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:libacflutter/Constants.dart';
import 'package:libacflutter/Prompt.dart';
import 'package:timetrack/consts.dart';
@ -112,10 +113,22 @@ class _HomePageState extends State<HomePage> {
body: SingleChildScrollView(
child: Column(
children: [
Text(
"Hit engage when you are ready to go online and start tracking location data, and trips.",
style: TextStyle(fontSize: 18),
),
if (!SessionData.IsOnTheClock)
Text(
"Hit engage when you are ready to go online and start tracking location data, and trips.",
style: TextStyle(fontSize: 18),
),
if (SessionData.LastSessionID.isNotEmpty)
ListTile(
title: Text("Session ID"),
subtitle: Text("${SessionData.LastSessionID} - Tap to copy"),
onTap: () {
Clipboard.setData(
ClipboardData(text: SessionData.LastSessionID),
);
},
),
if (!SessionData.IsOnTheClock)
Center(
child: ElevatedButton(

View file

@ -1,7 +1,10 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:timetrack/pages/HomePage.dart';
import 'package:timetrack/pages/MapPage.dart';
import 'package:timetrack/pages/UpdateSettings.dart';
import 'package:timetrack/pages/WebMainPage.dart';
import 'package:timetrack/pages/WorkData.dart';
class MainApp extends StatefulWidget {
@ -24,7 +27,7 @@ class MainAppState extends State<MainApp> {
return MaterialApp(
title: "Time Tracker",
routes: {
"/": (ctx) => HomePage(),
"/": (ctx) => Platform.isAndroid ? HomePage() : WebMain(),
"/upd": (ctx) => UpdateSettingsPage(),
"/map": (ctx) => MapPage(),
"/work": (ctx) => WorkDataPage(),

128
lib/pages/WebMainPage.dart Normal file
View file

@ -0,0 +1,128 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:libacflutter/Constants.dart';
import 'package:timetrack/consts.dart';
import 'package:timetrack/data.dart';
class WebMain extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _WebMain();
}
}
class _WebMain extends State<WebMain> {
TextEditingController sessionIDController = TextEditingController();
@override
void didChangeDependencies() {
sessionIDController.text = SessionData.LastSessionID;
super.didChangeDependencies();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Time Tracker"),
backgroundColor: LibACFlutterConstants.TITLEBAR_COLOR,
),
drawer: Drawer(
elevation: 8,
child: SingleChildScrollView(
child: Column(
children: [
DrawerHeader(
child: Column(
children: [
Text("Time Tracker"),
Text("Created by Tara Piccari"),
Text("Copyright 2025 - Present"),
Text("Version: ${TTConsts.VERSION}"),
],
),
),
if (SessionData.IsReadOnly)
ListTile(
title: Text("Trip Map"),
leading: Icon(Icons.map),
subtitle: Text(
"View a map of the route\n(NOTE: This is not live, and reflects the current state as of the time the map is opened.)",
),
onTap: () async {
await Navigator.pushNamed(context, "/map");
},
),
if (SessionData.IsReadOnly)
ListTile(
title: Text("Work Data"),
subtitle: Text("View work data"),
leading: Icon(Icons.work_history),
onTap: () async {
// Open up the work data viewer and editor.
// Edit will be disabled for web or read only mode.
await Navigator.pushNamed(context, "/work");
setState(() {});
},
),
],
),
),
),
body: Padding(
padding: EdgeInsets.all(8),
child: SingleChildScrollView(
child: Column(
children: [
// Start doing magic!
if (SessionData.DisplayError.isNotEmpty)
Text(SessionData.DisplayError, style: TextStyle(fontSize: 18)),
// Check what widgets need to be displayed.
if (SessionData.IsReadOnly) GetReadOnlyWidgets(),
if (!SessionData.IsReadOnly) GetLoadWidgets(),
],
),
),
),
);
}
Widget GetReadOnlyWidgets() {
return Column(
children: [
Text(
"Use the top left menu to show the various pages for the data viewer.",
),
ElevatedButton(
onPressed: () async {
SessionData.IsReadOnly = false;
SessionData.Trips = [];
SessionData.positions = [];
SessionData.DisplayError = "";
},
child: Text("Close Session"),
),
],
);
}
Widget GetLoadWidgets() {
return Column(
children: [
// Present a text box for the session ID, and a button for loading.
ListTile(title: Text("Session ID")),
TextField(
controller: sessionIDController,
decoration: InputDecoration(border: OutlineInputBorder()),
),
ElevatedButton(
onPressed: () async {
await SessionData.DownloadData();
setState(() {});
},
child: Text("Load Session", style: TextStyle(fontSize: 18)),
),
],
);
}
}

View file

@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:libacflutter/Constants.dart';
import 'package:timetrack/consts.dart';
import 'package:timetrack/data.dart';
class WorkDataPage extends StatefulWidget {
@ -70,7 +69,7 @@ class _WorkData extends State<WorkDataPage> {
),
SizedBox(height: 20),
Text(
"Total Miles: ${SessionData.GetTotalMilesAsString()}",
"Total Estimated Miles: ${SessionData.GetTotalMilesAsString()}\n(Note: The miles displayed above may not be 100% accurate)",
style: TextStyle(fontSize: 24),
),
],

View file

@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.0-dev.10
version: 1.0.0-beta.3
environment:
sdk: ^3.7.2

View file

@ -10,6 +10,7 @@ $DB = get_DB("timetrack");
$jsx = json_decode(file_get_contents("php://input"), true);
// Get operation information
// DISCLAIMER: All php code below this point is AI Generated
switch($jsx['cmd']) {
case "create": {
// Get UUID from MySQL and insert into sessions table