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:
parent
828a57a642
commit
6259078e1a
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
61
lib/models/view.dart
Normal 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
|
||||
};
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue
Block a user