Skip to content

Commit f3ea406

Browse files
committed
Add resources to calendar item dialog
1 parent 3dc2770 commit f3ea406

File tree

8 files changed

+164
-16
lines changed

8 files changed

+164
-16
lines changed

api/lib/models/event/item/model.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ sealed class CalendarItem
3535
});
3636

3737
factory CalendarItem.fromDatabase(Map<String, dynamic> row) =>
38-
CalendarItemMapper.fromMap(row);
38+
FixedCalendarItemMapper.fromMap(row);
3939

4040
CalendarItemType get type {
4141
if (start == null && end == null) {

api/lib/models/resource/database.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,12 +134,12 @@ abstract class ResourceDatabaseConnector<T> extends DatabaseModelConnector
134134
}
135135

136136
@override
137-
Future<List<T>> getConnected(Uint8List noteId,
137+
Future<List<T>> getConnected(Uint8List resourceId,
138138
{int offset = 0, int limit = 50}) async {
139139
final result = await db?.query(
140140
'$tableName JOIN resources ON resources.id = resourceId',
141-
where: 'noteId = ?',
142-
whereArgs: [noteId],
141+
where: 'resourceId = ?',
142+
whereArgs: [resourceId],
143143
offset: offset,
144144
limit: limit,
145145
);

api/lib/services/database.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ abstract class DatabaseModelConnector extends ModelConnector with TableService {
167167
Future<void> connect(Uint8List connectId, Uint8List itemId) async {
168168
if (await isConnected(connectId, itemId)) return;
169169
await db?.insert(tableName, {
170-
'noteId': itemId,
170+
itemIdName: itemId,
171171
connectedIdName: connectId,
172172
});
173173
}

app/lib/pages/calendar/item.dart

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'package:flow/cubits/flow.dart';
22
import 'package:flow/helpers/event.dart';
3+
import 'package:flow/pages/events/resources.dart';
34
import 'package:flow/pages/groups/select.dart';
45
import 'package:flow/widgets/markdown_field.dart';
56
import 'package:flutter/material.dart';
@@ -81,8 +82,9 @@ class _CalendarItemDialogState extends State<CalendarItemDialog> {
8182
@override
8283
Widget build(BuildContext context) {
8384
final cubit = context.read<FlowCubit>();
84-
final connector = cubit.getService(_source).calendarItemNote;
85-
final tabs = !_create && connector != null;
85+
final noteConnector = cubit.getService(_source).calendarItemNote;
86+
final resourceConnector = cubit.getService(_source).calendarItemResource;
87+
final tabs = !_create && noteConnector != null && resourceConnector != null;
8688
final type = _item.type;
8789
String title;
8890
switch (type) {
@@ -150,7 +152,7 @@ class _CalendarItemDialogState extends State<CalendarItemDialog> {
150152
),
151153
],
152154
content: DefaultTabController(
153-
length: tabs ? 2 : 1,
155+
length: tabs ? 3 : 1,
154156
child: Column(
155157
children: [
156158
if (tabs)
@@ -164,6 +166,10 @@ class _CalendarItemDialogState extends State<CalendarItemDialog> {
164166
PhosphorIconsLight.checkCircle,
165167
AppLocalizations.of(context).notes
166168
),
169+
(
170+
PhosphorIconsLight.cube,
171+
AppLocalizations.of(context).resources
172+
),
167173
]
168174
.map((e) => HorizontalTab(
169175
icon: PhosphorIcon(e.$1),
@@ -304,12 +310,18 @@ class _CalendarItemDialogState extends State<CalendarItemDialog> {
304310
],
305311
),
306312
),
307-
if (tabs)
313+
if (tabs) ...[
308314
NotesView(
309315
model: widget.item!,
310-
connector: connector,
316+
connector: noteConnector,
317+
source: _source,
318+
),
319+
ResourcesView(
320+
model: widget.item!,
321+
connector: resourceConnector,
311322
source: _source,
312323
),
324+
],
313325
],
314326
),
315327
),
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_bloc/flutter_bloc.dart';
3+
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
4+
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
5+
import 'package:phosphor_flutter/phosphor_flutter.dart';
6+
import 'package:flow_api/models/model.dart';
7+
import 'package:flow_api/models/resource/model.dart';
8+
import 'package:flow_api/models/resource/service.dart';
9+
10+
import '../../cubits/flow.dart';
11+
import '../../widgets/builder_delegate.dart';
12+
import '../resources/resource.dart';
13+
14+
class ResourcesView<T extends DescriptiveModel> extends StatefulWidget {
15+
final T model;
16+
final String source;
17+
final ResourceConnector<T> connector;
18+
19+
const ResourcesView(
20+
{super.key,
21+
required this.source,
22+
required this.connector,
23+
required this.model});
24+
25+
@override
26+
State<ResourcesView<T>> createState() => _ResourcesViewState();
27+
}
28+
29+
class _ResourcesViewState<T extends DescriptiveModel>
30+
extends State<ResourcesView<T>> {
31+
static const _pageSize = 20;
32+
33+
late final ResourceService? _resourceService;
34+
35+
final PagingController<int, Resource> _pagingController =
36+
PagingController(firstPageKey: 0);
37+
38+
@override
39+
void initState() {
40+
final service = context.read<FlowCubit>().getService(widget.source);
41+
_resourceService = service.resource;
42+
_pagingController.addPageRequestListener((pageKey) {
43+
_fetchPage(pageKey);
44+
});
45+
super.initState();
46+
}
47+
48+
Future<void> _fetchPage(int pageKey) async {
49+
try {
50+
final newItems = await widget.connector.getResources(widget.model.id!,
51+
offset: pageKey * _pageSize, limit: _pageSize);
52+
final isLastPage = newItems.length < _pageSize;
53+
if (isLastPage) {
54+
_pagingController.appendLastPage(newItems);
55+
} else {
56+
final nextPageKey = pageKey + 1;
57+
_pagingController.appendPage(newItems, nextPageKey);
58+
}
59+
} catch (error) {
60+
_pagingController.error = error;
61+
}
62+
}
63+
64+
@override
65+
Widget build(BuildContext context) =>
66+
// Don't worry about displaying progress or error indicators on screen; the
67+
// package takes care of that. If you want to customize them, use the
68+
// [PagedChildBuilderDelegate] properties.
69+
Stack(
70+
children: [
71+
Column(
72+
children: [
73+
Flexible(
74+
child: PagedListView<int, Resource>(
75+
pagingController: _pagingController,
76+
builderDelegate: buildMaterialPagedDelegate<Resource>(
77+
_pagingController,
78+
(context, item, index) {
79+
return Dismissible(
80+
key: ValueKey(item.id),
81+
background: Container(color: Colors.red),
82+
onDismissed: (direction) {
83+
_resourceService?.deleteResource(item.id!);
84+
_pagingController.itemList!.remove(item);
85+
},
86+
child: ListTile(
87+
title: Text(item.name),
88+
onTap: () async {
89+
await showDialog<Resource>(
90+
context: context,
91+
builder: (context) => ResourceDialog(
92+
source: widget.source,
93+
resource: item,
94+
),
95+
);
96+
_pagingController.refresh();
97+
},
98+
),
99+
);
100+
},
101+
),
102+
),
103+
),
104+
const SizedBox(height: 64),
105+
],
106+
),
107+
Align(
108+
alignment: Alignment.bottomCenter,
109+
child: Padding(
110+
padding: const EdgeInsets.all(16.0),
111+
child: FloatingActionButton.extended(
112+
label: Text(AppLocalizations.of(context).create),
113+
icon: const PhosphorIcon(PhosphorIconsLight.plus),
114+
onPressed: () async {
115+
final resource = await showDialog<Resource>(
116+
context: context,
117+
builder: (context) => ResourceDialog(
118+
source: widget.source,
119+
),
120+
);
121+
if (resource != null) {
122+
await widget.connector
123+
.connect(widget.model.id!, resource.id!);
124+
}
125+
_pagingController.refresh();
126+
},
127+
),
128+
),
129+
),
130+
],
131+
);
132+
133+
@override
134+
void dispose() {
135+
_pagingController.dispose();
136+
super.dispose();
137+
}
138+
}

app/lib/pages/resources/resource.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,7 @@ class ResourceDialog extends StatelessWidget {
100100
await currentService?.updateResource(currentResource);
101101
}
102102
if (context.mounted) {
103-
Navigator.of(context)
104-
.pop(SourcedModel(currentSource, currentResource));
103+
Navigator.of(context).pop(currentResource);
105104
}
106105
},
107106
child: Text(AppLocalizations.of(context).save),

app/lib/setup_web.dart

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
// ignore: avoid_web_libraries_in_flutter
2-
import 'dart:html';
1+
import 'package:flutter/services.dart';
32

43
import 'api/storage/sources.dart';
54
import 'cubits/settings.dart';
65
import 'setup.dart' as general_setup;
76

87
Future<void> setup(
98
SettingsCubit settingsCubit, SourcesService sourcesService) async {
10-
window.document.onContextMenu.listen((evt) => evt.preventDefault());
9+
await BrowserContextMenu.disableContextMenu();
1110
await general_setup.setup(settingsCubit, sourcesService);
1211
}

metadata/en-US/changelogs/9.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* Users can now be added to events and calendar items
55
* Rename places to resources and allow for multiple resources
66
* Convert data classes to dart_mappable
7-
* Upgrade to flutter 3.27
7+
* Upgrade to flutter 3.29
88
* Use minSdkVersion 23 instead of 21 (The minimum version of android is 6.0 (Marshmallow))
99
* Fix labels not filtering correctly
1010
* Update to agb 8.8

0 commit comments

Comments
 (0)