initial commit
This commit is contained in:
commit
18e07dcb6d
194 changed files with 14049 additions and 0 deletions
273
lib/screens/home_screen.dart
Normal file
273
lib/screens/home_screen.dart
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/cupertino.dart' show CupertinoColors;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:timetracker/src/rust/api.dart';
|
||||
import 'package:timetracker/time_tracking_service.dart';
|
||||
|
||||
int dateTimeToUnixSeconds(DateTime dt) =>
|
||||
dt.toUtc().millisecondsSinceEpoch ~/ 1000;
|
||||
DateTime unixSecondsToDateTime(int ts) =>
|
||||
DateTime.fromMillisecondsSinceEpoch(ts * 1000, isUtc: true).toLocal();
|
||||
|
||||
class HomeScreen extends StatefulWidget {
|
||||
const HomeScreen({super.key});
|
||||
|
||||
@override
|
||||
State<HomeScreen> createState() => _HomeScreenState();
|
||||
}
|
||||
|
||||
class _HomeScreenState extends State<HomeScreen> {
|
||||
Timer? _timer;
|
||||
Duration _elapsedTime = Duration.zero;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final initialTracking = context.read<TimeTrackingService>().currentTracking;
|
||||
if (initialTracking != null) {
|
||||
_startTimer(unixSecondsToDateTime(initialTracking.startTime));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_stopTimer();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _startTimer(DateTime startTime) {
|
||||
_stopTimer();
|
||||
_calculateElapsedTime(startTime);
|
||||
_timer = Timer.periodic(const Duration(seconds: 1), (_) {
|
||||
final currentTracking =
|
||||
context.read<TimeTrackingService>().currentTracking;
|
||||
if (currentTracking != null) {
|
||||
_calculateElapsedTime(unixSecondsToDateTime(currentTracking.startTime));
|
||||
} else {
|
||||
_stopTimer();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _stopTimer() {
|
||||
if (_timer != null) {
|
||||
_timer!.cancel();
|
||||
_timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
void _calculateElapsedTime(DateTime startTime) {
|
||||
final now = DateTime.now();
|
||||
final duration = now.difference(startTime);
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_elapsedTime = duration.isNegative ? Duration.zero : duration;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
String _formatDuration(Duration duration) {
|
||||
String twoDigits(int n) => n.toString().padLeft(2, "0");
|
||||
final hours = twoDigits(duration.inHours);
|
||||
final minutes = twoDigits(duration.inMinutes.remainder(60));
|
||||
final seconds = twoDigits(duration.inSeconds.remainder(60));
|
||||
return "$hours:$minutes:$seconds";
|
||||
}
|
||||
|
||||
void _checkAndManageTimerState(TimeEntry? currentTracking) {
|
||||
if (currentTracking != null) {
|
||||
if (_timer == null || !_timer!.isActive) {
|
||||
log("Timer wird gestartet...");
|
||||
_startTimer(unixSecondsToDateTime(currentTracking.startTime));
|
||||
}
|
||||
} else {
|
||||
if (_timer != null && _timer!.isActive) {
|
||||
log("Timer wird gestoppt...");
|
||||
_stopTimer();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (mounted && _elapsedTime != Duration.zero) {
|
||||
setState(() {
|
||||
_elapsedTime = Duration.zero;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final timeService = context.watch<TimeTrackingService>();
|
||||
final currentTracking = timeService.currentTracking;
|
||||
|
||||
_checkAndManageTimerState(currentTracking);
|
||||
|
||||
return PlatformScaffold(
|
||||
appBar: PlatformAppBar(title: PlatformText('Tracking')),
|
||||
body: _buildBodyContent(context, currentTracking, timeService),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBodyContent(
|
||||
BuildContext context,
|
||||
TimeEntry? currentTracking,
|
||||
TimeTrackingService timeService,
|
||||
) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
if (currentTracking != null)
|
||||
_buildRunningTrackingInfo(context, currentTracking)
|
||||
else
|
||||
PlatformText('Kein aktives Tracking.'),
|
||||
const SizedBox(height: 20),
|
||||
PlatformElevatedButton(
|
||||
onPressed: () async {
|
||||
if (currentTracking != null) {
|
||||
final success = await timeService.flStopTracking();
|
||||
if (!success && context.mounted) {
|
||||
_showPlatformFeedbackDialog(
|
||||
context,
|
||||
'Fehler',
|
||||
'Fehler beim Stoppen des Trackings.',
|
||||
);
|
||||
}
|
||||
} else {
|
||||
_showPlatformTagSelection(context);
|
||||
}
|
||||
},
|
||||
child: PlatformText(
|
||||
currentTracking != null ? 'Tracking Stoppen' : 'Tracking Starten',
|
||||
),
|
||||
material:
|
||||
(_, __) => MaterialElevatedButtonData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor:
|
||||
currentTracking != null ? Colors.red : Colors.green,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 50,
|
||||
vertical: 20,
|
||||
),
|
||||
textStyle: const TextStyle(fontSize: 20),
|
||||
),
|
||||
),
|
||||
cupertino:
|
||||
(_, __) => CupertinoElevatedButtonData(
|
||||
color:
|
||||
currentTracking != null
|
||||
? CupertinoColors.destructiveRed
|
||||
: CupertinoColors.activeGreen,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 50,
|
||||
vertical: 20,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showPlatformTagSelection(BuildContext context) {
|
||||
final timeService = context.read<TimeTrackingService>();
|
||||
|
||||
final List<Widget> options = [
|
||||
ListTile(
|
||||
title: PlatformText('Ohne Tag'),
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
timeService.flStartTracking(0);
|
||||
},
|
||||
),
|
||||
const Divider(height: 1),
|
||||
...timeService.tags.map(
|
||||
(tag) => ListTile(
|
||||
title: PlatformText(tag.name),
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
timeService.flStartTracking(tag.id);
|
||||
},
|
||||
),
|
||||
),
|
||||
];
|
||||
|
||||
showPlatformModalSheet(
|
||||
context: context,
|
||||
material: MaterialModalSheetData(),
|
||||
cupertino: CupertinoModalSheetData(),
|
||||
builder:
|
||||
(_) => Material(
|
||||
child: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: options,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildRunningTrackingInfo(BuildContext context, TimeEntry entry) {
|
||||
final tagName = entry.tagName ?? 'Ohne Tag';
|
||||
return Column(
|
||||
children: [
|
||||
PlatformText(
|
||||
_formatDuration(_elapsedTime),
|
||||
style: platformThemeData(
|
||||
context,
|
||||
material:
|
||||
(data) => data.textTheme.headlineMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontFeatures: const [FontFeature.tabularFigures()],
|
||||
),
|
||||
cupertino:
|
||||
(data) => data.textTheme.navLargeTitleTextStyle.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
PlatformText('Aktives Tracking: $tagName'),
|
||||
PlatformText(
|
||||
'Gestartet: ${DateFormat('HH:mm:ss').format(unixSecondsToDateTime(entry.startTime))}',
|
||||
style: platformThemeData(
|
||||
context,
|
||||
material: (data) => data.textTheme.bodySmall,
|
||||
cupertino: (data) => data.textTheme.actionSmallTextStyle,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _showPlatformFeedbackDialog(
|
||||
BuildContext context,
|
||||
String title,
|
||||
String message,
|
||||
) {
|
||||
showPlatformDialog(
|
||||
context: context,
|
||||
builder:
|
||||
(_) => PlatformAlertDialog(
|
||||
title: PlatformText(title),
|
||||
content: PlatformText(message),
|
||||
actions: <Widget>[
|
||||
PlatformDialogAction(
|
||||
child: PlatformText('OK'),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue