1
0
mirror of https://github.com/go-vikunja/app synced 2024-06-02 18:49:47 +00:00

feat: added model for new "views" feature.

This commit is contained in:
Benimautner 2024-04-29 15:52:00 +02:00
parent 828a57a642
commit 6259078e1a
9 changed files with 147 additions and 58 deletions

View File

@ -8,9 +8,9 @@ class BucketAPIService extends APIService implements BucketService {
BucketAPIService(Client client) : super(client);
@override
Future<Bucket?> add(int projectId, Bucket bucket) {
Future<Bucket?> add(int projectId, int viewId, Bucket bucket) {
return client
.put('/projects/$projectId/buckets', body: bucket.toJSON())
.put('/projects/$projectId/views/$viewId/buckets', body: bucket.toJSON())
.then((response) {
if (response == null) return null;
return Bucket.fromJSON(response.body);
@ -18,8 +18,8 @@ class BucketAPIService extends APIService implements BucketService {
}
@override
Future delete(int projectId, int bucketId) {
return client.delete('/projects/$projectId/buckets/$bucketId');
Future delete(int projectId,int viewId, int bucketId) {
return client.delete('/projects/$projectId/views/$viewId/buckets/$bucketId');
}
/* Not implemented in the Vikunja API
@ -32,9 +32,9 @@ class BucketAPIService extends APIService implements BucketService {
*/
@override
Future<Response?> getAllByList(int projectId,
Future<Response?> getAllByList(int projectId, int viewId,
[Map<String, List<String>>? queryParameters]) {
return client.get('/projects/$projectId/buckets', queryParameters).then(
return client.get('/projects/$projectId/views/$viewId/tasks', queryParameters).then(
(response) => response != null
? new Response(
convertList(response.body, (result) => Bucket.fromJSON(result)),
@ -48,9 +48,9 @@ class BucketAPIService extends APIService implements BucketService {
int get maxPages => maxPages;
@override
Future<Bucket?> update(Bucket bucket) {
Future<Bucket?> update(Bucket bucket, int projectId, int viewId) {
return client
.post('/projects/${bucket.projectId}/buckets/${bucket.id}',
.post('/projects/$projectId/views/$viewId/buckets/${bucket.id}',
body: bucket.toJSON())
.then((response) {
if (response == null) return null;

View File

@ -9,6 +9,7 @@ import '../global.dart';
import '../models/bucket.dart';
import '../models/list.dart';
import '../models/project.dart';
import '../models/view.dart';
import '../pages/list/list.dart';
import '../stores/project_store.dart';
import '../utils/calculate_item_position.dart';
@ -26,11 +27,12 @@ class KanbanClass {
Function _onViewTapped, _addItemDialog, notify;
Duration _lastTaskDragUpdateAction = Duration.zero;
Project _list;
Project _project;
ProjectView _view;
Map<int, BucketProps> _bucketProps = {};
KanbanClass(this.context, this.notify, this._onViewTapped,
this._addItemDialog, this._list) {
this._addItemDialog, this._project, this._view) {
taskState = Provider.of<ProjectProvider>(context);
}
@ -44,7 +46,7 @@ class KanbanClass {
_pageController!.viewportFraction != bucketFraction)
_pageController = PageController(viewportFraction: bucketFraction);
print(_list.doneBucketId);
print(_project.doneBucketId);
return ReorderableListView.builder(
scrollDirection: Axis.horizontal,
@ -173,9 +175,9 @@ class KanbanClass {
Future<void> _setDoneBucket(BuildContext context, int bucketId) async {
//setState(() {});
_list = (await VikunjaGlobal.of(context)
_project = (await VikunjaGlobal.of(context)
.projectService
.update(_list.copyWith(doneBucketId: bucketId)))!;
.update(_project.copyWith(doneBucketId: bucketId)))!;
notify();
}
@ -187,13 +189,15 @@ class KanbanClass {
await Provider.of<ProjectProvider>(context, listen: false).addBucket(
context: context,
newBucket: Bucket(
title: title,
createdBy: currentUser,
projectId: _list.id,
projectViewId: _view.id,
limit: 0,
),
listId: _list.id,
listId: _project.id,
viewId: _view.id,
);
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
@ -208,6 +212,8 @@ class KanbanClass {
.updateBucket(
context: context,
bucket: bucket,
listId: _project.id,
viewId: _view.id
)
.then((_) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
@ -221,7 +227,8 @@ class KanbanClass {
Future<void> _deleteBucket(BuildContext context, Bucket bucket) async {
await Provider.of<ProjectProvider>(context, listen: false).deleteBucket(
context: context,
listId: bucket.projectId,
listId: _project.id,
viewId: _view.id,
bucketId: bucket.id,
);
@ -282,7 +289,7 @@ class KanbanClass {
minLeadingWidth: 15,
horizontalTitleGap: 4,
contentPadding: const EdgeInsets.only(left: 16, right: 10),
leading: bucket.id == _list.doneBucketId
leading: bucket.id == _project.doneBucketId
? Icon(
Icons.done_all,
color: Colors.green,
@ -353,7 +360,7 @@ class KanbanClass {
break;
case BucketMenu.done:
//bucket.isDoneBucket = !(bucket.id == _list.doneBucketId);
_list = _list.copyWith(doneBucketId: bucket.id);
_project = _project.copyWith(doneBucketId: bucket.id);
_setDoneBucket(context, bucket.id);
notify();
//_updateBucket(context, bucket);
@ -378,7 +385,7 @@ class KanbanClass {
padding: const EdgeInsets.only(right: 4),
child: Icon(
Icons.done_all,
color: bucket.id == _list.doneBucketId
color: bucket.id == _project.doneBucketId
? Colors.green
: null,
),
@ -549,7 +556,8 @@ class KanbanClass {
}
Future<void> loadBucketsForPage(int page) {
print(_view.id);
return Provider.of<ProjectProvider>(context, listen: false)
.loadBuckets(context: context, listId: _list.id, page: page);
.loadBuckets(context: context, listId: _project.id, viewId: _view.id, page: page);
}
}

View File

@ -5,7 +5,7 @@ import 'package:vikunja_app/models/user.dart';
@JsonSerializable()
class Bucket {
int id, projectId, limit;
int id, projectViewId, limit;
String title;
double? position;
final DateTime created, updated;
@ -15,7 +15,7 @@ class Bucket {
Bucket({
this.id = 0,
required this.projectId,
required this.projectViewId,
required this.title,
this.position,
required this.limit,
@ -30,7 +30,7 @@ class Bucket {
Bucket.fromJSON(Map<String, dynamic> json)
: id = json['id'],
projectId = json['project_id'],
projectViewId = json['project_view_id'],
title = json['title'],
position = json['position'] is int
? json['position'].toDouble()
@ -48,7 +48,7 @@ class Bucket {
toJSON() => {
'id': id,
'list_id': projectId,
'project_view_id': projectViewId,
'title': title,
'position': position,
'limit': limit,

View File

@ -1,6 +1,7 @@
import 'dart:ui';
import 'package:vikunja_app/models/user.dart';
import 'package:vikunja_app/models/view.dart';
class Project {
final int id;
@ -15,6 +16,7 @@ class Project {
final int? doneBucketId;
Iterable<Project>? subprojects;
final List<ProjectView> views;
Project(
{this.id = 0,
@ -26,6 +28,7 @@ class Project {
this.color,
this.isArchived = false,
this.isFavourite = false,
this.views = const [],
required this.title,
created,
updated})
@ -41,6 +44,7 @@ class Project {
isFavourite = json['is_archived'],
doneBucketId = json['done_bucket_id'],
parentProjectId = json['parent_project_id'],
views = json['views'].map<ProjectView>((view) => ProjectView.fromJson(view)).toList(),
created = DateTime.parse(json['created']),
updated = DateTime.parse(json['updated']),
color = json['hex_color'] != ''

61
lib/models/view.dart Normal file
View File

@ -0,0 +1,61 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
enum ViewKind {
LIST,
GANTT,
TABLE,
KANBAN
}
class ProjectView {
//"bucket_configuration": [],
//"bucket_configuration_mode": 0,
final DateTime created; // "created": "string",
final int defaultBucketId;//": 0,
final int doneBucketId;
//"filter": "string",
final int id;//": 0,
final int position;
final int projectId;
final String title;
final DateTime updated;
final String viewKind;
get icon {
switch(viewKind) {
case "list":
return Icon(Icons.view_list);
case "kanban":
return Icon(Icons.view_kanban);
default:
return Icon(Icons.disabled_by_default_outlined);
}
}
ProjectView(this.created, this.defaultBucketId, this.doneBucketId, this.id, this.position, this.projectId, this.title, this.updated, this.viewKind);
ProjectView.fromJson(Map<String, dynamic> json)
: created = DateTime.parse(json['created']),
defaultBucketId = json['default_bucket_id'],
doneBucketId = json['done_bucket_id'],
id = json['id'],
position = json['position'],
projectId = json['project_id'],
title = json['title'],
viewKind = json['view_kind'],
updated = DateTime.parse(json['updated']);
toJSON() =>
{
"created": created.toUtc().toIso8601String(),
"default_bucket_id": defaultBucketId,
"done_bucket_id": doneBucketId,
"id": id,
"position": position,
"project_id": projectId,
"title": title,
"updated": updated.toUtc().toIso8601String(),
"view_kind": viewKind
};
}

View File

@ -68,7 +68,7 @@ class _ListPageState extends State<ListPage> {
Widget build(BuildContext context) {
taskState = Provider.of<ProjectProvider>(context);
_kanban = KanbanClass(
context, nullSetState, _onViewTapped, _addItemDialog, _project);
context, nullSetState, _onViewTapped, _addItemDialog, _project, _project.views[_viewIndex]);
Widget body;
@ -114,13 +114,13 @@ class _ListPageState extends State<ListPage> {
);
}(Theme.of(context)),
child: () {
switch (_viewIndex) {
case 0:
switch (_project.views[_viewIndex].viewKind) {
case "list":
return _listView(context);
case 1:
case "kanban":
return _kanban.kanbanView();
default:
return _listView(context);
return Text("Not implemented");
}
}(),
),
@ -153,15 +153,23 @@ class _ListPageState extends State<ListPage> {
],
),
body: RefreshIndicator(onRefresh: () => _loadList(), child: body),
floatingActionButton: _viewIndex == 1
floatingActionButton: _project.views[_viewIndex].viewKind == "kanban"
? null
: Builder(
builder: (context) => FloatingActionButton(
onPressed: () => _addItemDialog(context),
child: Icon(Icons.add)),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
bottomNavigationBar: _project.views.length >= 2 ? BottomNavigationBar(
type:BottomNavigationBarType.fixed,
items: _project.views.map((view) =>
BottomNavigationBarItem(
icon: view.icon,
label: view.title,
tooltip: view.title,
)).toList(),
/*
;const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.view_list),
label: 'List',
@ -172,10 +180,10 @@ class _ListPageState extends State<ListPage> {
label: 'Kanban',
tooltip: 'Kanban',
),
],
], */
currentIndex: _viewIndex,
onTap: _onViewTapped,
),
) : null,
);
}
@ -311,11 +319,11 @@ class _ListPageState extends State<ListPage> {
taskState.pageStatus = (PageStatus.loading);
updateDisplayDoneTasks().then((value) async {
switch (_viewIndex) {
case 0:
switch (_project.views[_viewIndex].viewKind) {
case "list":
_loadTasksForPage(1);
break;
case 1:
case "kanban":
await _kanban.loadBucketsForPage(1);
// load all buckets to get length for RecordableListView
while (_currentPage < taskState.maxPages) {

View File

@ -208,13 +208,13 @@ abstract class TaskService {
abstract class BucketService {
// Not implemented in the Vikunja API
// Future<Bucket> get(int listId, int bucketId);
Future<Bucket?> update(Bucket bucket);
Future<Bucket?> update(Bucket bucket, int projectId, int viewId);
Future delete(int listId, int bucketId);
Future delete(int listId, int viewId, int bucketId);
Future<Bucket?> add(int listId, Bucket bucket);
Future<Bucket?> add(int listId, int viewId, Bucket bucket);
Future<Response?> getAllByList(int listId,
Future<Response?> getAllByList(int listId, int viewId,
[Map<String, List<String>> queryParameters]);
int get maxPages;

View File

@ -43,7 +43,6 @@ class ListProvider with ChangeNotifier {
set pageStatus(PageStatus ps) {
_pageStatus = ps;
print("new PageStatus: ${ps.toString()}");
notifyListeners();
}
@ -83,7 +82,7 @@ class ListProvider with ChangeNotifier {
}
Future<void> loadBuckets(
{required BuildContext context, required int listId, int page = 1}) {
{required BuildContext context, required int listId, required int viewId, int page = 1}) {
_buckets = [];
pageStatus = PageStatus.loading;
notifyListeners();
@ -94,7 +93,7 @@ class ListProvider with ChangeNotifier {
return VikunjaGlobal.of(context)
.bucketService
.getAllByList(listId, queryParams)
.getAllByList(listId, viewId, queryParams)
.then((response) {
if (response == null) {
pageStatus = PageStatus.error;
@ -179,11 +178,12 @@ class ListProvider with ChangeNotifier {
Future<void> addBucket(
{required BuildContext context,
required Bucket newBucket,
required int listId}) {
required int listId,
required int viewId}) {
notifyListeners();
return VikunjaGlobal.of(context)
.bucketService
.add(listId, newBucket)
.add(listId, viewId, newBucket)
.then((bucket) {
if (bucket == null) return null;
_buckets.add(bucket);
@ -192,10 +192,11 @@ class ListProvider with ChangeNotifier {
}
Future<void> updateBucket(
{required BuildContext context, required Bucket bucket}) {
{required BuildContext context, required Bucket bucket, required int listId,
required int viewId}) {
return VikunjaGlobal.of(context)
.bucketService
.update(bucket)
.update(bucket, listId, viewId)
.then((rBucket) {
if (rBucket == null) return null;
_buckets[_buckets.indexWhere((b) => rBucket.id == b.id)] = rBucket;
@ -207,10 +208,11 @@ class ListProvider with ChangeNotifier {
Future<void> deleteBucket(
{required BuildContext context,
required int listId,
required int viewId,
required int bucketId}) {
return VikunjaGlobal.of(context)
.bucketService
.delete(listId, bucketId)
.delete(listId, viewId, bucketId)
.then((_) {
_buckets.removeWhere((bucket) => bucket.id == bucketId);
notifyListeners();

View File

@ -43,7 +43,6 @@ class ProjectProvider with ChangeNotifier {
set pageStatus(PageStatus ps) {
_pageStatus = ps;
print("new PageStatus: ${ps.toString()}");
notifyListeners();
}
@ -85,7 +84,7 @@ class ProjectProvider with ChangeNotifier {
}
Future<void> loadBuckets(
{required BuildContext context, required int listId, int page = 1}) {
{required BuildContext context, required int listId, required int viewId, int page = 1}) {
_buckets = [];
pageStatus = PageStatus.loading;
notifyListeners();
@ -94,9 +93,12 @@ class ProjectProvider with ChangeNotifier {
"page": [page.toString()]
};
print(listId);
print(viewId);
return VikunjaGlobal.of(context)
.bucketService
.getAllByList(listId, queryParams)
.getAllByList(listId, viewId, queryParams)
.then((response) {
if (response == null) {
pageStatus = PageStatus.error;
@ -106,6 +108,7 @@ class ProjectProvider with ChangeNotifier {
_maxPages = int.parse(response.headers["x-pagination-total-pages"]!);
}
_buckets.addAll(response.body);
print(_buckets[0].toJSON());
pageStatus = PageStatus.success;
});
@ -181,11 +184,12 @@ class ProjectProvider with ChangeNotifier {
Future<void> addBucket(
{required BuildContext context,
required Bucket newBucket,
required int listId}) {
required int listId,
required int viewId}) {
notifyListeners();
return VikunjaGlobal.of(context)
.bucketService
.add(listId, newBucket)
.add(listId, viewId, newBucket)
.then((bucket) {
if (bucket == null) return null;
_buckets.add(bucket);
@ -194,10 +198,11 @@ class ProjectProvider with ChangeNotifier {
}
Future<void> updateBucket(
{required BuildContext context, required Bucket bucket}) {
{required BuildContext context, required Bucket bucket, required int listId,
required int viewId}) {
return VikunjaGlobal.of(context)
.bucketService
.update(bucket)
.update(bucket, listId, viewId)
.then((rBucket) {
if (rBucket == null) return null;
_buckets[_buckets.indexWhere((b) => rBucket.id == b.id)] = rBucket;
@ -209,10 +214,11 @@ class ProjectProvider with ChangeNotifier {
Future<void> deleteBucket(
{required BuildContext context,
required int listId,
required int bucketId}) {
required int bucketId,
required int viewId}) {
return VikunjaGlobal.of(context)
.bucketService
.delete(listId, bucketId)
.delete(listId, viewId, bucketId)
.then((_) {
_buckets.removeWhere((bucket) => bucket.id == bucketId);
notifyListeners();