Begin to add the web interface page
This commit is contained in:
parent
37e5688842
commit
0b83eaaf3c
7 changed files with 165 additions and 6 deletions
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"alpha": "1.0.0-dev.10",
|
||||
"beta": "1.0.0-beta.2"
|
||||
"beta": "1.0.0-beta.3"
|
||||
}
|
||||
|
|
|
@ -9,10 +9,10 @@ class TTConsts {
|
|||
static get SESSION_SERVER =>
|
||||
"https://api.zontreck.com/timetrack/${UPDATE_CHANNEL}/timetrack.php";
|
||||
|
||||
static const VERSION = "1.0.0-beta.2";
|
||||
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,
|
||||
|
|
|
@ -22,6 +22,7 @@ class SessionData {
|
|||
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;
|
||||
|
@ -222,8 +223,25 @@ class SessionData {
|
|||
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 =
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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
128
lib/pages/WebMainPage.dart
Normal 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)),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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-beta.2
|
||||
version: 1.0.0-beta.3
|
||||
|
||||
environment:
|
||||
sdk: ^3.7.2
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue