mirror of
https://github.com/go-vikunja/app
synced 2024-06-02 18:49:47 +00:00
Compare commits
3 Commits
c37daa7e8b
...
f1ebc2c516
Author | SHA1 | Date | |
---|---|---|---|
|
f1ebc2c516 | ||
|
5234b730d3 | ||
|
bb06ed4a15 |
|
@ -1,14 +1,12 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
|
||||
import 'package:vikunja_app/components/label.dart';
|
||||
import 'package:vikunja_app/models/task.dart';
|
||||
import 'package:vikunja_app/pages/list/task_edit.dart';
|
||||
import 'package:vikunja_app/stores/project_store.dart';
|
||||
import 'package:vikunja_app/theme/constants.dart';
|
||||
import 'package:vikunja_app/utils/priority.dart';
|
||||
|
||||
import '../models/label.dart';
|
||||
import '../models/task.dart';
|
||||
import '../pages/list/task_edit.dart';
|
||||
import '../stores/project_store.dart';
|
||||
import '../theme/constants.dart';
|
||||
import 'label.dart';
|
||||
|
||||
class TaskBottomSheet extends StatefulWidget {
|
||||
final Task task;
|
||||
final bool showInfo;
|
||||
|
@ -26,129 +24,131 @@ class TaskBottomSheet extends StatefulWidget {
|
|||
this.showInfo = false,
|
||||
this.onMarkedAsDone,
|
||||
}) : super(key: key);
|
||||
/*
|
||||
@override
|
||||
TaskTileState createState() {
|
||||
return new TaskTileState(this.task, this.loading);
|
||||
}
|
||||
|
||||
*/
|
||||
@override
|
||||
TaskBottomSheetState createState() => TaskBottomSheetState(this.task);
|
||||
TaskBottomSheetState createState() => TaskBottomSheetState();
|
||||
}
|
||||
|
||||
class TaskBottomSheetState extends State<TaskBottomSheet> {
|
||||
Task _currentTask;
|
||||
late Task _currentTask;
|
||||
|
||||
TaskBottomSheetState(this._currentTask);
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_currentTask = widget.task;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
ThemeData theme = Theme.of(context);
|
||||
return Container(
|
||||
height: MediaQuery.of(context).size.height * 0.9,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.fromLTRB(20, 10, 10, 20),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
// Title and edit button
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(_currentTask.title,
|
||||
style: theme.textTheme.headlineLarge),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
Navigator.push<Task>(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (buildContext) => TaskEditPage(
|
||||
task: _currentTask,
|
||||
taskState: widget.taskState,
|
||||
),
|
||||
),
|
||||
)
|
||||
.then((task) => setState(() {
|
||||
if (task != null) _currentTask = task;
|
||||
}))
|
||||
.whenComplete(() => widget.onEdit());
|
||||
},
|
||||
icon: Icon(Icons.edit)),
|
||||
],
|
||||
),
|
||||
Wrap(
|
||||
spacing: 10,
|
||||
children: _currentTask.labels.map((Label label) {
|
||||
return LabelComponent(
|
||||
label: label,
|
||||
);
|
||||
}).toList()),
|
||||
final theme = Theme.of(context);
|
||||
return SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
_currentTask.title,
|
||||
style: theme.textTheme.headline6,
|
||||
),
|
||||
IconButton(
|
||||
onPressed: _editTask,
|
||||
icon: Icon(Icons.edit),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: _currentTask.labels.map((label) {
|
||||
return LabelComponent(label: label);
|
||||
}).toList(),
|
||||
),
|
||||
HtmlWidget(
|
||||
_currentTask.description.isNotEmpty
|
||||
? _currentTask.description
|
||||
: 'No description',
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
_buildRowWithIconAndText(
|
||||
Icons.access_time,
|
||||
_currentTask.dueDate != null
|
||||
? vDateFormatShort.format(_currentTask.dueDate!.toLocal())
|
||||
: 'No due date',
|
||||
),
|
||||
_buildRowWithIconAndText(
|
||||
Icons.play_arrow_rounded,
|
||||
_currentTask.startDate != null
|
||||
? vDateFormatShort.format(_currentTask.startDate!.toLocal())
|
||||
: 'No start date',
|
||||
),
|
||||
_buildRowWithIconAndText(
|
||||
Icons.stop_rounded,
|
||||
_currentTask.endDate != null
|
||||
? vDateFormatShort.format(_currentTask.endDate!.toLocal())
|
||||
: 'No end date',
|
||||
),
|
||||
_buildRowWithIconAndText(
|
||||
Icons.priority_high,
|
||||
_currentTask.priority != null
|
||||
? priorityToString(_currentTask.priority)
|
||||
: 'No priority',
|
||||
),
|
||||
_buildRowWithIconAndText(
|
||||
Icons.percent,
|
||||
_currentTask.percent_done != null
|
||||
? '${(_currentTask.percent_done! * 100).toInt()}%'
|
||||
: 'Unset',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// description with html rendering
|
||||
Text("Description", style: theme.textTheme.headlineSmall),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(10, 0, 0, 0),
|
||||
child: HtmlWidget(_currentTask.description.isNotEmpty
|
||||
? _currentTask.description
|
||||
: "No description"),
|
||||
),
|
||||
// Due date
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.access_time),
|
||||
Padding(padding: EdgeInsets.fromLTRB(10, 0, 0, 0)),
|
||||
Text(_currentTask.dueDate != null
|
||||
? vDateFormatShort.format(_currentTask.dueDate!.toLocal())
|
||||
: "No due date"),
|
||||
],
|
||||
),
|
||||
// start date
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.play_arrow_rounded),
|
||||
Padding(padding: EdgeInsets.fromLTRB(10, 0, 0, 0)),
|
||||
Text(_currentTask.startDate != null
|
||||
? vDateFormatShort
|
||||
.format(_currentTask.startDate!.toLocal())
|
||||
: "No start date"),
|
||||
],
|
||||
),
|
||||
// end date
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.stop_rounded),
|
||||
Padding(padding: EdgeInsets.fromLTRB(10, 0, 0, 0)),
|
||||
Text(_currentTask.endDate != null
|
||||
? vDateFormatShort.format(_currentTask.endDate!.toLocal())
|
||||
: "No end date"),
|
||||
],
|
||||
),
|
||||
// priority
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.priority_high),
|
||||
Padding(padding: EdgeInsets.fromLTRB(10, 0, 0, 0)),
|
||||
Text(_currentTask.priority != null
|
||||
? priorityToString(_currentTask.priority)
|
||||
: "No priority"),
|
||||
],
|
||||
),
|
||||
// progress
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.percent),
|
||||
Padding(padding: EdgeInsets.fromLTRB(10, 0, 0, 0)),
|
||||
Text(_currentTask.percent_done != null
|
||||
? (_currentTask.percent_done! * 100).toInt().toString() +
|
||||
"%"
|
||||
: "Unset"),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
));
|
||||
void _editTask() {
|
||||
Navigator.push<Task>(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (buildContext) => TaskEditPage(
|
||||
task: _currentTask,
|
||||
taskState: widget.taskState,
|
||||
),
|
||||
),
|
||||
).then((task) {
|
||||
if (task != null) {
|
||||
setState(() {
|
||||
_currentTask = task;
|
||||
});
|
||||
}
|
||||
widget.onEdit();
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildSectionTitle(String title) {
|
||||
return Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildRowWithIconAndText(IconData icon, String text) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 4),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(icon),
|
||||
SizedBox(width: 8),
|
||||
Text(text),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,160 +1,65 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:vikunja_app/components/TaskBottomSheet.dart';
|
||||
import 'package:vikunja_app/models/project.dart';
|
||||
import 'package:vikunja_app/models/task.dart';
|
||||
import 'package:vikunja_app/utils/misc.dart';
|
||||
import 'package:vikunja_app/pages/list/task_edit.dart';
|
||||
import 'package:vikunja_app/stores/project_store.dart';
|
||||
import 'package:vikunja_app/utils/misc.dart';
|
||||
import 'package:vikunja_app/utils/priority.dart';
|
||||
|
||||
import '../stores/project_store.dart';
|
||||
|
||||
class TaskTile extends StatefulWidget {
|
||||
final Task task;
|
||||
final Function onEdit;
|
||||
final bool showInfo;
|
||||
final bool loading;
|
||||
final ValueSetter<bool>? onMarkedAsDone;
|
||||
final Map<int, Project>? projectsMap;
|
||||
|
||||
const TaskTile({
|
||||
Key? key,
|
||||
required this.task,
|
||||
required this.onEdit,
|
||||
this.projectsMap,
|
||||
this.loading = false,
|
||||
this.showInfo = false,
|
||||
this.onMarkedAsDone,
|
||||
}) : super(key: key);
|
||||
/*
|
||||
@override
|
||||
TaskTileState createState() {
|
||||
return new TaskTileState(this.task, this.loading);
|
||||
}
|
||||
|
||||
*/
|
||||
@override
|
||||
TaskTileState createState() => TaskTileState(this.task);
|
||||
_TaskTileState createState() => _TaskTileState();
|
||||
}
|
||||
|
||||
Widget? _buildTaskSubtitle(Task? task, bool showInfo, BuildContext context) {
|
||||
Duration? durationUntilDue = task?.dueDate?.difference(DateTime.now());
|
||||
class _TaskTileState extends State<TaskTile> {
|
||||
late Task _currentTask;
|
||||
|
||||
if (task == null) return null;
|
||||
|
||||
List<TextSpan> texts = [];
|
||||
|
||||
if (showInfo && task.hasDueDate) {
|
||||
texts.add(TextSpan(
|
||||
text: "Due " + durationToHumanReadable(durationUntilDue!),
|
||||
style: durationUntilDue.isNegative
|
||||
? TextStyle(color: Colors.red)
|
||||
: Theme.of(context).textTheme.bodyMedium));
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_currentTask = widget.task;
|
||||
}
|
||||
if (task.priority != null && task.priority != 0) {
|
||||
texts.add(TextSpan(
|
||||
text: " !" + priorityToString(task.priority),
|
||||
style: TextStyle(color: Colors.orange)));
|
||||
}
|
||||
|
||||
//if(texts.isEmpty && task.description.isNotEmpty) {
|
||||
// return HtmlWidget(task.description);
|
||||
// }
|
||||
|
||||
if (texts.isNotEmpty) {
|
||||
return RichText(text: TextSpan(children: texts));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
class TaskTileState extends State<TaskTile> with AutomaticKeepAliveClientMixin {
|
||||
Task _currentTask;
|
||||
|
||||
TaskTileState(this._currentTask);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
final taskState = Provider.of<ProjectProvider>(context);
|
||||
if (_currentTask.loading) {
|
||||
return ListTile(
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: SizedBox(
|
||||
height: Checkbox.width,
|
||||
width: Checkbox.width,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2.0,
|
||||
)),
|
||||
),
|
||||
title: Text(_currentTask.title),
|
||||
subtitle: _currentTask.description.isEmpty
|
||||
? null
|
||||
: HtmlWidget(_currentTask.description),
|
||||
trailing: IconButton(
|
||||
icon: Icon(Icons.edit),
|
||||
onPressed: () {},
|
||||
),
|
||||
);
|
||||
}
|
||||
return IntrinsicHeight(
|
||||
child: Row(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
|
||||
Container(
|
||||
width: 4.0, // Adjust the width of the red line
|
||||
color: widget.task.color,
|
||||
//margin: EdgeInsets.only(left: 10.0),
|
||||
return ListTile(
|
||||
onTap: () {
|
||||
_showBottomSheet(context, taskState);
|
||||
},
|
||||
leading: _buildLeading(),
|
||||
title: _buildTitle(),
|
||||
subtitle: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
_buildSubtitle(context),
|
||||
SizedBox(height: 8),
|
||||
buildChip() ?? Container(),
|
||||
],
|
||||
),
|
||||
Flexible(
|
||||
child: ListTile(
|
||||
onTap: () {
|
||||
showModalBottomSheet<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return TaskBottomSheet(task: widget.task, onEdit: widget.onEdit, taskState: taskState);
|
||||
});
|
||||
},
|
||||
title: widget.showInfo
|
||||
? RichText(
|
||||
text: TextSpan(
|
||||
text: null,
|
||||
children: <TextSpan>[
|
||||
// TODO: get list name of task
|
||||
//TextSpan(text: widget.task.list.title+" - ", style: TextStyle(color: Colors.grey)),
|
||||
TextSpan(text: widget.task.title),
|
||||
],
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.white
|
||||
: Colors.black,
|
||||
),
|
||||
))
|
||||
: Text(_currentTask.title),
|
||||
subtitle: _buildTaskSubtitle(widget.task, widget.showInfo, context),
|
||||
leading: Checkbox(
|
||||
value: _currentTask.done,
|
||||
onChanged: (bool? newValue) {
|
||||
_change(newValue);
|
||||
},
|
||||
),
|
||||
trailing: IconButton(
|
||||
icon: Icon(Icons.edit),
|
||||
onPressed: () {
|
||||
Navigator.push<Task>(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (buildContext) => TaskEditPage(
|
||||
task: _currentTask,
|
||||
taskState: taskState,
|
||||
),
|
||||
),
|
||||
)
|
||||
.then((task) => setState(() {
|
||||
if (task != null) _currentTask = task;
|
||||
}))
|
||||
.whenComplete(() => widget.onEdit());
|
||||
}),
|
||||
))
|
||||
]));
|
||||
trailing: _buildTrailing(context),
|
||||
);
|
||||
}
|
||||
|
||||
void _change(bool? value) async {
|
||||
|
@ -179,8 +84,123 @@ class TaskTileState extends State<TaskTile> with AutomaticKeepAliveClientMixin {
|
|||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => _currentTask != widget.task;
|
||||
}
|
||||
Widget _buildLeading() {
|
||||
return Checkbox(
|
||||
value: _currentTask.done,
|
||||
onChanged: (bool? newValue) {
|
||||
_change(newValue);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
typedef Future<void> TaskChanged(Task task, bool newValue);
|
||||
Widget _buildTitle() {
|
||||
return widget.showInfo
|
||||
? RichText(
|
||||
text: TextSpan(
|
||||
text: null,
|
||||
children: <TextSpan>[
|
||||
TextSpan(
|
||||
text: widget.task.title,
|
||||
style: TextStyle(
|
||||
decoration:
|
||||
_currentTask.done ? TextDecoration.lineThrough : null,
|
||||
),
|
||||
),
|
||||
],
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.white
|
||||
: Colors.black,
|
||||
),
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
_currentTask.title,
|
||||
style: TextStyle(
|
||||
decoration: _currentTask.done ? TextDecoration.lineThrough : null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSubtitle(BuildContext context) {
|
||||
final durationUntilDue = _currentTask.dueDate?.difference(DateTime.now());
|
||||
if (widget.loading) {
|
||||
return CircularProgressIndicator(
|
||||
strokeWidth: 2.0,
|
||||
);
|
||||
} else if (widget.showInfo && _currentTask.hasDueDate) {
|
||||
return Text(
|
||||
"Due " + durationToHumanReadable(durationUntilDue!),
|
||||
style: TextStyle(
|
||||
color: durationUntilDue.isNegative ? Colors.red : null,
|
||||
),
|
||||
);
|
||||
} else if (_currentTask.priority != null && _currentTask.priority != 0) {
|
||||
return Text(
|
||||
" !" + priorityToString(_currentTask.priority),
|
||||
style: TextStyle(color: Colors.orange),
|
||||
);
|
||||
} else if (_currentTask.description.isNotEmpty) {
|
||||
return HtmlWidget(_currentTask.description);
|
||||
} else {
|
||||
return Container();
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildTrailing(BuildContext context) {
|
||||
return IconButton(
|
||||
icon: Icon(Icons.edit),
|
||||
onPressed: () {
|
||||
Navigator.push<Task>(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (buildContext) => TaskEditPage(
|
||||
task: _currentTask,
|
||||
taskState: Provider.of<ProjectProvider>(context, listen: false),
|
||||
),
|
||||
),
|
||||
).then((task) {
|
||||
if (task != null) {
|
||||
setState(() {
|
||||
_currentTask = task;
|
||||
});
|
||||
}
|
||||
widget.onEdit();
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _showBottomSheet(BuildContext context, ProjectProvider taskState) {
|
||||
showModalBottomSheet<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return TaskBottomSheet(
|
||||
task: widget.task,
|
||||
onEdit: widget.onEdit,
|
||||
taskState: taskState,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget? buildChip() {
|
||||
if (_currentTask.projectId == null || widget.projectsMap == null) {
|
||||
return null;
|
||||
}
|
||||
Project? p = widget.projectsMap![_currentTask.projectId!];
|
||||
if (p != null) {
|
||||
return Transform(
|
||||
transform: new Matrix4.identity()..scale(0.8),
|
||||
child: Chip(
|
||||
label: Text(p.title),
|
||||
backgroundColor: p.color,
|
||||
labelStyle: TextStyle(color: Colors.white),
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
visualDensity: VisualDensity(horizontal: 0.0, vertical: -4),
|
||||
),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -129,7 +129,12 @@ class VikunjaGlobalState extends State<VikunjaGlobal> {
|
|||
});
|
||||
}
|
||||
|
||||
void changeUser(User newUser, {String? token, String? base}) async {
|
||||
void changeUser(
|
||||
User newUser, {
|
||||
String? token,
|
||||
String? base,
|
||||
String? xClientToken,
|
||||
}) async {
|
||||
setState(() {
|
||||
_loading = true;
|
||||
});
|
||||
|
@ -145,6 +150,16 @@ class VikunjaGlobalState extends State<VikunjaGlobal> {
|
|||
// Write new base to secure storage
|
||||
await _storage.write(key: "${newUser.id.toString()}_base", value: base);
|
||||
}
|
||||
|
||||
if (xClientToken == null) {
|
||||
xClientToken =
|
||||
await _storage.read(key: "${newUser.id.toString()}_x_client_token");
|
||||
} else {
|
||||
// Write new xClientToken to secure storage
|
||||
await _storage.write(
|
||||
key: "${newUser.id.toString()}_x_client_token", value: xClientToken);
|
||||
}
|
||||
|
||||
// Set current user in storage
|
||||
await _storage.write(key: 'currentUser', value: newUser.id.toString());
|
||||
client.configure(token: token, base: base, authenticated: true);
|
||||
|
@ -182,13 +197,20 @@ class VikunjaGlobalState extends State<VikunjaGlobal> {
|
|||
}
|
||||
var token = await _storage.read(key: currentUser);
|
||||
var base = await _storage.read(key: '${currentUser}_base');
|
||||
var xClientToken =
|
||||
await _storage.read(key: '${currentUser}_x_client_token');
|
||||
if (token == null || base == null) {
|
||||
setState(() {
|
||||
_loading = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
client.configure(token: token, base: base, authenticated: true);
|
||||
client.configure(
|
||||
token: token,
|
||||
base: base,
|
||||
authenticated: true,
|
||||
xClientToken: xClientToken,
|
||||
);
|
||||
User loadedCurrentUser;
|
||||
try {
|
||||
loadedCurrentUser = await UserAPIService(client).getCurrentUser();
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:after_layout/after_layout.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:vikunja_app/global.dart';
|
||||
import 'package:vikunja_app/models/project.dart';
|
||||
import 'package:vikunja_app/service/services.dart';
|
||||
|
||||
import 'dart:developer';
|
||||
|
@ -33,6 +34,7 @@ class LandingPageState extends State<LandingPage>
|
|||
int? defaultList;
|
||||
bool onlyDueDate = true;
|
||||
List<Task> _tasks = [];
|
||||
Map<int, Project> _projectsMap = {};
|
||||
PageStatus landingPageStatus = PageStatus.built;
|
||||
static const platform = const MethodChannel('vikunja');
|
||||
|
||||
|
@ -115,9 +117,10 @@ class LandingPageState extends State<LandingPage>
|
|||
body = ListView(
|
||||
scrollDirection: Axis.vertical,
|
||||
padding: EdgeInsets.symmetric(vertical: 8.0),
|
||||
children:
|
||||
ListTile.divideTiles(context: context, tiles: _listTasks(context))
|
||||
.toList(),
|
||||
children: ListTile.divideTiles(
|
||||
context: context,
|
||||
tiles: _listTasks(context),
|
||||
).toList(),
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
@ -205,66 +208,66 @@ class LandingPageState extends State<LandingPage>
|
|||
}
|
||||
|
||||
List<Widget> _listTasks(BuildContext context) {
|
||||
var tasks = (_tasks.map((task) => _buildTile(task, context))).toList();
|
||||
//tasks.addAll(_loadingTasks.map(_buildLoadingTile));
|
||||
return tasks;
|
||||
return (_tasks.map((task) => _buildTile(task, context))).toList();
|
||||
}
|
||||
|
||||
TaskTile _buildTile(Task task, BuildContext context) {
|
||||
// key: UniqueKey() seems like a weird workaround to fix the loading issue
|
||||
// is there a better way?
|
||||
return TaskTile(
|
||||
key: UniqueKey(),
|
||||
key: Key("task_${task.id}"),
|
||||
projectsMap: _projectsMap,
|
||||
task: task,
|
||||
onEdit: () => _loadList(context),
|
||||
showInfo: true,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _loadList(BuildContext context) {
|
||||
Future<void> _loadList(BuildContext context) async {
|
||||
_tasks = [];
|
||||
_projectsMap = {};
|
||||
landingPageStatus = PageStatus.loading;
|
||||
// FIXME: loads and reschedules tasks each time list is updated
|
||||
VikunjaGlobal.of(context)
|
||||
.notifications
|
||||
.scheduleDueNotifications(VikunjaGlobal.of(context).taskService);
|
||||
return VikunjaGlobal.of(context)
|
||||
bool showOnlyDueDateTasks = await VikunjaGlobal.of(context)
|
||||
.settingsManager
|
||||
.getLandingPageOnlyDueDateTasks()
|
||||
.then((showOnlyDueDateTasks) {
|
||||
VikunjaGlobalState global = VikunjaGlobal.of(context);
|
||||
Map<String, dynamic>? 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<Future<void>?>((response) =>
|
||||
_handleTaskList(response?.body, showOnlyDueDateTasks));
|
||||
}
|
||||
.getLandingPageOnlyDueDateTasks();
|
||||
|
||||
return global.taskService
|
||||
.getByOptions(TaskServiceOptions(newOptions: [
|
||||
TaskServiceOption<TaskServiceOptionSortBy>(
|
||||
"sort_by", ["due_date", "id"]),
|
||||
TaskServiceOption<TaskServiceOptionSortBy>(
|
||||
"order_by", ["asc", "desc"]),
|
||||
TaskServiceOption<TaskServiceOptionFilterBy>("filter_by", "done"),
|
||||
TaskServiceOption<TaskServiceOptionFilterValue>(
|
||||
"filter_value", "false"),
|
||||
TaskServiceOption<TaskServiceOptionFilterComparator>(
|
||||
"filter_comparator", "equals"),
|
||||
TaskServiceOption<TaskServiceOptionFilterConcat>(
|
||||
"filter_concat", "and"),
|
||||
], clearOther: true))
|
||||
.then<Future<void>?>(
|
||||
(taskList) => _handleTaskList(taskList, showOnlyDueDateTasks));
|
||||
}); //.onError((error, stackTrace) {print("error");});
|
||||
VikunjaGlobalState global = VikunjaGlobal.of(context);
|
||||
List<Project>? projects = await global.projectService.getAll();
|
||||
if (projects != null) {
|
||||
projects.forEach((project) {
|
||||
_projectsMap[project.id] = project;
|
||||
});
|
||||
}
|
||||
|
||||
Map<String, dynamic>? 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) {
|
||||
var response = await global.taskService.getAllByProject(filterId, {
|
||||
"sort_by": ["due_date", "id"],
|
||||
"order_by": ["asc", "desc"],
|
||||
});
|
||||
await _handleTaskList(response?.body, showOnlyDueDateTasks);
|
||||
return;
|
||||
}
|
||||
|
||||
var taskList =
|
||||
await global.taskService.getByOptions(TaskServiceOptions(newOptions: [
|
||||
TaskServiceOption<TaskServiceOptionSortBy>("sort_by", ["due_date", "id"]),
|
||||
TaskServiceOption<TaskServiceOptionSortBy>("order_by", ["asc", "desc"]),
|
||||
TaskServiceOption<TaskServiceOptionFilterBy>("filter_by", "done"),
|
||||
TaskServiceOption<TaskServiceOptionFilterValue>("filter_value", "false"),
|
||||
TaskServiceOption<TaskServiceOptionFilterComparator>(
|
||||
"filter_comparator", "equals"),
|
||||
TaskServiceOption<TaskServiceOptionFilterConcat>("filter_concat", "and"),
|
||||
], clearOther: true));
|
||||
await _handleTaskList(taskList, showOnlyDueDateTasks);
|
||||
}
|
||||
|
||||
Future<void> _handleTaskList(
|
||||
|
|
|
@ -65,7 +65,7 @@ class _ListPageState extends State<ListPage> {
|
|||
Widget build(BuildContext context) {
|
||||
taskState = Provider.of<ListProvider>(context);
|
||||
//_kanban = KanbanClass(
|
||||
// context, nullSetState, _onViewTapped, _addItemDialog, _list);
|
||||
// context, nullSetState, _onViewTapped, _addItemDialog, _list);
|
||||
|
||||
Widget body;
|
||||
|
||||
|
@ -126,10 +126,8 @@ class _ListPageState extends State<ListPage> {
|
|||
]);
|
||||
break;
|
||||
case PageStatus.empty:
|
||||
body = new Stack(children: [
|
||||
ListView(),
|
||||
Center(child: Text("This view is empty"))
|
||||
]);
|
||||
body = new Stack(
|
||||
children: [ListView(), Center(child: Text("This view is empty"))]);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -241,8 +239,8 @@ class _ListPageState extends State<ListPage> {
|
|||
TaskTile _buildLoadingTile(Task task) {
|
||||
return TaskTile(
|
||||
task: task,
|
||||
loading: true, onEdit: () {},
|
||||
|
||||
loading: true,
|
||||
onEdit: () {},
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -255,13 +253,11 @@ class _ListPageState extends State<ListPage> {
|
|||
_loadTasksForPage(1);
|
||||
break;
|
||||
case 1:
|
||||
await _kanban
|
||||
.loadBucketsForPage(1);
|
||||
await _kanban.loadBucketsForPage(1);
|
||||
// load all buckets to get length for RecordableListView
|
||||
while (_currentPage < taskState.maxPages) {
|
||||
_currentPage++;
|
||||
await _kanban
|
||||
.loadBucketsForPage(_currentPage);
|
||||
await _kanban.loadBucketsForPage(_currentPage);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -271,12 +267,11 @@ class _ListPageState extends State<ListPage> {
|
|||
}
|
||||
|
||||
Future<void> _loadTasksForPage(int page) {
|
||||
return Provider.of<ListProvider>(context, listen: false)
|
||||
.loadTasks(
|
||||
context: context,
|
||||
listId: _list.id,
|
||||
page: page,
|
||||
displayDoneTasks: displayDoneTasks);
|
||||
return Provider.of<ListProvider>(context, listen: false).loadTasks(
|
||||
context: context,
|
||||
listId: _list.id,
|
||||
page: page,
|
||||
displayDoneTasks: displayDoneTasks);
|
||||
}
|
||||
|
||||
Future<void> _addItemDialog(BuildContext context, [Bucket? bucket]) {
|
||||
|
|
|
@ -357,7 +357,12 @@ class _LoginPageState extends State<LoginPage> {
|
|||
}
|
||||
|
||||
if (newUser.error == 0)
|
||||
vGlobal.changeUser(newUser.user!, token: newUser.token, base: _server);
|
||||
vGlobal.changeUser(
|
||||
newUser.user!,
|
||||
token: newUser.token,
|
||||
base: _server,
|
||||
xClientToken: _xClientToken,
|
||||
);
|
||||
} catch (ex) {
|
||||
print(ex);
|
||||
} finally {
|
||||
|
|
Loading…
Reference in New Issue
Block a user