187 lines
5.1 KiB
Dart
187 lines
5.1 KiB
Dart
import 'dart:developer';
|
|
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_io.dart';
|
|
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
|
import 'package:timetracker/src/rust/api.dart';
|
|
import 'package:timetracker/src/rust/frb_generated.dart';
|
|
|
|
int dateTimeToUnixSeconds(DateTime dt) =>
|
|
dt.toUtc().millisecondsSinceEpoch ~/ 1000;
|
|
DateTime unixSecondsToDateTime(int ts) =>
|
|
DateTime.fromMillisecondsSinceEpoch(ts * 1000, isUtc: true).toLocal();
|
|
|
|
class TimeTrackingService extends ChangeNotifier {
|
|
// final RustLibApi api;
|
|
bool _isInitialized = false;
|
|
bool _isLoading = true;
|
|
TimeEntry? _currentTracking;
|
|
List<Tag> _tags = [];
|
|
|
|
bool get isInitialized => _isInitialized;
|
|
bool get isLoading => _isLoading;
|
|
TimeEntry? get currentTracking => _currentTracking;
|
|
List<Tag> get tags => _tags;
|
|
|
|
// TimeTrackingService({required this.api}) {
|
|
TimeTrackingService() {
|
|
_loadInitialData();
|
|
}
|
|
|
|
Future<void> _loadInitialData() async {
|
|
_isLoading = true;
|
|
_isInitialized = false;
|
|
notifyListeners();
|
|
try {
|
|
_currentTracking = await getLastUnfinishedTracking();
|
|
_tags = await getTags();
|
|
|
|
_isInitialized = true;
|
|
} on FrbException catch (e) {
|
|
_isInitialized = false;
|
|
log("error loading initial data: $e");
|
|
} catch (e) {
|
|
log("unexpected error loading initial data: $e");
|
|
_isInitialized = false;
|
|
} finally {
|
|
_isLoading = false;
|
|
notifyListeners();
|
|
}
|
|
}
|
|
|
|
Future<void> refreshData() async {
|
|
await _loadInitialData();
|
|
}
|
|
|
|
Future<bool> flStartTracking(int? tagId) async {
|
|
if (_currentTracking != null) {
|
|
log("Cannot start tracking: Another tracking is already active.");
|
|
return false;
|
|
}
|
|
final startTime = DateTime.now();
|
|
|
|
try {
|
|
final newEntryId = await startTracking(
|
|
tagId: tagId,
|
|
startTimeUnixTs: dateTimeToUnixSeconds(startTime),
|
|
);
|
|
log("Tracking started with new ID: $newEntryId");
|
|
await _loadInitialData();
|
|
return true;
|
|
} on FrbException catch (e) {
|
|
log("Error starting tracking: $e");
|
|
return false;
|
|
} catch (e) {
|
|
log("Unexpected error starting tracking: $e");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Future<bool> flStopTracking() async {
|
|
if (_currentTracking == null) {
|
|
log("Cannot stop tracking: No tracking is active.");
|
|
return false;
|
|
}
|
|
final endTime = DateTime.now();
|
|
final entryIdToStop = _currentTracking!.id;
|
|
|
|
try {
|
|
await stopTracking(
|
|
entryId: entryIdToStop,
|
|
endTimeUnixTs: dateTimeToUnixSeconds(endTime),
|
|
);
|
|
log("Tracking stopped for ID: $entryIdToStop");
|
|
await _loadInitialData();
|
|
return true;
|
|
} on FrbException catch (e) {
|
|
log("Error stopping tracking: $e");
|
|
return false;
|
|
} catch (e) {
|
|
log("Unexpected error stopping tracking: $e");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Future<bool> flCreateTag(String name) async {
|
|
if (name.trim().isEmpty) {
|
|
log("Cannot create tag: Name is empty.");
|
|
return false;
|
|
}
|
|
final trimmedName = name.trim();
|
|
|
|
try {
|
|
final tagId = await createTag(name: trimmedName);
|
|
log("Tag created with ID: $tagId");
|
|
_tags = await getTags();
|
|
notifyListeners();
|
|
return true;
|
|
} on FrbException catch (e) {
|
|
log("Error creating tag: $e");
|
|
return false;
|
|
} catch (e) {
|
|
log("Unexpected error creating tag: $e");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Future<ReportData?> flGetReport(
|
|
int? tagId,
|
|
DateTime start,
|
|
DateTime end,
|
|
) async {
|
|
try {
|
|
final report = await generateReport(
|
|
tagIdFilter: tagId,
|
|
startDateUnixTs: dateTimeToUnixSeconds(start),
|
|
endDateUnixTs: dateTimeToUnixSeconds(end),
|
|
);
|
|
return report;
|
|
} on FrbException catch (e) {
|
|
log("Error generating report: $e");
|
|
return null;
|
|
} catch (e) {
|
|
log("Unexpected error generating report: $e");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
Future<bool> flUpdateTimeEntry({
|
|
required int entryId,
|
|
required int? tagId,
|
|
required DateTime startTime,
|
|
required DateTime? endTime,
|
|
}) async {
|
|
try {
|
|
await updateTimeEntry(
|
|
entryId: entryId,
|
|
newTagId: tagId,
|
|
newStartTimeUnixTs: dateTimeToUnixSeconds(startTime),
|
|
newEndTimeUnixTs: dateTimeToUnixSeconds(endTime!),
|
|
);
|
|
log("Time entry $entryId updated successfully via service.");
|
|
return true;
|
|
} on FrbException catch (e) {
|
|
log("Error updating time entry $entryId: $e");
|
|
return false;
|
|
} catch (e) {
|
|
log("Unexpected error updating time entry $entryId: $e");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Future<bool> flDeleteTimeEntry(int entryId) async {
|
|
log('Service: Attempting to delete entry $entryId');
|
|
try {
|
|
await deleteTimeEntry(id: entryId);
|
|
log('Service: Successfully deleted entry $entryId');
|
|
return true;
|
|
} on FrbException catch (e, s) {
|
|
log('Service: Error deleting entry $entryId: $e\n$s');
|
|
return false;
|
|
} catch (e, s) {
|
|
log('Service: Unexpected error deleting entry $entryId: $e\n$s');
|
|
return false;
|
|
}
|
|
}
|
|
}
|