2022-04-25 21:47:40 +00:00
|
|
|
import 'dart:developer' as dev;
|
2022-04-19 22:12:50 +00:00
|
|
|
|
2024-04-05 12:35:23 +00:00
|
|
|
import 'package:flutter/foundation.dart';
|
2018-09-16 19:47:33 +00:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
2022-07-15 14:25:16 +00:00
|
|
|
import 'package:vikunja_app/api/bucket_implementation.dart';
|
2018-09-22 20:56:16 +00:00
|
|
|
import 'package:vikunja_app/api/client.dart';
|
2021-06-04 09:34:25 +00:00
|
|
|
import 'package:vikunja_app/api/label_task.dart';
|
|
|
|
import 'package:vikunja_app/api/label_task_bulk.dart';
|
|
|
|
import 'package:vikunja_app/api/labels.dart';
|
2022-05-07 15:18:03 +00:00
|
|
|
import 'package:vikunja_app/api/server_implementation.dart';
|
2018-09-22 20:56:16 +00:00
|
|
|
import 'package:vikunja_app/api/task_implementation.dart';
|
|
|
|
import 'package:vikunja_app/api/user_implementation.dart';
|
2023-01-07 01:17:00 +00:00
|
|
|
import 'package:vikunja_app/api/version_check.dart';
|
2022-04-19 22:12:50 +00:00
|
|
|
import 'package:vikunja_app/managers/notifications.dart';
|
2018-09-22 20:56:16 +00:00
|
|
|
import 'package:vikunja_app/managers/user.dart';
|
|
|
|
import 'package:vikunja_app/models/user.dart';
|
|
|
|
import 'package:vikunja_app/service/services.dart';
|
2022-04-19 22:12:50 +00:00
|
|
|
import 'package:timezone/data/latest_all.dart' as tz;
|
2023-01-07 15:55:40 +00:00
|
|
|
import 'package:workmanager/workmanager.dart';
|
2022-04-22 19:39:01 +00:00
|
|
|
|
2023-07-22 20:54:50 +00:00
|
|
|
import 'api/project.dart';
|
2024-04-29 14:29:44 +00:00
|
|
|
import 'api/view.dart';
|
2023-05-16 23:22:59 +00:00
|
|
|
import 'main.dart';
|
|
|
|
|
2018-09-16 19:47:33 +00:00
|
|
|
class VikunjaGlobal extends StatefulWidget {
|
|
|
|
final Widget child;
|
|
|
|
final Widget login;
|
|
|
|
|
2022-08-27 21:04:43 +00:00
|
|
|
VikunjaGlobal({required this.child, required this.login});
|
2018-09-16 19:47:33 +00:00
|
|
|
|
|
|
|
@override
|
|
|
|
VikunjaGlobalState createState() => VikunjaGlobalState();
|
|
|
|
|
|
|
|
static VikunjaGlobalState of(BuildContext context) {
|
2021-12-21 11:22:17 +00:00
|
|
|
var widget =
|
2023-05-16 23:22:59 +00:00
|
|
|
context.dependOnInheritedWidgetOfExactType<VikunjaGlobalInherited>();
|
2022-08-27 21:04:43 +00:00
|
|
|
return widget!.data;
|
2018-09-16 19:47:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class VikunjaGlobalState extends State<VikunjaGlobal> {
|
|
|
|
final FlutterSecureStorage _storage = new FlutterSecureStorage();
|
|
|
|
|
2022-08-27 21:04:43 +00:00
|
|
|
User? _currentUser;
|
2018-09-16 19:47:33 +00:00
|
|
|
bool _loading = true;
|
2022-04-25 21:47:40 +00:00
|
|
|
bool expired = false;
|
2022-08-27 21:04:43 +00:00
|
|
|
late Client _client;
|
|
|
|
UserService? _newUserService;
|
2023-01-07 21:50:32 +00:00
|
|
|
NotificationClass _notificationClass = NotificationClass();
|
2022-04-25 21:47:40 +00:00
|
|
|
|
2022-08-27 21:04:43 +00:00
|
|
|
User? get currentUser => _currentUser;
|
2019-03-18 16:56:15 +00:00
|
|
|
|
2018-09-16 19:47:33 +00:00
|
|
|
Client get client => _client;
|
|
|
|
|
2023-05-16 23:22:59 +00:00
|
|
|
GlobalKey<ScaffoldMessengerState> get snackbarKey => globalSnackbarKey;
|
2022-05-16 21:31:22 +00:00
|
|
|
|
2018-09-16 19:47:33 +00:00
|
|
|
UserManager get userManager => new UserManager(_storage);
|
2019-03-18 16:56:15 +00:00
|
|
|
|
2022-08-27 21:04:43 +00:00
|
|
|
UserService? get newUserService => _newUserService;
|
2022-05-07 15:18:03 +00:00
|
|
|
|
|
|
|
ServerService get serverService => new ServerAPIService(client);
|
2019-03-18 16:56:15 +00:00
|
|
|
|
2022-07-18 21:25:47 +00:00
|
|
|
SettingsManager get settingsManager => new SettingsManager(_storage);
|
|
|
|
|
2023-01-07 01:17:00 +00:00
|
|
|
VersionChecker get versionChecker => new VersionChecker(snackbarKey);
|
|
|
|
|
2023-07-22 23:50:55 +00:00
|
|
|
ProjectService get projectService => new ProjectAPIService(client, _storage);
|
2023-07-22 20:54:50 +00:00
|
|
|
|
2024-04-29 14:29:44 +00:00
|
|
|
ProjectViewService get projectViewService =>
|
|
|
|
new ProjectViewAPIService(client);
|
|
|
|
|
2018-09-17 16:16:50 +00:00
|
|
|
TaskService get taskService => new TaskAPIService(client);
|
2019-03-18 16:56:15 +00:00
|
|
|
|
2022-07-15 14:25:16 +00:00
|
|
|
BucketService get bucketService => new BucketAPIService(client);
|
|
|
|
|
2022-04-15 18:14:04 +00:00
|
|
|
TaskServiceOptions get taskServiceOptions => new TaskServiceOptions();
|
|
|
|
|
2023-01-07 21:50:32 +00:00
|
|
|
NotificationClass get notifications => _notificationClass;
|
2022-04-19 22:12:50 +00:00
|
|
|
|
2021-06-04 09:34:25 +00:00
|
|
|
LabelService get labelService => new LabelAPIService(client);
|
|
|
|
|
|
|
|
LabelTaskService get labelTaskService => new LabelTaskAPIService(client);
|
|
|
|
|
|
|
|
LabelTaskBulkAPIService get labelTaskBulkService =>
|
|
|
|
new LabelTaskBulkAPIService(client);
|
2022-04-19 22:12:50 +00:00
|
|
|
|
2022-08-27 21:04:43 +00:00
|
|
|
late String currentTimeZone;
|
2022-04-29 21:27:18 +00:00
|
|
|
|
2023-01-07 15:55:40 +00:00
|
|
|
void updateWorkmanagerDuration() {
|
2024-04-05 12:35:23 +00:00
|
|
|
if (kIsWeb) {
|
|
|
|
return;
|
|
|
|
}
|
2023-01-07 15:55:40 +00:00
|
|
|
Workmanager().cancelAll().then((value) {
|
2024-04-05 20:36:56 +00:00
|
|
|
settingsManager.getWorkmanagerDuration().then((duration) {
|
|
|
|
if (duration.inMinutes > 0) {
|
|
|
|
Workmanager().registerPeriodicTask("update-tasks", "update-tasks",
|
|
|
|
frequency: duration,
|
2024-06-06 21:12:07 +00:00
|
|
|
constraints: Constraints(
|
|
|
|
networkType: NetworkType.connected, requiresDeviceIdle: true),
|
2024-04-05 20:36:56 +00:00
|
|
|
initialDelay: Duration(seconds: 15),
|
|
|
|
inputData: {
|
|
|
|
"client_token": client.token,
|
|
|
|
"client_base": client.base
|
|
|
|
});
|
2023-01-07 15:55:40 +00:00
|
|
|
}
|
2024-02-11 22:30:45 +00:00
|
|
|
|
2024-04-05 20:36:56 +00:00
|
|
|
Workmanager().registerPeriodicTask("refresh-token", "refresh-token",
|
|
|
|
frequency: Duration(hours: 12),
|
2024-06-06 21:12:07 +00:00
|
|
|
constraints: Constraints(
|
|
|
|
networkType: NetworkType.connected, requiresDeviceIdle: true),
|
2024-02-11 22:30:45 +00:00
|
|
|
initialDelay: Duration(seconds: 15));
|
2023-01-07 15:55:40 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2022-04-29 21:27:18 +00:00
|
|
|
|
2018-09-16 19:47:33 +00:00
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
super.initState();
|
2023-05-16 23:22:59 +00:00
|
|
|
_client = Client(snackbarKey);
|
2024-04-05 20:36:56 +00:00
|
|
|
settingsManager
|
|
|
|
.getIgnoreCertificates()
|
2024-04-05 13:41:48 +00:00
|
|
|
.then((value) => client.reloadIgnoreCerts(value == "1"));
|
2022-05-07 15:18:03 +00:00
|
|
|
_newUserService = UserAPIService(client);
|
2018-09-16 19:47:33 +00:00
|
|
|
_loadCurrentUser();
|
2022-04-19 22:12:50 +00:00
|
|
|
tz.initializeTimeZones();
|
2023-01-07 21:50:32 +00:00
|
|
|
notifications.notificationInitializer();
|
2023-01-07 02:55:29 +00:00
|
|
|
settingsManager.getVersionNotifications().then((value) {
|
2024-04-05 20:36:56 +00:00
|
|
|
if (value == "1") {
|
2023-01-07 02:55:29 +00:00
|
|
|
versionChecker.postVersionCheckSnackbar();
|
|
|
|
}
|
|
|
|
});
|
2018-09-16 19:47:33 +00:00
|
|
|
}
|
|
|
|
|
2022-08-27 21:04:43 +00:00
|
|
|
void changeUser(User newUser, {String? token, String? base}) async {
|
2018-09-16 19:47:33 +00:00
|
|
|
setState(() {
|
|
|
|
_loading = true;
|
|
|
|
});
|
|
|
|
if (token == null) {
|
|
|
|
token = await _storage.read(key: newUser.id.toString());
|
|
|
|
} else {
|
|
|
|
// Write new token to secure storage
|
|
|
|
await _storage.write(key: newUser.id.toString(), value: token);
|
|
|
|
}
|
2018-09-17 13:35:57 +00:00
|
|
|
if (base == null) {
|
|
|
|
base = await _storage.read(key: "${newUser.id.toString()}_base");
|
|
|
|
} else {
|
|
|
|
// Write new base to secure storage
|
|
|
|
await _storage.write(key: "${newUser.id.toString()}_base", value: base);
|
|
|
|
}
|
2018-09-16 19:47:33 +00:00
|
|
|
// Set current user in storage
|
|
|
|
await _storage.write(key: 'currentUser', value: newUser.id.toString());
|
2022-05-07 15:18:03 +00:00
|
|
|
client.configure(token: token, base: base, authenticated: true);
|
2023-01-07 21:50:32 +00:00
|
|
|
updateWorkmanagerDuration();
|
|
|
|
|
2018-09-16 19:47:33 +00:00
|
|
|
setState(() {
|
|
|
|
_currentUser = newUser;
|
|
|
|
_loading = false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-05-12 11:53:41 +00:00
|
|
|
void logoutUser(BuildContext context) async {
|
|
|
|
// _storage.deleteAll().then((_) {
|
2024-04-05 20:36:56 +00:00
|
|
|
var userId = await _storage.read(key: "currentUser");
|
|
|
|
await _storage.delete(key: userId!); //delete token
|
|
|
|
await _storage.delete(key: "${userId}_base");
|
|
|
|
setState(() {
|
|
|
|
client.reset();
|
|
|
|
_currentUser = null;
|
|
|
|
});
|
|
|
|
/* }).catchError((err) {
|
2022-04-10 13:31:56 +00:00
|
|
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
2023-05-16 19:08:58 +00:00
|
|
|
content: Text('An error occurred while logging out!'),
|
2019-03-18 16:56:15 +00:00
|
|
|
));
|
2023-05-12 11:53:41 +00:00
|
|
|
});*/
|
2019-03-18 16:56:15 +00:00
|
|
|
}
|
|
|
|
|
2018-09-16 19:47:33 +00:00
|
|
|
void _loadCurrentUser() async {
|
|
|
|
var currentUser = await _storage.read(key: 'currentUser');
|
2018-09-17 13:35:57 +00:00
|
|
|
if (currentUser == null) {
|
|
|
|
setState(() {
|
|
|
|
_loading = false;
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var token = await _storage.read(key: currentUser);
|
|
|
|
var base = await _storage.read(key: '${currentUser}_base');
|
|
|
|
if (token == null || base == null) {
|
|
|
|
setState(() {
|
|
|
|
_loading = false;
|
|
|
|
});
|
|
|
|
return;
|
2018-09-16 19:47:33 +00:00
|
|
|
}
|
2022-05-07 15:44:11 +00:00
|
|
|
client.configure(token: token, base: base, authenticated: true);
|
2022-09-03 15:43:16 +00:00
|
|
|
User loadedCurrentUser;
|
2018-09-17 16:16:50 +00:00
|
|
|
try {
|
|
|
|
loadedCurrentUser = await UserAPIService(client).getCurrentUser();
|
2024-02-11 22:30:45 +00:00
|
|
|
// load new token from server to avoid expiration
|
|
|
|
String? newToken = await newUserService?.getToken();
|
2024-04-05 20:36:56 +00:00
|
|
|
if (newToken != null) {
|
2024-02-11 22:30:45 +00:00
|
|
|
_storage.write(key: currentUser, value: newToken);
|
|
|
|
client.configure(token: newToken);
|
|
|
|
}
|
2018-09-17 16:16:50 +00:00
|
|
|
} on ApiException catch (e) {
|
2024-04-05 20:36:56 +00:00
|
|
|
dev.log("Error code: " + e.errorCode.toString(), level: 1000);
|
2018-09-17 16:16:50 +00:00
|
|
|
if (e.errorCode ~/ 100 == 4) {
|
2022-05-07 15:18:03 +00:00
|
|
|
client.authenticated = false;
|
2022-04-25 21:47:40 +00:00
|
|
|
if (e.errorCode == 401) {
|
|
|
|
// token has expired, but we can reuse username and base. user just has to enter password again
|
|
|
|
expired = true;
|
|
|
|
}
|
2018-09-17 16:16:50 +00:00
|
|
|
setState(() {
|
2022-05-07 15:18:03 +00:00
|
|
|
client.authenticated = false;
|
2018-09-17 16:16:50 +00:00
|
|
|
_currentUser = null;
|
|
|
|
_loading = false;
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
2022-09-03 15:43:16 +00:00
|
|
|
loadedCurrentUser = User(id: int.parse(currentUser), username: '');
|
2018-09-17 16:16:50 +00:00
|
|
|
} catch (otherExceptions) {
|
2022-09-03 15:43:16 +00:00
|
|
|
loadedCurrentUser = User(id: int.parse(currentUser), username: '');
|
2018-09-17 16:16:50 +00:00
|
|
|
}
|
2023-01-07 21:50:32 +00:00
|
|
|
updateWorkmanagerDuration();
|
2018-09-17 13:35:57 +00:00
|
|
|
setState(() {
|
2018-09-16 19:47:33 +00:00
|
|
|
_currentUser = loadedCurrentUser;
|
2018-09-17 16:16:50 +00:00
|
|
|
_loading = false;
|
2018-09-16 19:47:33 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
if (_loading) {
|
|
|
|
return new Center(child: new CircularProgressIndicator());
|
|
|
|
}
|
2024-04-05 20:36:56 +00:00
|
|
|
if (client.authenticated) {
|
2023-01-07 21:50:32 +00:00
|
|
|
notifications.scheduleDueNotifications(taskService);
|
2022-04-19 22:12:50 +00:00
|
|
|
}
|
2023-05-16 23:22:59 +00:00
|
|
|
return new VikunjaGlobalInherited(
|
2018-09-16 19:47:33 +00:00
|
|
|
data: this,
|
2022-08-27 21:04:43 +00:00
|
|
|
key: UniqueKey(),
|
|
|
|
child: !client.authenticated ? widget.login : widget.child,
|
2018-09-16 19:47:33 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-16 23:22:59 +00:00
|
|
|
class VikunjaGlobalInherited extends InheritedWidget {
|
2018-09-16 19:47:33 +00:00
|
|
|
final VikunjaGlobalState data;
|
|
|
|
|
2023-05-16 23:22:59 +00:00
|
|
|
VikunjaGlobalInherited({Key? key, required this.data, required Widget child})
|
2018-09-16 19:47:33 +00:00
|
|
|
: super(key: key, child: child);
|
|
|
|
|
|
|
|
@override
|
2023-05-16 23:22:59 +00:00
|
|
|
bool updateShouldNotify(VikunjaGlobalInherited oldWidget) {
|
2018-09-16 19:47:53 +00:00
|
|
|
return (data.currentUser != null &&
|
2022-08-27 21:04:43 +00:00
|
|
|
data.currentUser!.id != oldWidget.data.currentUser!.id) ||
|
2018-09-16 19:47:33 +00:00
|
|
|
data.client != oldWidget.data.client;
|
|
|
|
}
|
|
|
|
}
|