Added option to delete, edit and reorder categories.
NOTE: The click action is removed, if the user doesn't have the required permission and the drag-handle is not shown. BUG: Editing the list (in any way, other than reordering it), wont reload the list - hopefully this will be addressed later, by caching the categories (or removing them from the cache) and auto-rebuilding on cache-change
This commit is contained in:
parent
b73362c101
commit
1ba36ff9f0
5 changed files with 760 additions and 250 deletions
|
@ -372,6 +372,23 @@ class RoomCategory {
|
||||||
name: json['title'],
|
name: json['title'],
|
||||||
color: colorFromString(json['color']));
|
color: colorFromString(json['color']));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static List<ColorSwatch<int>> listColors() {
|
||||||
|
return [
|
||||||
|
"red",
|
||||||
|
"green",
|
||||||
|
"yellow",
|
||||||
|
"blue",
|
||||||
|
"aqua",
|
||||||
|
"purple",
|
||||||
|
"red-acc",
|
||||||
|
"green-acc",
|
||||||
|
"yellow-acc",
|
||||||
|
"blue-acc",
|
||||||
|
"aqua-acc",
|
||||||
|
"purple-acc",
|
||||||
|
].map((txt) => colorFromString(txt)).toList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ColorSwatch<int> colorFromString(String text) {
|
ColorSwatch<int> colorFromString(String text) {
|
||||||
|
@ -404,3 +421,45 @@ ColorSwatch<int> colorFromString(String text) {
|
||||||
return Colors.purple;
|
return Colors.purple;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String colorIdFromColor(ColorSwatch<int> color) {
|
||||||
|
if (color == Colors.redAccent) {
|
||||||
|
return 'red-acc';
|
||||||
|
}
|
||||||
|
if (color == Colors.greenAccent) {
|
||||||
|
return 'green-acc';
|
||||||
|
}
|
||||||
|
if (color == Colors.yellowAccent) {
|
||||||
|
return 'yellow-acc';
|
||||||
|
}
|
||||||
|
if (color == Colors.blueAccent) {
|
||||||
|
return 'blue-acc';
|
||||||
|
}
|
||||||
|
if (color == Colors.tealAccent) {
|
||||||
|
return 'teal-acc';
|
||||||
|
}
|
||||||
|
if (color == Colors.purpleAccent) {
|
||||||
|
return 'purple-acc';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (color == Colors.red) {
|
||||||
|
return 'red';
|
||||||
|
}
|
||||||
|
if (color == Colors.green) {
|
||||||
|
return 'green';
|
||||||
|
}
|
||||||
|
if (color == Colors.yellow) {
|
||||||
|
return 'yellow';
|
||||||
|
}
|
||||||
|
if (color == Colors.blue) {
|
||||||
|
return 'blue';
|
||||||
|
}
|
||||||
|
if (color == Colors.teal) {
|
||||||
|
return 'teal';
|
||||||
|
}
|
||||||
|
if (color == Colors.purple) {
|
||||||
|
return 'purple';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'purple';
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,4 @@
|
||||||
{
|
{
|
||||||
"helloWorld": "Hello World!",
|
|
||||||
"@helloWorld": {
|
|
||||||
"description": "The conventional newborn programmer greeting"
|
|
||||||
},
|
|
||||||
|
|
||||||
"welcomeTitle": "Welcome to Outbag",
|
"welcomeTitle": "Welcome to Outbag",
|
||||||
"@welcomeTitle": {
|
"@welcomeTitle": {
|
||||||
"description": "Title shown on welcome screen"
|
"description": "Title shown on welcome screen"
|
||||||
|
@ -291,7 +286,7 @@
|
||||||
"errorAccountDeletion": "Your account no longer exists",
|
"errorAccountDeletion": "Your account no longer exists",
|
||||||
|
|
||||||
"errorNoSuchAccount": "Account does not exist",
|
"errorNoSuchAccount": "Account does not exist",
|
||||||
"errorUsernameTaken": "Already with that username already exists",
|
"errorUsernameTaken": "Account with that username already exists",
|
||||||
"errorNoSuchRoom": "Room does not exist",
|
"errorNoSuchRoom": "Room does not exist",
|
||||||
"errorRoomIdTaken": "Room with that ID already exists",
|
"errorRoomIdTaken": "Room with that ID already exists",
|
||||||
|
|
||||||
|
@ -318,5 +313,28 @@
|
||||||
"errorInvalidToken": "Invalid Token",
|
"errorInvalidToken": "Invalid Token",
|
||||||
|
|
||||||
"errorServerDoesntExist": "Server does not exist",
|
"errorServerDoesntExist": "Server does not exist",
|
||||||
"errorInvalidServerToken": "Server token invalid"
|
"errorInvalidServerToken": "Server token invalid",
|
||||||
|
|
||||||
|
"editCategoryShort": "Edit",
|
||||||
|
"editCategory": "Edit Category",
|
||||||
|
"editCategoryLong": "Change the category color or name",
|
||||||
|
"newCategory": "New Category",
|
||||||
|
"newCategoryLong": "Create a new category",
|
||||||
|
"newCategoryShort": "Create",
|
||||||
|
"deleteCategory": "Delete Category",
|
||||||
|
"deleteCategoryConfirm": "Do you really want to remove the category named {category}",
|
||||||
|
"@deleteCategoryConfirm": {
|
||||||
|
"placeholders": {
|
||||||
|
"category": {
|
||||||
|
"type": "String",
|
||||||
|
"example": "fruit"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deleteCategoryLong": "Remove category and unlink items",
|
||||||
|
"changeCategoryColor": "Change category color",
|
||||||
|
"chooseCategoryColor": "Choose a color for your category",
|
||||||
|
"inputCategoryNameLabel": "Category Name",
|
||||||
|
"inputCategoryNameHint": "Name the category",
|
||||||
|
"inputCategoryNameHelp": "Categories can be used to sort your shopping list"
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart';
|
||||||
import 'package:outbag_app/backend/themes.dart';
|
import 'package:outbag_app/backend/themes.dart';
|
||||||
import 'package:outbag_app/backend/user.dart';
|
import 'package:outbag_app/backend/user.dart';
|
||||||
import 'package:outbag_app/backend/request.dart';
|
import 'package:outbag_app/backend/request.dart';
|
||||||
|
import 'package:outbag_app/screens/room/categories/edit.dart';
|
||||||
|
|
||||||
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
||||||
|
|
||||||
|
@ -28,9 +29,9 @@ void main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
final GlobalKey<NavigatorState> _rootNavigatorKey =
|
final GlobalKey<NavigatorState> _rootNavigatorKey =
|
||||||
GlobalKey<NavigatorState>(debugLabel: 'root');
|
GlobalKey<NavigatorState>(debugLabel: 'root');
|
||||||
final GlobalKey<NavigatorState> _userShellNavigatorKey =
|
final GlobalKey<NavigatorState> _userShellNavigatorKey =
|
||||||
GlobalKey<NavigatorState>(debugLabel: 'user');
|
GlobalKey<NavigatorState>(debugLabel: 'user');
|
||||||
|
|
||||||
class OutbagApp extends StatefulWidget {
|
class OutbagApp extends StatefulWidget {
|
||||||
const OutbagApp({super.key});
|
const OutbagApp({super.key});
|
||||||
|
@ -247,6 +248,19 @@ class _OutbagAppState extends State {
|
||||||
EditRoomPermissionSetPage(
|
EditRoomPermissionSetPage(
|
||||||
state.params['server'] ?? '',
|
state.params['server'] ?? '',
|
||||||
state.params['id'] ?? '')),
|
state.params['id'] ?? '')),
|
||||||
|
GoRoute(
|
||||||
|
name: 'new-category',
|
||||||
|
path: 'new-category',
|
||||||
|
builder: (context, state)=>EditCategoryPage(
|
||||||
|
state.params['server'] ?? '',
|
||||||
|
state.params['id'] ?? '')),
|
||||||
|
GoRoute(
|
||||||
|
name: 'edit-category',
|
||||||
|
path: 'edit-category/:cid',
|
||||||
|
builder: (context, state)=>EditCategoryPage(
|
||||||
|
state.params['server'] ?? '',
|
||||||
|
state.params['id'] ?? '',
|
||||||
|
id: int.tryParse(state.params['cid'] ?? ''))),
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
|
|
248
lib/screens/room/categories/edit.dart
Normal file
248
lib/screens/room/categories/edit.dart
Normal file
|
@ -0,0 +1,248 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:outbag_app/backend/request.dart';
|
||||||
|
import 'package:outbag_app/backend/room.dart';
|
||||||
|
import 'package:outbag_app/backend/user.dart';
|
||||||
|
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
||||||
|
import 'package:outbag_app/tools/snackbar.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
class EditCategoryPage extends StatefulWidget {
|
||||||
|
final String server;
|
||||||
|
final String tag;
|
||||||
|
int? id;
|
||||||
|
|
||||||
|
EditCategoryPage(this.server, this.tag, {super.key, this.id});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() => _EditCategoryPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _EditCategoryPageState extends State<EditCategoryPage> {
|
||||||
|
final TextEditingController _ctrName = TextEditingController();
|
||||||
|
ColorSwatch<int> _ctrColor = Colors.purple;
|
||||||
|
|
||||||
|
bool showSpinner = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) => fetchCategory());
|
||||||
|
}
|
||||||
|
|
||||||
|
void fetchCategory() {
|
||||||
|
final user = context.read<User>();
|
||||||
|
|
||||||
|
// TODO: load cached rooms
|
||||||
|
|
||||||
|
doNetworkRequest(ScaffoldMessenger.of(context),
|
||||||
|
req: () => postWithCreadentials(
|
||||||
|
credentials: user,
|
||||||
|
target: user.server,
|
||||||
|
path: 'getCategory',
|
||||||
|
body: {
|
||||||
|
'room': widget.tag,
|
||||||
|
'server': widget.server,
|
||||||
|
'listCatID': widget.id}),
|
||||||
|
onOK: (json) {
|
||||||
|
setState(() {
|
||||||
|
_ctrName.text = json['data']['title'];
|
||||||
|
_ctrColor = colorFromString(json['data']['color']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final textTheme = Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.apply(displayColor: Theme.of(context).colorScheme.onSurface);
|
||||||
|
|
||||||
|
double width = MediaQuery.of(context).size.width;
|
||||||
|
double height = MediaQuery.of(context).size.height;
|
||||||
|
double smallest = min(min(width, height), 400);
|
||||||
|
|
||||||
|
return showSpinner
|
||||||
|
? Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const CircularProgressIndicator(),
|
||||||
|
Text(AppLocalizations.of(context)!.loading,
|
||||||
|
style: textTheme.titleLarge),
|
||||||
|
])))
|
||||||
|
: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text((widget.id == null)
|
||||||
|
? AppLocalizations.of(context)!.newCategory
|
||||||
|
: AppLocalizations.of(context)!.editCategory),
|
||||||
|
),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
child: Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(14),
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(maxWidth: 400),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.square_rounded,
|
||||||
|
size: 48.0, color: _ctrColor),
|
||||||
|
tooltip: AppLocalizations.of(context)!
|
||||||
|
.changeCategoryColor,
|
||||||
|
onPressed: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (ctx) => AlertDialog(
|
||||||
|
title: Text(
|
||||||
|
AppLocalizations.of(context)!
|
||||||
|
.chooseCategoryColor),
|
||||||
|
actions: const [],
|
||||||
|
content: SizedBox(
|
||||||
|
width: smallest * 0.3 * 3,
|
||||||
|
height: smallest * 0.3 * 3,
|
||||||
|
child: GridView.count(
|
||||||
|
crossAxisCount: 3,
|
||||||
|
children: RoomCategory
|
||||||
|
.listColors()
|
||||||
|
.map((color) {
|
||||||
|
return GridTile(
|
||||||
|
child: IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Icons
|
||||||
|
.square_rounded,
|
||||||
|
color:
|
||||||
|
color,
|
||||||
|
size: 48.0),
|
||||||
|
// do not display tooltip for now
|
||||||
|
// as it is hard to translate
|
||||||
|
// and the tooltip prevented the click event,
|
||||||
|
// when clicked on the tooltip bar
|
||||||
|
// tooltip:icon.text,
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
_ctrColor =
|
||||||
|
color;
|
||||||
|
});
|
||||||
|
Navigator.of(
|
||||||
|
ctx)
|
||||||
|
.pop();
|
||||||
|
}));
|
||||||
|
}).toList())),
|
||||||
|
));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: TextField(
|
||||||
|
controller: _ctrName,
|
||||||
|
keyboardType: TextInputType.name,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
prefixIcon: const Icon(Icons.badge),
|
||||||
|
labelText: AppLocalizations.of(context)!
|
||||||
|
.inputCategoryNameLabel,
|
||||||
|
hintText: AppLocalizations.of(context)!
|
||||||
|
.inputCategoryNameHint,
|
||||||
|
helperText: AppLocalizations.of(context)!
|
||||||
|
.inputCategoryNameHelp,
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
))))),
|
||||||
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
|
onPressed: () async {
|
||||||
|
final scaffMgr = ScaffoldMessenger.of(context);
|
||||||
|
final router = GoRouter.of(context);
|
||||||
|
final trans = AppLocalizations.of(context);
|
||||||
|
|
||||||
|
// name may not be empty
|
||||||
|
if (_ctrName.text.isEmpty) {
|
||||||
|
showSimpleSnackbar(scaffMgr,
|
||||||
|
text: trans!.errorNoRoomName, action: trans.ok);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
showSpinner = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
final user = context.read<User>();
|
||||||
|
final color = colorIdFromColor(_ctrColor);
|
||||||
|
|
||||||
|
if (widget.id == null) {
|
||||||
|
await doNetworkRequest(scaffMgr,
|
||||||
|
req: () => postWithCreadentials(
|
||||||
|
target: user.server,
|
||||||
|
credentials: user,
|
||||||
|
path: 'addCategory',
|
||||||
|
body: {
|
||||||
|
'room': widget.tag,
|
||||||
|
'server': widget.server,
|
||||||
|
'title': _ctrName.text,
|
||||||
|
'color': color
|
||||||
|
}),
|
||||||
|
onOK: (body) async {
|
||||||
|
final id = body['data']['catID'];
|
||||||
|
|
||||||
|
final cat = RoomCategory(
|
||||||
|
id: id, name: _ctrName.text, color: _ctrColor);
|
||||||
|
// TODO: cache category
|
||||||
|
|
||||||
|
// go back
|
||||||
|
router.pop();
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
after: () {
|
||||||
|
setState(() {
|
||||||
|
showSpinner = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await doNetworkRequest(scaffMgr,
|
||||||
|
req: () => postWithCreadentials(
|
||||||
|
target: user.server,
|
||||||
|
credentials: user,
|
||||||
|
path: 'changeCategory',
|
||||||
|
body: {
|
||||||
|
'room': widget.tag,
|
||||||
|
'server': widget.server,
|
||||||
|
'title': _ctrName.text,
|
||||||
|
'listCatID': widget.id,
|
||||||
|
'color': color
|
||||||
|
}),
|
||||||
|
onOK: (body) async {
|
||||||
|
final cat = RoomCategory(
|
||||||
|
id: widget.id!,
|
||||||
|
name: _ctrName.text,
|
||||||
|
color: _ctrColor);
|
||||||
|
// TODO: cache category
|
||||||
|
|
||||||
|
// go back
|
||||||
|
router.pop();
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
after: () {
|
||||||
|
setState(() {
|
||||||
|
showSpinner = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
label: Text((widget.id == null)
|
||||||
|
? AppLocalizations.of(context)!.newCategoryShort
|
||||||
|
: AppLocalizations.of(context)!.editCategoryShort),
|
||||||
|
icon: Icon((widget.id == null)
|
||||||
|
? Icons.add : Icons.edit)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:outbag_app/backend/permissions.dart';
|
||||||
import 'package:outbag_app/backend/request.dart';
|
import 'package:outbag_app/backend/request.dart';
|
||||||
import 'package:outbag_app/backend/room.dart';
|
import 'package:outbag_app/backend/room.dart';
|
||||||
import 'package:outbag_app/backend/user.dart';
|
import 'package:outbag_app/backend/user.dart';
|
||||||
|
@ -51,33 +53,202 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final textTheme = Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.apply(displayColor: Theme.of(context).colorScheme.onSurface);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: ReorderableListView.builder(
|
body: ReorderableListView.builder(
|
||||||
|
buildDefaultDragHandles: false,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final item = list[index];
|
final item = list[index];
|
||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
key: Key('cat-${item.id}'),
|
key: Key('cat-${item.id}'),
|
||||||
leading: Icon(Icons.square_rounded, color: item.color),
|
leading: Icon(Icons.square_rounded, color: item.color),
|
||||||
|
trailing: ((widget.info?.isAdmin ?? false) ||
|
||||||
|
(widget.info?.isOwner ?? false) ||
|
||||||
|
((widget.info?.permissions)! &
|
||||||
|
RoomPermission.editRoomContent !=
|
||||||
|
0))
|
||||||
|
? ReorderableDragStartListener(
|
||||||
|
index: index,
|
||||||
|
child: const Icon(Icons.drag_handle)
|
||||||
|
)
|
||||||
|
: null,
|
||||||
title: Text(item.name),
|
title: Text(item.name),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// TODO show edit category popup
|
// TODO show edit category popup
|
||||||
// NOTE: maybe use ModalBottomSheet
|
// NOTE: maybe use ModalBottomSheet
|
||||||
// and show delete button in there
|
// and show delete button in there
|
||||||
|
|
||||||
|
if (!((widget.info?.isAdmin ?? false) ||
|
||||||
|
(widget.info?.isOwner ?? false) ||
|
||||||
|
((widget.info?.permissions)! &
|
||||||
|
RoomPermission.editRoomContent !=
|
||||||
|
0))) {
|
||||||
|
// user is not allowed to edit or delete categories
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => BottomSheet(
|
||||||
|
builder: (context) => Column(children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(Icons.square_rounded,
|
||||||
|
size: 48.0, color: item.color),
|
||||||
|
Text(item.name, style: textTheme.titleLarge)
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
// edit category
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.edit),
|
||||||
|
title: Text(AppLocalizations.of(context)!.editCategory),
|
||||||
|
subtitle:
|
||||||
|
Text(AppLocalizations.of(context)!.editCategoryLong),
|
||||||
|
trailing: const Icon(Icons.chevron_right),
|
||||||
|
onTap: () {
|
||||||
|
// close the modal bottom sheet
|
||||||
|
// so the user returns to the list,
|
||||||
|
// when leaving the category editor
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
|
||||||
|
// launch category editor
|
||||||
|
context.pushNamed('edit-category', params: {
|
||||||
|
'server': widget.room!.serverTag,
|
||||||
|
'id': widget.room!.id,
|
||||||
|
'cid': item.id.toString()
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// delete category
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.delete),
|
||||||
|
title: Text(AppLocalizations.of(context)!.deleteCategory),
|
||||||
|
subtitle: Text(
|
||||||
|
AppLocalizations.of(context)!.deleteCategoryLong),
|
||||||
|
trailing: const Icon(Icons.chevron_right),
|
||||||
|
onTap: () {
|
||||||
|
// show popup
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (ctx) => AlertDialog(
|
||||||
|
icon: const Icon(Icons.delete),
|
||||||
|
title: Text(AppLocalizations.of(context)!
|
||||||
|
.deleteCategory),
|
||||||
|
content: Text(AppLocalizations.of(context)!
|
||||||
|
.deleteCategoryConfirm(item.name)),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
// close popup
|
||||||
|
Navigator.of(ctx).pop();
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
AppLocalizations.of(context)!.cancel),
|
||||||
|
),
|
||||||
|
FilledButton(
|
||||||
|
onPressed: () async {
|
||||||
|
// send request
|
||||||
|
final scaffMgr =
|
||||||
|
ScaffoldMessenger.of(ctx);
|
||||||
|
// popup context
|
||||||
|
final navInner = Navigator.of(ctx);
|
||||||
|
// bottomsheet context
|
||||||
|
final nav = Navigator.of(context);
|
||||||
|
final user = context.read<User>();
|
||||||
|
|
||||||
|
doNetworkRequest(scaffMgr,
|
||||||
|
req: () => postWithCreadentials(
|
||||||
|
path: 'deleteCategory',
|
||||||
|
target: user.server,
|
||||||
|
body: {
|
||||||
|
'room': widget.room?.id,
|
||||||
|
'server':
|
||||||
|
widget.room?.serverTag,
|
||||||
|
'listCatID': item.id
|
||||||
|
},
|
||||||
|
credentials: user),
|
||||||
|
onOK: (_) async {
|
||||||
|
// TODO: remove cached category
|
||||||
|
},
|
||||||
|
after: () {
|
||||||
|
// close popup
|
||||||
|
navInner.pop();
|
||||||
|
// close modal bottom sheet
|
||||||
|
nav.pop();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Text(AppLocalizations.of(context)!
|
||||||
|
.deleteCategory),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
onClosing: () {},
|
||||||
|
),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
itemCount: list.length,
|
itemCount: list.length,
|
||||||
onReorder: (int start, int current) {},
|
onReorder: (int oldIndex, int newIndex) {
|
||||||
|
|
||||||
|
if (!((widget.info?.isAdmin ?? false) ||
|
||||||
|
(widget.info?.isOwner ?? false) ||
|
||||||
|
((widget.info?.permissions)! &
|
||||||
|
RoomPermission.editRoomContent !=
|
||||||
|
0))) {
|
||||||
|
// user is not allowed to edit or delete categories
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
if (oldIndex < newIndex) {
|
||||||
|
newIndex -= 1;
|
||||||
|
}
|
||||||
|
final item = list.removeAt(oldIndex);
|
||||||
|
list.insert(newIndex, item);
|
||||||
|
|
||||||
|
// network request
|
||||||
|
final user = context.read<User>();
|
||||||
|
doNetworkRequest(ScaffoldMessenger.of(context),
|
||||||
|
req: () => postWithCreadentials(
|
||||||
|
credentials: user,
|
||||||
|
target: user.server,
|
||||||
|
path: 'changeCategoriesOrder',
|
||||||
|
body: {
|
||||||
|
'room': widget.room?.id,
|
||||||
|
'server': widget.room?.serverTag,
|
||||||
|
'listCatIDs': list.map((item) => item.id).toList()
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
},
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton.extended(
|
floatingActionButton: ((widget.info?.isAdmin ?? false) ||
|
||||||
|
(widget.info?.isOwner ?? false) ||
|
||||||
|
((widget.info?.permissions)! &
|
||||||
|
RoomPermission.editRoomContent !=
|
||||||
|
0))?FloatingActionButton.extended(
|
||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
||||||
label: Text(AppLocalizations.of(context)!.newCategoryShort),
|
label: Text(AppLocalizations.of(context)!.newCategoryShort),
|
||||||
tooltip: AppLocalizations.of(context)!.newCategoryLong,
|
tooltip: AppLocalizations.of(context)!.newCategoryLong,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// TODO show new category popup
|
// show new category popup
|
||||||
|
context.pushNamed('new-category', params: {
|
||||||
|
'server': widget.room!.serverTag,
|
||||||
|
'id': widget.room!.id,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
),
|
):null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue