diff --git a/.vscode/launch.json b/.vscode/launch.json index dc01eb3..f923891 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,7 +7,7 @@ "flutterMode": "debug", "args": [ "--flavor", - "main" + "core" ], } ] diff --git a/android/app/build.gradle b/android/app/build.gradle index 75f0c58..9cdf098 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -32,7 +32,7 @@ if (keystorePropertiesFile.exists()) { } android { - compileSdkVersion 33 + compileSdkVersion 34 sourceSets { main.java.srcDirs += 'src/main/kotlin' @@ -41,8 +41,8 @@ android { defaultConfig { applicationId "io.vikunja.app" - minSdkVersion 21 - targetSdkVersion 33 + minSdkVersion 33 + targetSdkVersion 34 testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 7744c5e..fd4ace3 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -23,6 +23,7 @@ android:icon="@mipmap/ic_launcher" android:networkSecurityConfig="@xml/network_security_config" > + - + diff --git a/android/build.gradle b/android/build.gradle index fbe7210..09dcd8d 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.7.20' + ext.kotlin_version = '1.9.23' repositories { google() mavenCentral() diff --git a/lib/api/client.dart b/lib/api/client.dart index 2658470..1ec07c6 100644 --- a/lib/api/client.dart +++ b/lib/api/client.dart @@ -9,7 +9,6 @@ import 'package:vikunja_app/global.dart'; import '../main.dart'; - class Client { GlobalKey? global_scaffold_key; final JsonDecoder _decoder = new JsonDecoder(); @@ -25,8 +24,6 @@ class Client { String? post_body; - - bool operator ==(dynamic otherClient) { return otherClient._token == _token; } @@ -36,18 +33,17 @@ class Client { configure(token: token, base: base, authenticated: authenticated); } - void reload_ignore_certs(bool? val) { + void reloadIgnoreCerts(bool? val) { ignoreCertificates = val ?? false; HttpOverrides.global = new IgnoreCertHttpOverrides(ignoreCertificates); - if(global_scaffold_key == null || global_scaffold_key!.currentContext == null) return; - VikunjaGlobal - .of(global_scaffold_key!.currentContext!) + if (global_scaffold_key == null || + global_scaffold_key!.currentContext == null) return; + VikunjaGlobal.of(global_scaffold_key!.currentContext!) .settingsManager .setIgnoreCertificates(ignoreCertificates); } - get _headers => - { + get _headers => { 'Authorization': _token != '' ? 'Bearer $_token' : '', 'Content-Type': 'application/json', 'User-Agent': 'Vikunja Mobile App' @@ -59,20 +55,15 @@ class Client { int get hashCode => _token.hashCode; void configure({String? token, String? base, bool? authenticated}) { - if (token != null) - _token = token; + if (token != null) _token = token; if (base != null) { base = base.replaceAll(" ", ""); - if(base.endsWith("/")) - base = base.substring(0,base.length-1); + if (base.endsWith("/")) base = base.substring(0, base.length - 1); _base = base.endsWith('/api/v1') ? base : '$base/api/v1'; } - if (authenticated != null) - this.authenticated = authenticated; - + if (authenticated != null) this.authenticated = authenticated; } - void reset() { _token = _base = ''; authenticated = false; @@ -84,54 +75,61 @@ class Client { // why are we doing it like this? because Uri doesnt have setters. wtf. uri = Uri( - scheme: uri.scheme, + scheme: uri.scheme, userInfo: uri.userInfo, host: uri.host, port: uri.port, path: uri.path, //queryParameters: {...uri.queryParameters, ...?queryParameters}, queryParameters: queryParameters, - fragment: uri.fragment - ); + fragment: uri.fragment); - return http.get(uri, headers: _headers) - .then(_handleResponse).onError((error, stackTrace) => - _handleError(error, stackTrace)); + return http + .get(uri, headers: _headers) + .then(_handleResponse) + .onError((error, stackTrace) => _handleError(error, stackTrace)); } Future delete(String url) { - return http.delete( - '${this.base}$url'.toUri()!, - headers: _headers, - ).then(_handleResponse).onError((error, stackTrace) => - _handleError(error, stackTrace)); + return http + .delete( + '${this.base}$url'.toUri()!, + headers: _headers, + ) + .then(_handleResponse) + .onError((error, stackTrace) => _handleError(error, stackTrace)); } Future post(String url, {dynamic body}) { - return http.post( - '${this.base}$url'.toUri()!, - headers: _headers, - body: _encoder.convert(body), - ) - .then(_handleResponse).onError((error, stackTrace) => - _handleError(error, stackTrace)); + return http + .post( + '${this.base}$url'.toUri()!, + headers: _headers, + body: _encoder.convert(body), + ) + .then(_handleResponse) + .onError((error, stackTrace) => _handleError(error, stackTrace)); } Future put(String url, {dynamic body}) { - return http.put( - '${this.base}$url'.toUri()!, - headers: _headers, - body: _encoder.convert(body), - ) - .then(_handleResponse).onError((error, stackTrace) => - _handleError(error, stackTrace)); + return http + .put( + '${this.base}$url'.toUri()!, + headers: _headers, + body: _encoder.convert(body), + ) + .then(_handleResponse) + .onError((error, stackTrace) => _handleError(error, stackTrace)); } Response? _handleError(Object? e, StackTrace? st) { - if(global_scaffold_key == null) return null; + if (global_scaffold_key == null) return null; SnackBar snackBar = SnackBar( content: Text("Error on request: " + e.toString()), - action: SnackBarAction(label: "Clear", onPressed: () => global_scaffold_key!.currentState?.clearSnackBars()),); + action: SnackBarAction( + label: "Clear", + onPressed: () => global_scaffold_key!.currentState?.clearSnackBars()), + ); global_scaffold_key!.currentState?.showSnackBar(snackBar); return null; } @@ -144,39 +142,38 @@ class Client { return map; } - Error? _handleResponseErrors(http.Response response) { - if (response.statusCode < 200 || - response.statusCode >= 400) { + if (response.statusCode < 200 || response.statusCode >= 400) { Map error; error = _decoder.convert(response.body); - final SnackBar snackBar = SnackBar( - content: Text( - "Error code " + response.statusCode.toString() + " received."), - action: globalNavigatorKey.currentContext == null ? null : SnackBarAction( - label: ("Details"), - onPressed: () { - showDialog( - context: globalNavigatorKey.currentContext!, - builder: (BuildContext context) => - AlertDialog( - title: Text("Error ${response.statusCode}"), - content: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Text("Message: ${error["message"]}", textAlign: TextAlign.start,), - Text("Url: ${response.request!.url.toString()}"), - ], - ) - ) - ); - }, - ), + content: + Text("Error code " + response.statusCode.toString() + " received."), + action: globalNavigatorKey.currentContext == null + ? null + : SnackBarAction( + label: ("Details"), + onPressed: () { + showDialog( + context: globalNavigatorKey.currentContext!, + builder: (BuildContext context) => AlertDialog( + title: Text("Error ${response.statusCode}"), + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "Message: ${error["message"]}", + textAlign: TextAlign.start, + ), + Text("Url: ${response.request!.url.toString()}"), + ], + ))); + }, + ), ); - if(global_scaffold_key != null && showSnackBar) + if (global_scaffold_key != null && showSnackBar) global_scaffold_key!.currentState?.showSnackBar(snackBar); else print("error on request: ${error["message"]}"); diff --git a/lib/api/list_implementation.dart b/lib/api/list_implementation.dart index 1debbbf..e55513b 100644 --- a/lib/api/list_implementation.dart +++ b/lib/api/list_implementation.dart @@ -8,7 +8,9 @@ import 'package:vikunja_app/service/services.dart'; class ListAPIService extends APIService implements ListService { FlutterSecureStorage _storage; - ListAPIService(Client client, FlutterSecureStorage storage) : _storage = storage, super(client); + ListAPIService(Client client, FlutterSecureStorage storage) + : _storage = storage, + super(client); @override Future create(namespaceId, TaskList tl) { @@ -16,9 +18,9 @@ class ListAPIService extends APIService implements ListService { return client .put('/namespaces/$namespaceId/lists', body: tl.toJSON()) .then((response) { - if (response == null) return null; - return TaskList.fromJson(response.body); - }); + if (response == null) return null; + return TaskList.fromJson(response.body); + }); } @override @@ -32,12 +34,10 @@ class ListAPIService extends APIService implements ListService { if (response == null) return null; final map = response.body; if (map.containsKey('id')) { - return client - .get("/lists/$listId/tasks") - .then((tasks) { - map['tasks'] = tasks?.body; - return TaskList.fromJson(map); - }); + return client.get("/lists/$listId/tasks").then((tasks) { + map['tasks'] = tasks?.body; + return TaskList.fromJson(map); + }); } return TaskList.fromJson(map); }); @@ -45,47 +45,44 @@ class ListAPIService extends APIService implements ListService { @override Future?> getAll() { - return client.get('/lists').then( - (list) { - if (list == null || list.statusCode != 200) return null; - if (list.body.toString().isEmpty) - return Future.value([]); - print(list.statusCode); - return convertList(list.body, (result) => TaskList.fromJson(result));}); + return client.get('/lists').then((list) { + if (list == null || list.statusCode != 200) return null; + if (list.body.toString().isEmpty) return Future.value([]); + print(list.statusCode); + return convertList(list.body, (result) => TaskList.fromJson(result)); + }); } @override Future?> getByNamespace(int namespaceId) { // TODO there needs to be a better way for this. /namespaces/-2/lists should // return favorite lists - if(namespaceId == -2) { + if (namespaceId == -2) { // Favourites. return getAll().then((value) { if (value == null) return null; - value.removeWhere((element) => !element.isFavorite); return value;}); + value.removeWhere((element) => !element.isFavorite); + return value; + }); } - return client.get('/namespaces/$namespaceId/lists').then( - (list) { - if (list == null || list.statusCode != 200) return null; - return convertList(list.body, (result) => TaskList.fromJson(result)); - }); + return client.get('/namespaces/$namespaceId/lists').then((list) { + if (list == null || list.statusCode != 200) return null; + return convertList(list.body, (result) => TaskList.fromJson(result)); + }); } @override Future update(TaskList tl) { - return client - .post('/lists/${tl.id}', body: tl.toJSON()) - .then((response) { - if (response == null) return null; - return TaskList.fromJson(response.body); - }); + return client.post('/lists/${tl.id}', body: tl.toJSON()).then((response) { + if (response == null) return null; + return TaskList.fromJson(response.body); + }); } @override Future getDisplayDoneTasks(int listId) { - return _storage.read(key: "display_done_tasks_list_$listId").then((value) - { - if(value == null) { + return _storage.read(key: "display_done_tasks_list_$listId").then((value) { + if (value == null) { // TODO: implement default value setDisplayDoneTasks(listId, "1"); return Future.value("1"); @@ -104,7 +101,6 @@ class ListAPIService extends APIService implements ListService { return _storage.read(key: "default_list_id"); } - @override void setDefaultList(int? listId) { _storage.write(key: "default_list_id", value: listId.toString()); } diff --git a/lib/api/project.dart b/lib/api/project.dart index 9f2f6b4..f094965 100644 --- a/lib/api/project.dart +++ b/lib/api/project.dart @@ -6,7 +6,9 @@ import 'package:vikunja_app/service/services.dart'; class ProjectAPIService extends APIService implements ProjectService { FlutterSecureStorage _storage; - ProjectAPIService(client, storage) : _storage = storage, super(client); + ProjectAPIService(client, storage) + : _storage = storage, + super(client); @override Future create(Project p) { @@ -47,9 +49,7 @@ class ProjectAPIService extends APIService implements ProjectService { @override Future update(Project p) { - return client - .post('/projects/${p.id}', body: p.toJSON()) - .then((response) { + return client.post('/projects/${p.id}', body: p.toJSON()).then((response) { if (response == null) return null; return Project.fromJson(response.body); }); @@ -57,9 +57,8 @@ class ProjectAPIService extends APIService implements ProjectService { @override Future getDisplayDoneTasks(int listId) { - return _storage.read(key: "display_done_tasks_list_$listId").then((value) - { - if(value == null) { + return _storage.read(key: "display_done_tasks_list_$listId").then((value) { + if (value == null) { // TODO: implement default value setDisplayDoneTasks(listId, "1"); return Future.value("1"); @@ -68,19 +67,15 @@ class ProjectAPIService extends APIService implements ProjectService { }); } - @override void setDisplayDoneTasks(int listId, String value) { _storage.write(key: "display_done_tasks_list_$listId", value: value); } - @override Future getDefaultList() { return _storage.read(key: "default_list_id"); } - @override void setDefaultList(int? listId) { _storage.write(key: "default_list_id", value: listId.toString()); } - -} \ No newline at end of file +} diff --git a/lib/components/BucketTaskCard.dart b/lib/components/BucketTaskCard.dart index 73a371c..dfdfbd9 100644 --- a/lib/components/BucketTaskCard.dart +++ b/lib/components/BucketTaskCard.dart @@ -11,7 +11,7 @@ import 'package:vikunja_app/theme/constants.dart'; import '../stores/project_store.dart'; -enum DropLocation {above, below, none} +enum DropLocation { above, below, none } class TaskData { final Task task; @@ -37,7 +37,8 @@ class BucketTaskCard extends StatefulWidget { State createState() => _BucketTaskCardState(); } -class _BucketTaskCardState extends State with AutomaticKeepAliveClientMixin { +class _BucketTaskCardState extends State + with AutomaticKeepAliveClientMixin { Size? _cardSize; bool _dragging = false; DropLocation _dropLocation = DropLocation.none; @@ -49,7 +50,8 @@ class _BucketTaskCardState extends State with AutomaticKeepAlive if (_cardSize == null) _updateCardSize(context); final taskState = Provider.of(context); - final bucket = taskState.buckets[taskState.buckets.indexWhere((b) => b.id == widget.task.bucketId)]; + final bucket = taskState.buckets[ + taskState.buckets.indexWhere((b) => b.id == widget.task.bucketId)]; // default chip height: 32 const double chipHeight = 28; const chipConstraints = BoxConstraints(maxHeight: chipHeight); @@ -59,7 +61,8 @@ class _BucketTaskCardState extends State with AutomaticKeepAlive children: [ Text( widget.task.identifier.isNotEmpty - ? '#${widget.task.identifier}' : '${widget.task.id}', + ? '#${widget.task.identifier}' + : '${widget.task.id}', style: (theme.textTheme.titleSmall ?? TextStyle()).copyWith( color: Colors.grey, ), @@ -67,21 +70,25 @@ class _BucketTaskCardState extends State with AutomaticKeepAlive ], ); if (widget.task.done) { - identifierRow.children.insert(0, Container( - constraints: chipConstraints, - padding: EdgeInsets.only(right: 4), - child: FittedBox( - child: Chip( - label: Text('Done'), - labelStyle: (theme.textTheme.labelLarge ?? TextStyle()).copyWith( - fontWeight: FontWeight.bold, - color: theme.brightness == Brightness.dark - ? Colors.black : Colors.white, + identifierRow.children.insert( + 0, + Container( + constraints: chipConstraints, + padding: EdgeInsets.only(right: 4), + child: FittedBox( + child: Chip( + label: Text('Done'), + labelStyle: + (theme.textTheme.labelLarge ?? TextStyle()).copyWith( + fontWeight: FontWeight.bold, + color: theme.brightness == Brightness.dark + ? Colors.black + : Colors.white, + ), + backgroundColor: vGreen, + ), ), - backgroundColor: vGreen, - ), - ), - )); + )); } final titleRow = Row( @@ -89,9 +96,11 @@ class _BucketTaskCardState extends State with AutomaticKeepAlive Expanded( child: Text( widget.task.title, - style: (theme.textTheme.titleMedium ?? TextStyle(fontSize: 16)).copyWith( + style: (theme.textTheme.titleMedium ?? TextStyle(fontSize: 16)) + .copyWith( color: theme.brightness == Brightness.dark - ? Colors.white : Colors.black, + ? Colors.white + : Colors.black, ), ), ), @@ -145,10 +154,10 @@ class _BucketTaskCardState extends State with AutomaticKeepAlive backgroundColor: Colors.grey, ), ), - label: Text( - (checkboxStatistics.checked == checkboxStatistics.total ? '' : '${checkboxStatistics.checked} of ') - + '${checkboxStatistics.total} tasks' - ), + label: Text((checkboxStatistics.checked == checkboxStatistics.total + ? '' + : '${checkboxStatistics.checked} of ') + + '${checkboxStatistics.total} tasks'), )); } if (widget.task.attachments.isNotEmpty) { @@ -185,7 +194,8 @@ class _BucketTaskCardState extends State with AutomaticKeepAlive child: identifierRow, ), Padding( - padding: EdgeInsets.only(top: 4, bottom: labelRow.children.isNotEmpty ? 8 : 0), + padding: EdgeInsets.only( + top: 4, bottom: labelRow.children.isNotEmpty ? 8 : 0), child: Container( constraints: rowConstraints, child: titleRow, @@ -213,7 +223,9 @@ class _BucketTaskCardState extends State with AutomaticKeepAlive return LongPressDraggable( data: TaskData(widget.task, _cardSize), - maxSimultaneousDrags: taskState.taskDragging ? 0 : 1, // only one task can be dragged at a time + maxSimultaneousDrags: taskState.taskDragging + ? 0 + : 1, // only one task can be dragged at a time onDragStarted: () { taskState.taskDragging = true; setState(() => _dragging = true); @@ -223,14 +235,16 @@ class _BucketTaskCardState extends State with AutomaticKeepAlive taskState.taskDragging = false; setState(() => _dragging = false); }, - feedback: (_cardSize == null) ? SizedBox.shrink() : SizedBox.fromSize( - size: _cardSize, - child: Card( - color: card.color, - child: (card.child as InkWell).child, - elevation: (card.elevation ?? 0) + 5, - ), - ), + feedback: (_cardSize == null) + ? SizedBox.shrink() + : SizedBox.fromSize( + size: _cardSize, + child: Card( + color: card.color, + child: (card.child as InkWell).child, + elevation: (card.elevation ?? 0) + 5, + ), + ), childWhenDragging: SizedBox.shrink(), child: () { if (_dragging || _cardSize == null) return card; @@ -241,25 +255,30 @@ class _BucketTaskCardState extends State with AutomaticKeepAlive color: Colors.grey, child: SizedBox.fromSize(size: dropBoxSize), ); - final dropAbove = taskState.taskDragging && _dropLocation == DropLocation.above; - final dropBelow = taskState.taskDragging && _dropLocation == DropLocation.below; - final DragTargetLeave dragTargetOnLeave = (data) => setState(() { - _dropLocation = DropLocation.none; - _dropData = null; - }); - final dragTargetOnWillAccept = (TaskData data, DropLocation dropLocation) { - if (data.task.bucketId != bucket.id) - if (bucket.limit != 0 && bucket.tasks.length >= bucket.limit) - return false; + final dropAbove = + taskState.taskDragging && _dropLocation == DropLocation.above; + final dropBelow = + taskState.taskDragging && _dropLocation == DropLocation.below; + final DragTargetLeave dragTargetOnLeave = + (data) => setState(() { + _dropLocation = DropLocation.none; + _dropData = null; + }); + final dragTargetOnWillAccept = + (TaskData data, DropLocation dropLocation) { + if (data.task.bucketId != bucket.id) if (bucket.limit != 0 && + bucket.tasks.length >= bucket.limit) return false; setState(() { _dropLocation = dropLocation; _dropData = data; }); return true; }; - final DragTargetAccept dragTargetOnAccept = (data) { + final DragTargetAccept> dragTargetOnAccept = + (data) { final index = bucket.tasks.indexOf(widget.task); - widget.onAccept(data.task, _dropLocation == DropLocation.above ? index : index + 1); + widget.onAccept(data.data.task, + _dropLocation == DropLocation.above ? index : index + 1); setState(() { _dropLocation = DropLocation.none; _dropData = null; @@ -268,7 +287,8 @@ class _BucketTaskCardState extends State with AutomaticKeepAlive return SizedBox( width: cardSize.width, - height: cardSize.height + (dropAbove || dropBelow ? dropBoxSize.height + 4 : 0), + height: cardSize.height + + (dropAbove || dropBelow ? dropBoxSize.height + 4 : 0), child: Stack( children: [ Column( @@ -281,18 +301,22 @@ class _BucketTaskCardState extends State with AutomaticKeepAlive Column( children: [ SizedBox( - height: (cardSize.height / 2) + (dropAbove ? dropBoxSize.height : 0), + height: (cardSize.height / 2) + + (dropAbove ? dropBoxSize.height : 0), child: DragTarget( - onWillAcceptWithDetails: (data) => dragTargetOnWillAccept(data, DropLocation.above), + onWillAcceptWithDetails: (data) => + dragTargetOnWillAccept(data.data, DropLocation.above), onAcceptWithDetails: dragTargetOnAccept, onLeave: dragTargetOnLeave, builder: (_, __, ___) => SizedBox.expand(), ), ), SizedBox( - height: (cardSize.height / 2) + (dropBelow ? dropBoxSize.height : 0), + height: (cardSize.height / 2) + + (dropBelow ? dropBoxSize.height : 0), child: DragTarget( - onWillAcceptWithDetails: (data) => dragTargetOnWillAccept(data, DropLocation.below), + onWillAcceptWithDetails: (data) => + dragTargetOnWillAccept(data.data, DropLocation.below), onAcceptWithDetails: dragTargetOnAccept, onLeave: dragTargetOnLeave, builder: (_, __, ___) => SizedBox.expand(), @@ -309,12 +333,13 @@ class _BucketTaskCardState extends State with AutomaticKeepAlive void _updateCardSize(BuildContext context) { SchedulerBinding.instance.addPostFrameCallback((_) { - if (mounted) setState(() { - _cardSize = context.size; - }); + if (mounted) + setState(() { + _cardSize = context.size; + }); }); } @override bool get wantKeepAlive => _dragging; -} \ No newline at end of file +} diff --git a/lib/components/KanbanWidget.dart b/lib/components/KanbanWidget.dart index 287031b..fbbff7e 100644 --- a/lib/components/KanbanWidget.dart +++ b/lib/components/KanbanWidget.dart @@ -25,23 +25,22 @@ class KanbanClass { Function _onViewTapped, _addItemDialog, notify; Duration _lastTaskDragUpdateAction = Duration.zero; - Project _list; Map _bucketProps = {}; - - KanbanClass(this.context, this.notify, this._onViewTapped, this._addItemDialog, this._list) { + KanbanClass(this.context, this.notify, this._onViewTapped, + this._addItemDialog, this._list) { taskState = Provider.of(context); } - Widget kanbanView() { final deviceData = MediaQuery.of(context); final portrait = deviceData.orientation == Orientation.portrait; final bucketFraction = portrait ? 0.8 : 0.4; final bucketWidth = deviceData.size.width * bucketFraction; - if (_pageController == null || _pageController!.viewportFraction != bucketFraction) + if (_pageController == null || + _pageController!.viewportFraction != bucketFraction) _pageController = PageController(viewportFraction: bucketFraction); print(_list.doneBucketId); @@ -170,14 +169,16 @@ class KanbanClass { ), )); } + Future _setDoneBucket(BuildContext context, int bucketId) async { //setState(() {}); - _list = (await VikunjaGlobal.of(context).projectService.update(_list.copyWith(doneBucketId: bucketId)))!; + _list = (await VikunjaGlobal.of(context) + .projectService + .update(_list.copyWith(doneBucketId: bucketId)))!; notify(); } - Future _addBucket( - String title, BuildContext context) async { + Future _addBucket(String title, BuildContext context) async { final currentUser = VikunjaGlobal.of(context).currentUser; if (currentUser == null) { return; @@ -256,14 +257,12 @@ class KanbanClass { SchedulerBinding.instance.addPostFrameCallback((_) { if (_bucketProps[bucket.id]!.controller.hasClients) //setState(() { - _bucketProps[bucket.id]!.bucketLength = bucket.tasks.length; - _bucketProps[bucket.id]!.scrollable = - _bucketProps[bucket.id]!.controller.position.maxScrollExtent > - 0; - _bucketProps[bucket.id]!.portrait = portrait; - //}); + _bucketProps[bucket.id]!.bucketLength = bucket.tasks.length; + _bucketProps[bucket.id]!.scrollable = + _bucketProps[bucket.id]!.controller.position.maxScrollExtent > 0; + _bucketProps[bucket.id]!.portrait = portrait; + //}); notify(); - }); if (_bucketProps[bucket.id]!.titleController.text.isEmpty) _bucketProps[bucket.id]!.titleController.text = bucket.title; @@ -427,10 +426,11 @@ class KanbanClass { final screenSize = MediaQuery.of(context).size; const scrollDuration = Duration(milliseconds: 250); const scrollCurve = Curves.easeInOut; - final updateAction = () { //setState(() => + final updateAction = () { + //setState(() => _lastTaskDragUpdateAction = details.sourceTimeStamp!; notify(); - };//); + }; //); if (details.globalPosition.dx < screenSize.width * 0.1) { // scroll left @@ -502,8 +502,8 @@ class KanbanClass { if (bucket.tasks.length == 0) DragTarget( onWillAcceptWithDetails: (data) { - /*setState(() =>*/ _bucketProps[bucket.id]!.taskDropSize = - data.size;//); + /*setState(() =>*/ _bucketProps[bucket.id]! + .taskDropSize = data.data.size; //); notify(); return true; }, @@ -511,23 +511,23 @@ class KanbanClass { Provider.of(context, listen: false) .moveTaskToBucket( context: context, - task: data.task, + task: data.data.task, newBucketId: bucket.id, index: 0, ) .then((_) => ScaffoldMessenger.of(context) .showSnackBar(SnackBar( content: Text( - '\'${data.task.title}\' was moved to \'${bucket.title}\' successfully!'), + '\'${data.data.task.title}\' was moved to \'${bucket.title}\' successfully!'), ))); //setState(() => - _bucketProps[bucket.id]!.taskDropSize = null;//); + _bucketProps[bucket.id]!.taskDropSize = null; //); notify(); }, onLeave: (_) { //setState(() => - _bucketProps[bucket.id]!.taskDropSize = null;//) + _bucketProps[bucket.id]!.taskDropSize = null; //) notify(); }, builder: (_, __, ___) => SizedBox.expand(), @@ -548,12 +548,7 @@ class KanbanClass { } Future loadBucketsForPage(int page) { - return Provider.of(context, listen: false).loadBuckets( - context: context, - listId: _list.id, - page: page - ); + return Provider.of(context, listen: false) + .loadBuckets(context: context, listId: _list.id, page: page); } - - } diff --git a/lib/global.dart b/lib/global.dart index 5cf490c..006b4fc 100644 --- a/lib/global.dart +++ b/lib/global.dart @@ -23,7 +23,6 @@ import 'package:workmanager/workmanager.dart'; import 'api/project.dart'; import 'main.dart'; - class VikunjaGlobal extends StatefulWidget { final Widget child; final Widget login; @@ -50,7 +49,6 @@ class VikunjaGlobalState extends State { UserService? _newUserService; NotificationClass _notificationClass = NotificationClass(); - User? get currentUser => _currentUser; Client get client => _client; @@ -81,7 +79,6 @@ class VikunjaGlobalState extends State { NotificationClass get notifications => _notificationClass; - LabelService get labelService => new LabelAPIService(client); LabelTaskService get labelTaskService => new LabelTaskAPIService(client); @@ -89,21 +86,25 @@ class VikunjaGlobalState extends State { LabelTaskBulkAPIService get labelTaskBulkService => new LabelTaskBulkAPIService(client); - late String currentTimeZone; void updateWorkmanagerDuration() { Workmanager().cancelAll().then((value) { - settingsManager.getWorkmanagerDuration().then((duration) - { - if(duration.inMinutes > 0) { - Workmanager().registerPeriodicTask( - "update-tasks", "update-tasks", frequency: duration, constraints: Constraints(networkType: NetworkType.connected), - initialDelay: Duration(seconds: 15), inputData: {"client_token": client.token, "client_base": client.base}); + settingsManager.getWorkmanagerDuration().then((duration) { + if (duration.inMinutes > 0) { + Workmanager().registerPeriodicTask("update-tasks", "update-tasks", + frequency: duration, + constraints: Constraints(networkType: NetworkType.connected), + initialDelay: Duration(seconds: 15), + inputData: { + "client_token": client.token, + "client_base": client.base + }); } - Workmanager().registerPeriodicTask( - "refresh-token", "refresh-token", frequency: Duration(hours: 12), constraints: Constraints(networkType: NetworkType.connected), + Workmanager().registerPeriodicTask("refresh-token", "refresh-token", + frequency: Duration(hours: 12), + constraints: Constraints(networkType: NetworkType.connected), initialDelay: Duration(seconds: 15)); }); }); @@ -113,13 +114,15 @@ class VikunjaGlobalState extends State { void initState() { super.initState(); _client = Client(snackbarKey); - settingsManager.getIgnoreCertificates().then((value) => client.reload_ignore_certs(value == "1")); + settingsManager + .getIgnoreCertificates() + .then((value) => client.reloadIgnoreCerts(value == "1")); _newUserService = UserAPIService(client); _loadCurrentUser(); tz.initializeTimeZones(); notifications.notificationInitializer(); settingsManager.getVersionNotifications().then((value) { - if(value == "1") { + if (value == "1") { versionChecker.postVersionCheckSnackbar(); } }); @@ -152,17 +155,16 @@ class VikunjaGlobalState extends State { }); } - void logoutUser(BuildContext context) async { // _storage.deleteAll().then((_) { - 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) { + 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) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text('An error occurred while logging out!'), )); @@ -191,13 +193,12 @@ class VikunjaGlobalState extends State { loadedCurrentUser = await UserAPIService(client).getCurrentUser(); // load new token from server to avoid expiration String? newToken = await newUserService?.getToken(); - if(newToken != null) { + if (newToken != null) { _storage.write(key: currentUser, value: newToken); client.configure(token: newToken); } - } on ApiException catch (e) { - dev.log("Error code: " + e.errorCode.toString(),level: 1000); + dev.log("Error code: " + e.errorCode.toString(), level: 1000); if (e.errorCode ~/ 100 == 4) { client.authenticated = false; if (e.errorCode == 401) { @@ -227,7 +228,7 @@ class VikunjaGlobalState extends State { if (_loading) { return new Center(child: new CircularProgressIndicator()); } - if(client.authenticated) { + if (client.authenticated) { notifications.scheduleDueNotifications(taskService); } return new VikunjaGlobalInherited( diff --git a/lib/main.dart b/lib/main.dart index b0ae919..efece9a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -35,7 +35,8 @@ class IgnoreCertHttpOverrides extends HttpOverrides { @pragma('vm:entry-point') void callbackDispatcher() { Workmanager().executeTask((task, inputData) async { - print("Native called background task: $task"); //simpleTask will be emitted here. + print( + "Native called background task: $task"); //simpleTask will be emitted here. if (task == "update-tasks" && inputData != null) { Client client = Client(null, token: inputData["client_token"], @@ -47,7 +48,7 @@ void callbackDispatcher() { .getIgnoreCertificates() .then((value) async { print("ignoring: $value"); - client.reload_ignore_certs(value == "1"); + client.reloadIgnoreCerts(value == "1"); TaskAPIService taskService = TaskAPIService(client); NotificationClass nc = NotificationClass(); @@ -56,7 +57,7 @@ void callbackDispatcher() { .scheduleDueNotifications(taskService) .then((value) => Future.value(true)); }); - } else if( task == "refresh-token") { + } else if (task == "refresh-token") { print("running refresh from workmanager"); final FlutterSecureStorage _storage = new FlutterSecureStorage(); @@ -66,25 +67,24 @@ void callbackDispatcher() { } var token = await _storage.read(key: currentUser); - var base = await _storage.read(key: '${currentUser}_base'); if (token == null || base == null) { return Future.value(true); } Client client = Client(null); client.configure(token: token, base: base, authenticated: true); - // load new token from server to avoid expiration - String? newToken = await UserAPIService(client).getToken(); - if(newToken != null) { - _storage.write(key: currentUser, value: newToken); - } + // load new token from server to avoid expiration + String? newToken = await UserAPIService(client).getToken(); + if (newToken != null) { + _storage.write(key: currentUser, value: newToken); + } return Future.value(true); - } else { return Future.value(true); } }); } + final globalSnackbarKey = GlobalKey(); final globalNavigatorKey = GlobalKey(); @@ -108,60 +108,62 @@ void main() async { key: UniqueKey(), ))); } + final ValueNotifier updateTheme = ValueNotifier(false); class VikunjaApp extends StatelessWidget { final Widget home; final GlobalKey? navkey; - const VikunjaApp({Key? key, required this.home, this.navkey}) : super(key: key); + const VikunjaApp({Key? key, required this.home, this.navkey}) + : super(key: key); @override Widget build(BuildContext context) { - SettingsManager manager = SettingsManager(new FlutterSecureStorage()); - - return new ValueListenableBuilder(valueListenable: updateTheme, builder: (_,mode,__) { - updateTheme.value = false; - FlutterThemeMode themeMode = FlutterThemeMode.system; - Future theme = manager.getThemeMode().then((value) { - themeMode = value; - switch(value) { - case FlutterThemeMode.dark: - return buildVikunjaDarkTheme(); - case FlutterThemeMode.materialYouLight: - return buildVikunjaMaterialLightTheme(); - case FlutterThemeMode.materialYouDark: - return buildVikunjaMaterialDarkTheme(); - default: - return buildVikunjaTheme(); - } - - }); - return FutureBuilder( - future: theme, - builder: (BuildContext context, AsyncSnapshot data) { - if(data.hasData) { - return new DynamicColorBuilder(builder: (lightTheme, darkTheme) - { - ThemeData? themeData = data.data; - if(themeMode == FlutterThemeMode.materialYouLight) - themeData = themeData?.copyWith(colorScheme: lightTheme); - else if(themeMode == FlutterThemeMode.materialYouDark) - themeData = themeData?.copyWith(colorScheme: darkTheme); - return MaterialApp( - title: 'Vikunja', - theme: themeData, - scaffoldMessengerKey: globalSnackbarKey, - navigatorKey: navkey, - // <= this - home: this.home, - ); - }); - } else { - return Center(child: CircularProgressIndicator()); - } - });}); + return new ValueListenableBuilder( + valueListenable: updateTheme, + builder: (_, mode, __) { + updateTheme.value = false; + FlutterThemeMode themeMode = FlutterThemeMode.system; + Future theme = manager.getThemeMode().then((value) { + themeMode = value; + switch (value) { + case FlutterThemeMode.dark: + return buildVikunjaDarkTheme(); + case FlutterThemeMode.materialYouLight: + return buildVikunjaMaterialLightTheme(); + case FlutterThemeMode.materialYouDark: + return buildVikunjaMaterialDarkTheme(); + default: + return buildVikunjaTheme(); + } + }); + return FutureBuilder( + future: theme, + builder: (BuildContext context, AsyncSnapshot data) { + if (data.hasData) { + return new DynamicColorBuilder( + builder: (lightTheme, darkTheme) { + ThemeData? themeData = data.data; + if (themeMode == FlutterThemeMode.materialYouLight) + themeData = themeData?.copyWith(colorScheme: lightTheme); + else if (themeMode == FlutterThemeMode.materialYouDark) + themeData = themeData?.copyWith(colorScheme: darkTheme); + return MaterialApp( + title: 'Vikunja', + theme: themeData, + scaffoldMessengerKey: globalSnackbarKey, + navigatorKey: navkey, + // <= this + home: this.home, + ); + }); + } else { + return Center(child: CircularProgressIndicator()); + } + }); + }); } } diff --git a/lib/managers/notifications.dart b/lib/managers/notifications.dart index 973ca89..2b96f18 100644 --- a/lib/managers/notifications.dart +++ b/lib/managers/notifications.dart @@ -5,7 +5,8 @@ import 'dart:math'; import 'package:flutter_timezone/flutter_timezone.dart'; import 'package:timezone/timezone.dart' as tz; -import 'package:flutter_local_notifications/flutter_local_notifications.dart'as notifs; +import 'package:flutter_local_notifications/flutter_local_notifications.dart' + as notifs; import 'package:rxdart/subjects.dart' as rxSub; import 'package:vikunja_app/service/services.dart'; @@ -19,62 +20,58 @@ class NotificationClass { late String currentTimeZone; notifs.NotificationAppLaunchDetails? notifLaunch; - notifs.FlutterLocalNotificationsPlugin get notificationsPlugin => new notifs.FlutterLocalNotificationsPlugin(); - + notifs.FlutterLocalNotificationsPlugin get notificationsPlugin => + new notifs.FlutterLocalNotificationsPlugin(); var androidSpecificsDueDate = notifs.AndroidNotificationDetails( - "Vikunja1", - "Due Date Notifications", + "Vikunja1", "Due Date Notifications", channelDescription: "description", icon: 'vikunja_notification_logo', - importance: notifs.Importance.high - ); + importance: notifs.Importance.high); var androidSpecificsReminders = notifs.AndroidNotificationDetails( - "Vikunja2", - "Reminder Notifications", + "Vikunja2", "Reminder Notifications", channelDescription: "description", icon: 'vikunja_notification_logo', - importance: notifs.Importance.high - ); - late notifs.IOSNotificationDetails iOSSpecifics; + importance: notifs.Importance.high); + late notifs.DarwinNotificationDetails iOSSpecifics; late notifs.NotificationDetails platformChannelSpecificsDueDate; late notifs.NotificationDetails platformChannelSpecificsReminders; NotificationClass({this.id, this.body, this.payload, this.title}); - final rxSub.BehaviorSubject< - NotificationClass> didReceiveLocalNotificationSubject = - rxSub.BehaviorSubject(); + final rxSub.BehaviorSubject + didReceiveLocalNotificationSubject = + rxSub.BehaviorSubject(); final rxSub.BehaviorSubject selectNotificationSubject = - rxSub.BehaviorSubject(); + rxSub.BehaviorSubject(); Future _initNotifications() async { var initializationSettingsAndroid = - notifs.AndroidInitializationSettings('vikunja_logo'); - var initializationSettingsIOS = notifs.IOSInitializationSettings( + notifs.AndroidInitializationSettings('vikunja_logo'); + var initializationSettingsIOS = notifs.DarwinInitializationSettings( requestAlertPermission: false, requestBadgePermission: false, requestSoundPermission: false, onDidReceiveLocalNotification: (int? id, String? title, String? body, String? payload) async { - didReceiveLocalNotificationSubject - .add(NotificationClass( + didReceiveLocalNotificationSubject.add(NotificationClass( id: id, title: title, body: body, payload: payload)); }); var initializationSettings = notifs.InitializationSettings( android: initializationSettingsAndroid, iOS: initializationSettingsIOS); await notificationsPlugin.initialize(initializationSettings, - onSelectNotification: (String? payload) async { - if (payload != null) { - print('notification payload: ' + payload); - selectNotificationSubject.add(payload); - } - }); + onDidReceiveNotificationResponse: + (notifs.NotificationResponse resp) async { + if (payload != null) { + print('notification payload: ' + resp.payload!); + selectNotificationSubject.add(resp.payload!); + } + }); print("Notifications initialised successfully"); } Future notificationInitializer() async { - iOSSpecifics = notifs.IOSNotificationDetails(); + iOSSpecifics = notifs.DarwinNotificationDetails(); platformChannelSpecificsDueDate = notifs.NotificationDetails( android: androidSpecificsDueDate, iOS: iOSSpecifics); platformChannelSpecificsReminders = notifs.NotificationDetails( @@ -86,20 +83,23 @@ class NotificationClass { return Future.value(); } - Future scheduleNotification(String title, String description, + Future scheduleNotification( + String title, + String description, notifs.FlutterLocalNotificationsPlugin notifsPlugin, - DateTime scheduledTime, String currentTimeZone, - notifs.NotificationDetails platformChannelSpecifics, {int? id}) async { - if (id == null) - id = Random().nextInt(1000000); + DateTime scheduledTime, + String currentTimeZone, + notifs.NotificationDetails platformChannelSpecifics, + {int? id}) async { + if (id == null) id = Random().nextInt(1000000); // TODO: move to setup - tz.TZDateTime time = tz.TZDateTime.from( - scheduledTime, tz.getLocation(currentTimeZone)); + tz.TZDateTime time = + tz.TZDateTime.from(scheduledTime, tz.getLocation(currentTimeZone)); if (time.difference(tz.TZDateTime.now(tz.getLocation(currentTimeZone))) < - Duration.zero) - return; - await notifsPlugin.zonedSchedule(id, title, description, - time, platformChannelSpecifics, androidAllowWhileIdle: true, + Duration.zero) return; + await notifsPlugin.zonedSchedule( + id, title, description, time, platformChannelSpecifics, + androidAllowWhileIdle: true, uiLocalNotificationDateInterpretation: notifs .UILocalNotificationDateInterpretation .wallClockTime); // This literally schedules the notification @@ -110,30 +110,27 @@ class NotificationClass { "This is a test notification", platformChannelSpecificsReminders); } - void requestIOSPermissions() { - notificationsPlugin.resolvePlatformSpecificImplementation< - notifs.IOSFlutterLocalNotificationsPlugin>() + notificationsPlugin + .resolvePlatformSpecificImplementation< + notifs.IOSFlutterLocalNotificationsPlugin>() ?.requestPermissions( - alert: true, - badge: true, - sound: true, - ); + alert: true, + badge: true, + sound: true, + ); } - Future scheduleDueNotifications(TaskService taskService, {List? tasks}) async { - if (tasks == null) - tasks = await taskService.getAll(); + if (tasks == null) tasks = await taskService.getAll(); if (tasks == null) { print("did not receive tasks on notification update"); return; } await notificationsPlugin.cancelAll(); for (final task in tasks) { - if(task.done) - continue; + if (task.done) continue; for (final reminder in task.reminderDates) { scheduleNotification( "Reminder", @@ -159,5 +156,4 @@ class NotificationClass { } print("notifications scheduled successfully"); } - -} \ No newline at end of file +} diff --git a/lib/pages/landing_page.dart b/lib/pages/landing_page.dart index a627f39..72471fd 100644 --- a/lib/pages/landing_page.dart +++ b/lib/pages/landing_page.dart @@ -37,11 +37,11 @@ class LandingPageState extends State static const platform = const MethodChannel('vikunja'); Future _updateDefaultList() async { - - return VikunjaGlobal.of(context).newUserService?.getCurrentUser().then((value) => - setState(() { - defaultList = value?.settings?.default_project_id; - } ),); + return VikunjaGlobal.of(context).newUserService?.getCurrentUser().then( + (value) => setState(() { + defaultList = value?.settings?.default_project_id; + }), + ); } @override @@ -55,7 +55,10 @@ class LandingPageState extends State } catch (e) { log(e.toString()); } - VikunjaGlobal.of(context).settingsManager.getLandingPageOnlyDueDateTasks().then((value) => onlyDueDate = value); + VikunjaGlobal.of(context) + .settingsManager + .getLandingPageOnlyDueDateTasks() + .then((value) => onlyDueDate = value); })); super.initState(); } @@ -133,26 +136,29 @@ class LandingPageState extends State PopupMenuButton(itemBuilder: (BuildContext context) { return [ PopupMenuItem( - child: - InkWell( - onTap: () { - Navigator.pop(context); - bool newval = !onlyDueDate; - VikunjaGlobal.of(context).settingsManager.setLandingPageOnlyDueDateTasks(newval).then((value) { - setState(() { - onlyDueDate = newval; - _loadList(context); - }); - }); - }, - child: - Row(mainAxisAlignment: MainAxisAlignment.end, children: [ - Text("Only show tasks with due date"), - Checkbox( - value: onlyDueDate, - onChanged: (bool? value) { }, - ) - ]))) + child: InkWell( + onTap: () { + Navigator.pop(context); + bool newval = !onlyDueDate; + VikunjaGlobal.of(context) + .settingsManager + .setLandingPageOnlyDueDateTasks(newval) + .then((value) { + setState(() { + onlyDueDate = newval; + _loadList(context); + }); + }); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text("Only show tasks with due date"), + Checkbox( + value: onlyDueDate, + onChanged: (bool? value) {}, + ) + ]))) ]; }), ], @@ -226,41 +232,46 @@ class LandingPageState extends State .settingsManager .getLandingPageOnlyDueDateTasks() .then((showOnlyDueDateTasks) { + VikunjaGlobalState global = VikunjaGlobal.of(context); + Map? frontend_settings = + global.currentUser?.settings?.frontend_settings; + int? filterId = 0; + if (frontend_settings != null) { + if (frontend_settings["filter_id_used_on_overview"] != null) + filterId = frontend_settings["filter_id_used_on_overview"]; + } + if (filterId != null && filterId != 0) { + return global.taskService.getAllByProject(filterId, { + "sort_by": ["due_date", "id"], + "order_by": ["asc", "desc"], + }).then?>((response) => + _handleTaskList(response?.body, showOnlyDueDateTasks)); + } - VikunjaGlobalState global = VikunjaGlobal.of(context); - Map? frontend_settings = global.currentUser?.settings?.frontend_settings; - int? filterId = 0; - if(frontend_settings != null) { - if(frontend_settings["filter_id_used_on_overview"] != null) - filterId = frontend_settings["filter_id_used_on_overview"]; - } - if(filterId != null && filterId != 0) { - return global.taskService.getAllByProject(filterId, { - "sort_by": ["due_date", "id"], - "order_by": ["asc", "desc"], - }).then?>((response) => _handleTaskList(response?.body, showOnlyDueDateTasks));; - } - - return global.taskService - .getByOptions(TaskServiceOptions( - newOptions: [ - TaskServiceOption("sort_by", ["due_date", "id"]), - TaskServiceOption("order_by", ["asc", "desc"]), - TaskServiceOption("filter_by", "done"), - TaskServiceOption("filter_value", "false"), - TaskServiceOption("filter_comparator", "equals"), - TaskServiceOption("filter_concat", "and"), - ], - clearOther: true - )) - .then?>((taskList) => _handleTaskList(taskList, showOnlyDueDateTasks)); - - });//.onError((error, stackTrace) {print("error");}); + return global.taskService + .getByOptions(TaskServiceOptions(newOptions: [ + TaskServiceOption( + "sort_by", ["due_date", "id"]), + TaskServiceOption( + "order_by", ["asc", "desc"]), + TaskServiceOption("filter_by", "done"), + TaskServiceOption( + "filter_value", "false"), + TaskServiceOption( + "filter_comparator", "equals"), + TaskServiceOption( + "filter_concat", "and"), + ], clearOther: true)) + .then?>( + (taskList) => _handleTaskList(taskList, showOnlyDueDateTasks)); + }); //.onError((error, stackTrace) {print("error");}); } - Future _handleTaskList(List? taskList, bool showOnlyDueDateTasks) { - if(showOnlyDueDateTasks) - taskList?.removeWhere((element) => element.dueDate == null || element.dueDate!.year == 0001); + Future _handleTaskList( + List? taskList, bool showOnlyDueDateTasks) { + if (showOnlyDueDateTasks) + taskList?.removeWhere((element) => + element.dueDate == null || element.dueDate!.year == 0001); if (taskList != null && taskList.isEmpty) { setState(() { diff --git a/lib/pages/list/task_edit.dart b/lib/pages/list/task_edit.dart index 0ccfb8a..1dfdd5e 100644 --- a/lib/pages/list/task_edit.dart +++ b/lib/pages/list/task_edit.dart @@ -48,8 +48,8 @@ class _TaskEditPageState extends State { @override void initState() { _repeatAfter = widget.task.repeatAfter; - if(_repeatAfterType == null) - _repeatAfterType = getRepeatAfterTypeFromDuration(_repeatAfter); + if (_repeatAfterType == null) + _repeatAfterType = getRepeatAfterTypeFromDuration(_repeatAfter); _reminderDates = widget.task.reminderDates; for (var i = 0; i < _reminderDates.length; i++) { @@ -86,25 +86,30 @@ class _TaskEditPageState extends State { actions: [ IconButton( icon: Icon(Icons.delete), - onPressed: () {showDialog(context: context, builder: (BuildContext context) { - return AlertDialog( - title: Text('Delete Task'), - content: Text('Are you sure you want to delete this task?'), - actions: [ - TextButton( - child: Text('Cancel'), - onPressed: () => Navigator.of(context).pop(), - ), - TextButton( - child: Text('Delete'), - onPressed: () { - _delete(widget.task.id); - Navigator.of(context).pop(); - }, - ), - ], - ); - });}, + onPressed: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text('Delete Task'), + content: Text( + 'Are you sure you want to delete this task?'), + actions: [ + TextButton( + child: Text('Cancel'), + onPressed: () => Navigator.of(context).pop(), + ), + TextButton( + child: Text('Delete'), + onPressed: () { + _delete(widget.task.id); + Navigator.of(context).pop(); + }, + ), + ], + ); + }); + }, ), ], ), @@ -114,7 +119,8 @@ class _TaskEditPageState extends State { key: _formKey, child: ListView( key: _listKey, - padding: EdgeInsets.fromLTRB(16, 16, 16, MediaQuery.of(context).size.height / 2), + padding: EdgeInsets.fromLTRB( + 16, 16, 16, MediaQuery.of(context).size.height / 2), children: [ Padding( padding: EdgeInsets.symmetric(vertical: 10.0), @@ -182,8 +188,9 @@ class _TaskEditPageState extends State { flex: 2, child: TextFormField( keyboardType: TextInputType.number, - initialValue: getRepeatAfterValueFromDuration( - _repeatAfter)?.toString(), + initialValue: + getRepeatAfterValueFromDuration(_repeatAfter) + ?.toString(), onSaved: (repeatAfter) => _repeatAfter = getDurationFromType( repeatAfter, _repeatAfterType), @@ -200,8 +207,7 @@ class _TaskEditPageState extends State { isExpanded: true, isDense: true, value: _repeatAfterType ?? - getRepeatAfterTypeFromDuration( - _repeatAfter), + getRepeatAfterTypeFromDuration(_repeatAfter), onChanged: (String? newValue) { setState(() { _repeatAfterType = newValue; @@ -321,9 +327,11 @@ class _TaskEditPageState extends State { Padding( padding: EdgeInsets.only( right: 15, - left: 2.0 + (IconTheme.of(context).size??0))), + left: 2.0 + (IconTheme.of(context).size ?? 0))), Container( - width: MediaQuery.of(context).size.width - 80 - ((IconTheme.of(context).size ?? 0) * 2), + width: MediaQuery.of(context).size.width - + 80 - + ((IconTheme.of(context).size ?? 0) * 2), child: TypeAheadField( builder: (builder, controller, focusnode) { return TextFormField( @@ -427,18 +435,25 @@ class _TaskEditPageState extends State { trailing: IconButton( icon: Icon(Icons.download), onPressed: () async { - String url = VikunjaGlobal.of(context).client.base; - url += '/tasks/${widget.task.id}/attachments/${widget.task.attachments[index].id}'; + String url = + VikunjaGlobal.of(context).client.base; + url += + '/tasks/${widget.task.id}/attachments/${widget.task.attachments[index].id}'; print(url); final taskId = await FlutterDownloader.enqueue( url: url, - fileName: widget.task.attachments[index].file.name, - headers: VikunjaGlobal.of(context).client.headers, // optional: header send with url (auth token etc) + fileName: + widget.task.attachments[index].file.name, + headers: VikunjaGlobal.of(context) + .client + .headers, // optional: header send with url (auth token etc) savedDir: '/storage/emulated/0/Download/', - showNotification: true, // show download progress in status bar (for Android) - openFileFromNotification: true, // click on notification to open downloaded file (for Android) + showNotification: + true, // show download progress in status bar (for Android) + openFileFromNotification: + true, // click on notification to open downloaded file (for Android) ); - if(taskId == null) return; + if (taskId == null) return; FlutterDownloader.open(taskId: taskId); }, ), @@ -594,8 +609,6 @@ class _TaskEditPageState extends State { }); } - - _onColorEdit() { _pickerColor = _resetColor || (_color ?? widget.task.color) == null ? Colors.black diff --git a/lib/pages/project/overview.dart b/lib/pages/project/overview.dart index 4ed35e4..c36468a 100644 --- a/lib/pages/project/overview.dart +++ b/lib/pages/project/overview.dart @@ -1,4 +1,3 @@ - import 'package:after_layout/after_layout.dart'; import 'package:flutter/material.dart'; import 'package:vikunja_app/pages/project/project_task_list.dart'; @@ -16,14 +15,9 @@ class ProjectOverviewPage extends StatefulWidget { class _ProjectOverviewPageState extends State with AfterLayoutMixin { List _projects = []; - int _selectedDrawerIndex = -2, _previousDrawerIndex = -2; + int _selectedDrawerIndex = -2; bool _loading = true; - Project? get _currentProject => - _selectedDrawerIndex >= -1 && _selectedDrawerIndex < _projects.length - ? _projects[_selectedDrawerIndex] - : null; - @override void afterFirstLayout(BuildContext context) { _loadProjects(); @@ -37,9 +31,9 @@ class _ProjectOverviewPageState extends State bool expanded = expandedList.contains(project.id); Widget icon; - List? children = addProjectChildren(project, level+1); + List? children = addProjectChildren(project, level + 1); bool no_children = children.length == 0; - if(no_children) { + if (no_children) { icon = Icon(Icons.list); } else { if (expanded) { @@ -50,34 +44,34 @@ class _ProjectOverviewPageState extends State } } - - return Column(children: [ - ListTile( - onTap: () { - setState(() { - openList(context, project); - }); - }, - contentPadding: insets, + return Column(children: [ + ListTile( + onTap: () { + setState(() { + openList(context, project); + }); + }, + contentPadding: insets, leading: IconButton( disabledColor: Theme.of(context).unselectedWidgetColor, icon: icon, - onPressed: !no_children ? () { - setState(() { - if (expanded) - expandedList.remove(project.id); - else - expandedList.add(project.id); - }); - } : null, + onPressed: !no_children + ? () { + setState(() { + if (expanded) + expandedList.remove(project.id); + else + expandedList.add(project.id); + }); + } + : null, ), title: new Text(project.title), //onTap: () => _onSelectItem(i), ), - ...?children - ]); - } - + ...?children + ]); + } List addProjectChildren(Project project, level) { Iterable children = @@ -120,7 +114,6 @@ class _ProjectOverviewPageState extends State .toList()), onRefresh: _loadProjects, ), - appBar: AppBar( title: Text("Projects"), ), @@ -136,8 +129,6 @@ class _ProjectOverviewPageState extends State }); } - - _addProjectDialog(BuildContext context) { showDialog( context: context, diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index aa5b685..12d0e28 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -8,7 +8,6 @@ import '../models/project.dart'; import '../models/user.dart'; import '../service/services.dart'; - class SettingsPage extends StatefulWidget { @override State createState() => SettingsPageState(); @@ -25,7 +24,6 @@ class SettingsPageState extends State { FlutterThemeMode? themeMode; User? currentUser; - void init() { durationTextController = TextEditingController(); @@ -51,20 +49,21 @@ class SettingsPageState extends State { .getCurrentVersionTag() .then((value) => setState(() => versionTag = value)); + VikunjaGlobal.of(context).settingsManager.getWorkmanagerDuration().then( + (value) => setState( + () => durationTextController.text = (value.inMinutes.toString()))); + VikunjaGlobal.of(context) .settingsManager - .getWorkmanagerDuration() - .then((value) => setState(() => durationTextController.text = (value.inMinutes.toString()))); - - VikunjaGlobal.of(context).settingsManager.getThemeMode().then((value) => setState(() => themeMode = value)); + .getThemeMode() + .then((value) => setState(() => themeMode = value)); VikunjaGlobal.of(context).newUserService?.getCurrentUser().then((value) => { - setState(() { - currentUser = value!; - defaultProject = value.settings?.default_project_id; - } ), - }); - + setState(() { + currentUser = value!; + defaultProject = value.settings?.default_project_id; + }), + }); initialized = true; } @@ -75,18 +74,22 @@ class SettingsPageState extends State { if (!initialized) init(); return new Scaffold( - appBar: AppBar(title: Text("Settings"),), - + appBar: AppBar( + title: Text("Settings"), + ), body: ListView( children: [ UserAccountsDrawerHeader( - accountName: currentUser != null ? Text(currentUser!.username) : null, + accountName: + currentUser != null ? Text(currentUser!.username) : null, accountEmail: currentUser != null ? Text(currentUser!.name) : null, currentAccountPicture: currentUser == null ? null : CircleAvatar( - backgroundImage: (currentUser?.username != "") ? NetworkImage(currentUser!.avatarUrl(context)) : null, - ), + backgroundImage: (currentUser?.username != "") + ? NetworkImage(currentUser!.avatarUrl(context)) + : null, + ), decoration: BoxDecoration( image: DecorationImage( image: AssetImage("assets/graphics/hypnotize.png"), @@ -109,11 +112,17 @@ class SettingsPageState extends State { child: Text(e.title), value: e.id)) .toList() ], - value: projectList?.firstWhereOrNull((element) => element.id == defaultProject) != null ? defaultProject : null, + value: projectList?.firstWhereOrNull( + (element) => element.id == defaultProject) != + null + ? defaultProject + : null, onChanged: (int? value) { setState(() => defaultProject = value); - global.newUserService?.setCurrentUserSettings( - currentUser!.settings!.copyWith(default_project_id: value)).then((value) => currentUser!.settings = value); + global.newUserService + ?.setCurrentUserSettings(currentUser!.settings! + .copyWith(default_project_id: value)) + .then((value) => currentUser!.settings = value); //VikunjaGlobal.of(context).userManager.setDefaultList(value); }, ), @@ -149,9 +158,7 @@ class SettingsPageState extends State { ], value: themeMode, onChanged: (FlutterThemeMode? value) { - VikunjaGlobal.of(context) - .settingsManager - .setThemeMode(value!); + VikunjaGlobal.of(context).settingsManager.setThemeMode(value!); setState(() => themeMode = value); updateTheme.value = true; }, @@ -164,27 +171,30 @@ class SettingsPageState extends State { value: ignoreCertificates, onChanged: (value) { setState(() => ignoreCertificates = value); - VikunjaGlobal.of(context).client.reload_ignore_certs(value); + VikunjaGlobal.of(context).client.reloadIgnoreCerts(value); }) : ListTile(title: Text("...")), - Divider(), - Padding(padding: EdgeInsets.only(left: 15, right: 15), + Divider(), + Padding( + padding: EdgeInsets.only(left: 15, right: 15), child: Row(children: [ - Flexible( - child: TextField( - controller: durationTextController, - decoration: InputDecoration( - labelText: 'Background Refresh Interval (minutes): ', - helperText: 'Minimum: 15, Set limit of 0 for no refresh', - ), - )), - TextButton( - onPressed: () => VikunjaGlobal.of(context) - .settingsManager - .setWorkmanagerDuration(Duration( - minutes: int.parse(durationTextController.text))).then((value) => VikunjaGlobal.of(context).updateWorkmanagerDuration()), - child: Text("Save")), - ])), + Flexible( + child: TextField( + controller: durationTextController, + decoration: InputDecoration( + labelText: 'Background Refresh Interval (minutes): ', + helperText: 'Minimum: 15, Set limit of 0 for no refresh', + ), + )), + TextButton( + onPressed: () => VikunjaGlobal.of(context) + .settingsManager + .setWorkmanagerDuration(Duration( + minutes: int.parse(durationTextController.text))) + .then((value) => VikunjaGlobal.of(context) + .updateWorkmanagerDuration()), + child: Text("Save")), + ])), Divider(), getVersionNotifications != null ? CheckboxListTile( diff --git a/lib/pages/user/login.dart b/lib/pages/user/login.dart index 4ad08ed..5d6a6b1 100644 --- a/lib/pages/user/login.dart +++ b/lib/pages/user/login.dart @@ -33,11 +33,10 @@ class _LoginPageState extends State { final _serverSuggestionController = SuggestionsController(); - @override void initState() { super.initState(); - Future.delayed(Duration.zero, () async{ + Future.delayed(Duration.zero, () async { if (VikunjaGlobal.of(context).expired) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text("Login has expired. Please reenter your details!"))); @@ -48,10 +47,16 @@ class _LoginPageState extends State { }); } final client = VikunjaGlobal.of(context).client; - await VikunjaGlobal.of(context).settingsManager.getIgnoreCertificates().then( - (value) => setState(() => client.ignoreCertificates = value == "1")); + await VikunjaGlobal.of(context) + .settingsManager + .getIgnoreCertificates() + .then((value) => + setState(() => client.ignoreCertificates = value == "1")); - await VikunjaGlobal.of(context).settingsManager.getPastServers().then((value) { + await VikunjaGlobal.of(context) + .settingsManager + .getPastServers() + .then((value) { print(value); if (value != null) setState(() => pastServers = value); }); @@ -101,11 +106,11 @@ class _LoginPageState extends State { enabled: !_loading, validator: (address) { return (isUrl(address) || - address != null || - address!.isEmpty) - ? null - : 'Invalid URL'; - }, + address != null || + address!.isEmpty) + ? null + : 'Invalid URL'; + }, decoration: new InputDecoration( border: OutlineInputBorder(), labelText: 'Server Address'), @@ -120,34 +125,42 @@ class _LoginPageState extends State { ),*/ onSelected: (suggestion) { _serverController.text = suggestion; - setState(() => _serverController.text = suggestion); + setState( + () => _serverController.text = suggestion); }, - itemBuilder: (BuildContext context, Object? itemData) { + itemBuilder: + (BuildContext context, Object? itemData) { return Card( child: Container( - padding: EdgeInsets.all(10), - child: - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(itemData.toString()), - IconButton(onPressed: () { - setState(() { - pastServers.remove(itemData.toString()); - //_serverSuggestionController.suggestionsBox?.close(); - VikunjaGlobal.of(context).settingsManager.setPastServers(pastServers); - - }); - }, icon: Icon(Icons.clear)) - ], - )) - ); + padding: EdgeInsets.all(10), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text(itemData.toString()), + IconButton( + onPressed: () { + setState(() { + pastServers.remove( + itemData.toString()); + //_serverSuggestionController.suggestionsBox?.close(); + VikunjaGlobal.of(context) + .settingsManager + .setPastServers( + pastServers); + }); + }, + icon: Icon(Icons.clear)) + ], + ))); }, suggestionsCallback: (String pattern) { List matches = []; matches.addAll(pastServers); - matches.retainWhere((s){ - return s.toLowerCase().contains(pattern.toLowerCase()); + matches.retainWhere((s) { + return s + .toLowerCase() + .contains(pattern.toLowerCase()); }); return matches; }, @@ -246,20 +259,18 @@ class _LoginPageState extends State { } }, child: VikunjaButtonText("Login with Frontend"))), - CheckboxListTile( - title: Text("Ignore Certificates"), - value: client.ignoreCertificates, - onChanged: (value) { - setState(() => - client.reload_ignore_certs(value ?? false)); - VikunjaGlobal.of(context) - .settingsManager - .setIgnoreCertificates(value ?? false); - VikunjaGlobal.of(context) - .client - .ignoreCertificates = value ?? false; - }) - + CheckboxListTile( + title: Text("Ignore Certificates"), + value: client.ignoreCertificates, + onChanged: (value) { + setState( + () => client.reloadIgnoreCerts(value ?? false)); + VikunjaGlobal.of(context) + .settingsManager + .setIgnoreCertificates(value ?? false); + VikunjaGlobal.of(context).client.ignoreCertificates = + value ?? false; + }) ], ), ), @@ -276,7 +287,7 @@ class _LoginPageState extends State { String _password = _passwordController.text; if (_server.isEmpty) return; - if(!pastServers.contains(_server)) pastServers.add(_server); + if (!pastServers.contains(_server)) pastServers.add(_server); await VikunjaGlobal.of(context).settingsManager.setPastServers(pastServers); setState(() => _loading = true); @@ -285,8 +296,7 @@ class _LoginPageState extends State { vGlobal.client.showSnackBar = false; vGlobal.client.configure(base: _server); Server? info = await vGlobal.serverService.getInfo(); - if (info == null) - throw Exception("Getting server info failed"); + if (info == null) throw Exception("Getting server info failed"); UserTokenPair newUser; @@ -297,27 +307,27 @@ class _LoginPageState extends State { TextEditingController totpController = TextEditingController(); bool dismissed = true; await showDialog( - context: context, - builder: (context) => new AlertDialog( - title: Text("Enter One Time Passcode"), - content: TextField( - controller: totpController, - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly - ], - ), - actions: [ - TextButton( - onPressed: () { - dismissed = false; - Navigator.pop(context); - }, - child: Text("Login")) - ], - ), + context: context, + builder: (context) => new AlertDialog( + title: Text("Enter One Time Passcode"), + content: TextField( + controller: totpController, + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly + ], + ), + actions: [ + TextButton( + onPressed: () { + dismissed = false; + Navigator.pop(context); + }, + child: Text("Login")) + ], + ), ); - if(!dismissed) { + if (!dismissed) { newUser = await vGlobal.newUserService!.login(_username, _password, rememberMe: this._rememberMe, totp: totpController.text); } else { diff --git a/pubspec.lock b/pubspec.lock index cf06862..e851c5f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -122,21 +122,21 @@ packages: source: hosted version: "2.0.3" chewie: - dependency: "direct main" + dependency: transitive description: name: chewie - sha256: "745e81e84c6d7f3835f89f85bb49771c0a66099e4caf8f8e9e9a372bc66fb2c1" + sha256: "8bc4ac4cf3f316e50a25958c0f5eb9bb12cf7e8308bb1d74a43b230da2cfc144" url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "1.7.5" cli_util: dependency: transitive description: name: cli_util - sha256: "66f86e916d285c1a93d3b79587d94bd71984a66aac4ff74e524cfa7877f1395c" + sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 url: "https://pub.dev" source: hosted - version: "0.3.5" + version: "0.4.1" clock: dependency: transitive description: @@ -253,10 +253,10 @@ packages: dependency: transitive description: name: file - sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" url: "https://pub.dev" source: hosted - version: "6.1.4" + version: "7.0.0" fixnum: dependency: transitive description: @@ -298,10 +298,10 @@ packages: dependency: "direct main" description: name: flutter_keyboard_visibility - sha256: "183ef857869a790aaff0219327a31783017fcc54b895fcdda1909645bb6ab965" + sha256: "98664be7be0e3ffca00de50f7f6a287ab62c763fc8c762e0a21584584a3ff4f8" url: "https://pub.dev" source: hosted - version: "5.4.2" + version: "6.0.0" flutter_keyboard_visibility_linux: dependency: transitive description: @@ -346,42 +346,42 @@ packages: dependency: "direct dev" description: name: flutter_launcher_icons - sha256: a9de6706cd844668beac27c0aed5910fa0534832b3c2cad61a5fd977fce82a5d + sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea" url: "https://pub.dev" source: hosted - version: "0.10.0" + version: "0.13.1" flutter_local_notifications: dependency: "direct main" description: name: flutter_local_notifications - sha256: "57d0012730780fe137260dd180e072c18a73fbeeb924cdc029c18aaa0f338d64" + sha256: f9a05409385b77b06c18f200a41c7c2711ebf7415669350bb0f8474c07bd40d1 url: "https://pub.dev" source: hosted - version: "9.9.1" + version: "17.0.0" flutter_local_notifications_linux: dependency: transitive description: name: flutter_local_notifications_linux - sha256: b472bfc173791b59ede323661eae20f7fff0b6908fea33dd720a6ef5d576bae8 + sha256: "33f741ef47b5f63cc7f78fe75eeeac7e19f171ff3c3df054d84c1e38bedb6a03" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "4.0.0+1" flutter_local_notifications_platform_interface: dependency: transitive description: name: flutter_local_notifications_platform_interface - sha256: "21bceee103a66a53b30ea9daf677f990e5b9e89b62f222e60dd241cd08d63d3a" + sha256: "7cf643d6d5022f3baed0be777b0662cce5919c0a7b86e700299f22dc4ae660ef" url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "7.0.0+1" flutter_secure_storage: dependency: "direct main" description: name: flutter_secure_storage - sha256: "22dbf16f23a4bcf9d35e51be1c84ad5bb6f627750565edd70dab70f3ff5fff8f" + sha256: ffdbb60130e4665d2af814a0267c481bcf522c41ae2e43caf69fa0146876d685 url: "https://pub.dev" source: hosted - version: "8.1.0" + version: "9.0.0" flutter_secure_storage_linux: dependency: transitive description: @@ -418,18 +418,18 @@ packages: dependency: transitive description: name: flutter_secure_storage_windows - sha256: "38f9501c7cb6f38961ef0e1eacacee2b2d4715c63cc83fe56449c4d3d0b47255" + sha256: "5809c66f9dd3b4b93b0a6e2e8561539405322ee767ac2f64d084e2ab5429d108" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" flutter_svg: dependency: transitive description: name: flutter_svg - sha256: d39e7f95621fc84376bc0f7d504f05c3a41488c562f4a8ad410569127507402c + sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2" url: "https://pub.dev" source: hosted - version: "2.0.9" + version: "2.0.10+1" flutter_test: dependency: "direct dev" description: flutter @@ -447,10 +447,10 @@ packages: dependency: "direct main" description: name: flutter_typeahead - sha256: "1f6b248bb4f3ebb4cf1ee0354aa23c77be457fb2d26d6847ecc33a917f65e58e" + sha256: d64712c65db240b1057559b952398ebb6e498077baeebf9b0731dade62438a6d url: "https://pub.dev" source: hosted - version: "5.0.1" + version: "5.2.0" flutter_web_plugins: dependency: transitive description: flutter @@ -548,10 +548,10 @@ packages: dependency: "direct main" description: name: http - sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" url: "https://pub.dev" source: hosted - version: "0.13.6" + version: "1.2.1" http_multi_server: dependency: transitive description: @@ -572,18 +572,18 @@ packages: dependency: transitive description: name: image - sha256: "8e9d133755c3e84c73288363e6343157c383a0c6c56fc51afcc5d4d7180306d6" + sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e" url: "https://pub.dev" source: hosted - version: "3.3.0" + version: "4.1.7" intl: dependency: "direct main" description: name: intl - sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.17.0" + version: "0.19.0" io: dependency: transitive description: @@ -737,13 +737,13 @@ packages: source: hosted version: "2.1.0" package_info_plus: - dependency: "direct main" + dependency: transitive description: name: package_info_plus - sha256: "10259b111176fba5c505b102e3a5b022b51dd97e30522e906d6922c745584745" + sha256: "7e76fad405b3e4016cd39d08f455a4eb5199723cf594cd1b8916d47140d93017" url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "4.2.0" package_info_plus_platform_interface: dependency: transitive description: @@ -828,50 +828,58 @@ packages: dependency: "direct main" description: name: permission_handler - sha256: bc56bfe9d3f44c3c612d8d393bd9b174eb796d706759f9b495ac254e4294baa5 + sha256: "74e962b7fad7ff75959161bb2c0ad8fe7f2568ee82621c9c2660b751146bfe44" url: "https://pub.dev" source: hosted - version: "10.4.5" + version: "11.3.0" permission_handler_android: dependency: transitive description: name: permission_handler_android - sha256: "59c6322171c29df93a22d150ad95f3aa19ed86542eaec409ab2691b8f35f9a47" + sha256: "1acac6bae58144b442f11e66621c062aead9c99841093c38f5bcdcc24c1c3474" url: "https://pub.dev" source: hosted - version: "10.3.6" + version: "12.0.5" permission_handler_apple: dependency: transitive description: name: permission_handler_apple - sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5" + sha256: e9ad66020b89ff1b63908f247c2c6f931c6e62699b756ef8b3c4569350cd8662 url: "https://pub.dev" source: hosted - version: "9.1.4" + version: "9.4.4" + permission_handler_html: + dependency: transitive + description: + name: permission_handler_html + sha256: "54bf176b90f6eddd4ece307e2c06cf977fb3973719c35a93b85cc7093eb6070d" + url: "https://pub.dev" + source: hosted + version: "0.1.1" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface - sha256: "6760eb5ef34589224771010805bea6054ad28453906936f843a8cc4d3a55c4a4" + sha256: "23dfba8447c076ab5be3dee9ceb66aad345c4a648f0cac292c77b1eb0e800b78" url: "https://pub.dev" source: hosted - version: "3.12.0" + version: "4.2.0" permission_handler_windows: dependency: transitive description: name: permission_handler_windows - sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098 + sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" url: "https://pub.dev" source: hosted - version: "0.1.3" + version: "0.2.1" petitparser: dependency: "direct main" description: name: petitparser - sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 url: "https://pub.dev" source: hosted - version: "5.4.0" + version: "6.0.2" platform: dependency: transitive description: @@ -892,10 +900,34 @@ packages: dependency: transitive description: name: pointer_interceptor - sha256: adf7a637f97c077041d36801b43be08559fd4322d2127b3f20bb7be1b9eebc22 + sha256: bd18321519718678d5fa98ad3a3359cbc7a31f018554eab80b73d08a7f0c165a url: "https://pub.dev" source: hosted - version: "0.9.3+7" + version: "0.10.1" + pointer_interceptor_ios: + dependency: transitive + description: + name: pointer_interceptor_ios + sha256: "2e73c39452830adc4695757130676a39412a3b7f3c34e3f752791b5384770877" + url: "https://pub.dev" + source: hosted + version: "0.10.0+2" + pointer_interceptor_platform_interface: + dependency: transitive + description: + name: pointer_interceptor_platform_interface + sha256: "0597b0560e14354baeb23f8375cd612e8bd4841bf8306ecb71fcd0bb78552506" + url: "https://pub.dev" + source: hosted + version: "0.10.0+1" + pointer_interceptor_web: + dependency: transitive + description: + name: pointer_interceptor_web + sha256: a6237528b46c411d8d55cdfad8fcb3269fc4cbb26060b14bff94879165887d1e + url: "https://pub.dev" + source: hosted + version: "0.10.2" pointycastle: dependency: transitive description: @@ -912,14 +944,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" - process: - dependency: transitive - description: - name: process - sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" - url: "https://pub.dev" - source: hosted - version: "4.2.4" provider: dependency: "direct main" description: @@ -1121,10 +1145,10 @@ packages: dependency: "direct main" description: name: timezone - sha256: "57b35f6e8ef731f18529695bffc62f92c6189fac2e52c12d478dec1931afb66e" + sha256: "1cfd8ddc2d1cfd836bc93e67b9be88c3adaeca6f40a00ca999104c30693cdca0" url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.9.2" typed_data: dependency: transitive description: @@ -1209,26 +1233,26 @@ packages: dependency: transitive description: name: vector_graphics - sha256: "4ac59808bbfca6da38c99f415ff2d3a5d7ca0a6b4809c71d9cf30fba5daf9752" + sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3" url: "https://pub.dev" source: hosted - version: "1.1.10+1" + version: "1.1.11+1" vector_graphics_codec: dependency: transitive description: name: vector_graphics_codec - sha256: f3247e7ab0ec77dc759263e68394990edc608fb2b480b80db8aa86ed09279e33 + sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da url: "https://pub.dev" source: hosted - version: "1.1.10+1" + version: "1.1.11+1" vector_graphics_compiler: dependency: transitive description: name: vector_graphics_compiler - sha256: "18489bdd8850de3dd7ca8a34e0c446f719ec63e2bab2e7a8cc66a9028dd76c5a" + sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81" url: "https://pub.dev" source: hosted - version: "1.1.10+1" + version: "1.1.11+1" vector_math: dependency: transitive description: @@ -1285,46 +1309,22 @@ packages: url: "https://pub.dev" source: hosted version: "13.0.0" - wakelock: + wakelock_plus: dependency: transitive description: - name: wakelock - sha256: "769ecf42eb2d07128407b50cb93d7c10bd2ee48f0276ef0119db1d25cc2f87db" + name: wakelock_plus + sha256: f268ca2116db22e57577fb99d52515a24bdc1d570f12ac18bb762361d43b043d url: "https://pub.dev" source: hosted - version: "0.6.2" - wakelock_macos: + version: "1.1.4" + wakelock_plus_platform_interface: dependency: transitive description: - name: wakelock_macos - sha256: "047c6be2f88cb6b76d02553bca5a3a3b95323b15d30867eca53a19a0a319d4cd" + name: wakelock_plus_platform_interface + sha256: "40fabed5da06caff0796dc638e1f07ee395fb18801fbff3255a2372db2d80385" url: "https://pub.dev" source: hosted - version: "0.4.0" - wakelock_platform_interface: - dependency: transitive - description: - name: wakelock_platform_interface - sha256: "1f4aeb81fb592b863da83d2d0f7b8196067451e4df91046c26b54a403f9de621" - url: "https://pub.dev" - source: hosted - version: "0.3.0" - wakelock_web: - dependency: transitive - description: - name: wakelock_web - sha256: "1b256b811ee3f0834888efddfe03da8d18d0819317f20f6193e2922b41a501b5" - url: "https://pub.dev" - source: hosted - version: "0.4.0" - wakelock_windows: - dependency: transitive - description: - name: wakelock_windows - sha256: "857f77b3fe6ae82dd045455baa626bc4b93cb9bb6c86bf3f27c182167c3a5567" - url: "https://pub.dev" - source: hosted - version: "0.2.1" + version: "1.1.0" watcher: dependency: transitive description: @@ -1393,10 +1393,10 @@ packages: dependency: transitive description: name: win32 - sha256: a6f0236dbda0f63aa9a25ad1ff9a9d8a4eaaa5012da0dc59d21afdb1dc361ca4 + sha256: "8cb58b45c47dcb42ab3651533626161d6b67a2921917d8d429791f76972b3480" url: "https://pub.dev" source: hosted - version: "3.1.4" + version: "5.3.0" workmanager: dependency: "direct main" description: @@ -1409,18 +1409,18 @@ packages: dependency: transitive description: name: xdg_directories - sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86 + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d url: "https://pub.dev" source: hosted - version: "0.2.0+3" + version: "1.0.4" xml: dependency: transitive description: name: xml - sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "6.5.0" yaml: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 8d215ff..0f65ec8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,44 +9,41 @@ environment: dependencies: flutter: sdk: flutter - cupertino_icons: ^1.0.5 - http: ^0.13.5 + cupertino_icons: ^1.0.6 + http: ^1.2.1 after_layout: ^1.2.0 - intl: ^0.17.0 - flutter_local_notifications: ^9.8.0+1 - rxdart: ^0.27.5 - flutter_timezone: ^1.0.4 - flutter_secure_storage: ^8.0.0 + intl: ^0.19.0 + flutter_local_notifications: ^17.0.0 + rxdart: ^0.27.7 + flutter_timezone: ^1.0.8 + flutter_secure_storage: ^9.0.0 datetime_picker_formfield: ^2.0.1 - flutter_typeahead: ^5.0.1 - build: ^2.3.0 - json_serializable: ^6.3.1 - petitparser: ^5.0.0 - provider: ^6.0.3 - webview_flutter: ^4.4.3 + flutter_typeahead: ^5.2.0 + build: ^2.4.1 + json_serializable: ^6.7.1 + petitparser: ^6.0.2 + provider: ^6.1.2 + webview_flutter: ^4.7.0 flutter_colorpicker: ^1.0.3 - flutter_keyboard_visibility: ^5.4.2 - dotted_border: ^2.0.0+2 - package_info_plus: ^3.0.2 - url_launcher: ^6.1.7 - workmanager: ^0.5.1 - permission_handler: ^10.2.0 - dynamic_color: ^1.6.6 - chewie: ^1.5.0 - flutter_widget_from_html: ^0.14.10 + flutter_keyboard_visibility: ^6.0.0 + dotted_border: ^2.1.0 + url_launcher: ^6.2.5 + workmanager: ^0.5.2 + permission_handler: ^11.3.0 + dynamic_color: ^1.7.0 + flutter_widget_from_html: ^0.14.11 flutter_downloader: ^1.11.6 - - meta: any - timezone: any - json_annotation: any - collection: any + meta: ^1.11.0 + timezone: ^0.9.2 + json_annotation: ^4.8.1 + collection: ^1.18.0 dev_dependencies: flutter_test: sdk: flutter version: any - test: ^1.21.1 - flutter_launcher_icons: ^0.10.0 + test: ^1.24.9 + flutter_launcher_icons: ^0.13.1 flutter_icons: image_path: "assets/vikunja_logo.png"