diff --git a/lib/pages/dashboard_page.dart b/lib/pages/dashboard_page.dart index 5692fc7..8cd250a 100644 --- a/lib/pages/dashboard_page.dart +++ b/lib/pages/dashboard_page.dart @@ -2,6 +2,7 @@ import 'package:briessenchecker/models/checklist.dart'; import 'package:briessenchecker/pages/detail_checklist_page.dart'; import 'package:briessenchecker/services/checklist_provider.dart'; import 'package:briessenchecker/services/dbhelper.dart'; +import 'package:briessenchecker/services/scaffold_messenger.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -87,8 +88,12 @@ class _DashboardPageState extends State { } void _onDeleteTapped() { - DbHelper.deleteChecklistByid( - checklists.elementAt(_selectedChecklistIndex!).id); + final checklist = checklists.elementAt(_selectedChecklistIndex!); + DbHelper.deleteChecklistByid(checklist.id); + if (!DbHelper.isOwner(checklist.ownerId)) { + Messenger.showError( + context, 'Can\'t delete checklists that aren\'t yours'); + } setState(() { _selectedChecklistIndex = null; }); diff --git a/lib/pages/detail_checklist_page.dart b/lib/pages/detail_checklist_page.dart index cf9b6e8..ab16907 100644 --- a/lib/pages/detail_checklist_page.dart +++ b/lib/pages/detail_checklist_page.dart @@ -2,9 +2,11 @@ import 'package:briessenchecker/models/checklist.dart'; import 'package:briessenchecker/models/listitem.dart'; import 'package:briessenchecker/services/checklist_provider.dart'; import 'package:briessenchecker/services/dbhelper.dart'; +import 'package:briessenchecker/widgets/item_detail_dialog.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import '../services/scaffold_messenger.dart'; import '../widgets/item_list_tile.dart'; class DetailChecklistPage extends StatefulWidget { @@ -78,69 +80,21 @@ class _DetailChecklistPageState extends State { void _addItemTapped() { showDialog( context: context, - builder: (context) => _itemDetailDialog(context, null)); + builder: (context) => ItemDetailDialog(checklistId: _checklist!.id)); } void _itemTapped(int index) { _selectedItemId = _items.elementAt(index).id; showDialog( context: context, - builder: (context) => _itemDetailDialog(context, index)).whenComplete( + builder: (context) => ItemDetailDialog( + checklistId: _checklist!.id, + item: _items.elementAt(index), + )).whenComplete( () => _selectedItemId = null, ); } - Widget _itemDetailDialog(BuildContext context, int? index) { - TextEditingController titleCon = TextEditingController(); - TextEditingController descCon = TextEditingController(); - if (_selectedItemId != null) { - final item = _items.elementAt(index!); - titleCon.text = item.title; - descCon.text = item.description; - } - return AlertDialog( - title: const Text('additem'), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - TextFormField( - controller: titleCon, - textInputAction: TextInputAction.next, - decoration: const InputDecoration( - label: Text('Title'), - ), - ), - TextFormField( - controller: descCon, - onFieldSubmitted: (value) => - _itemSaved(titleCon.text, descCon.text), - decoration: const InputDecoration( - label: Text('Description'), - ), - ), - ], - ), - actions: [ - TextButton( - onPressed: () { - _itemSaved(titleCon.text, descCon.text); - }, - child: const Text('save'), - ), - ], - ); - } - - void _itemSaved(String title, String description) { - DbHelper.addOrUpdateItem( - _checklistProvider.selectedChecklistId!, - title, - description, - _selectedItemId, - ); - Navigator.of(context).pop(); - } - Widget? _itemListBuilder(BuildContext context, int index) { Item item = _items.elementAt(index); return ItemListTile( @@ -170,8 +124,17 @@ class _DetailChecklistPageState extends State { void _onDeleteItemsPressed() { List itemIds = []; + bool showErrorPrompt = false; for (final itemIndex in selectedItemIndexes) { itemIds.add(_items.elementAt(itemIndex).id!); + if (!showErrorPrompt) { + showErrorPrompt = + !DbHelper.isOwner(_items.elementAt(itemIndex).ownerId) && + !DbHelper.isOwner(_checklist!.ownerId); + } + } + if (showErrorPrompt) { + Messenger.showError(context, 'Can\'t delete items that aren\'t yours.'); } DbHelper.deleteItemsById(itemIds); setState(() { diff --git a/lib/services/dbhelper.dart b/lib/services/dbhelper.dart index 6cc1c43..bd6c606 100644 --- a/lib/services/dbhelper.dart +++ b/lib/services/dbhelper.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:briessenchecker/services/checklist_provider.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -185,11 +187,15 @@ class DbHelper { } static Future deleteItemById(int id) async { - await _client.from(itemsTableName).delete().eq('id', id); + await _client.from(itemsTableName).delete().eq('id', id).onError(_onError); } static Future deleteItemsById(List ids) async { - await _client.from(itemsTableName).delete().in_('id', ids); + await _client + .from(itemsTableName) + .delete() + .in_('id', ids) + .onError(_onError); } static Future deleteChecklistByid(int id) async { @@ -201,6 +207,12 @@ class DbHelper { static Stream get authChangeEventStream => _client.auth.onAuthStateChange; + static bool isOwner(String id) { + return _client.auth.currentSession!.user.id == id; + } + + static User? get currentUser => _client.auth.currentUser; + static Stream>> get checklistChangeEventStream => _client.from(checklistsTableName).stream(primaryKey: ['id']); @@ -245,4 +257,8 @@ class DbHelper { } return items; } + + static FutureOr _onError(Object error, StackTrace stackTrace) { + print(error); + } } diff --git a/lib/services/scaffold_messenger.dart b/lib/services/scaffold_messenger.dart new file mode 100644 index 0000000..7c1387e --- /dev/null +++ b/lib/services/scaffold_messenger.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +class Messenger { + static void showError(BuildContext context, String error) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + backgroundColor: Theme.of(context).colorScheme.error, + content: Text( + error, + style: TextStyle(color: Theme.of(context).colorScheme.onError), + ), + ), + ); + } +} diff --git a/lib/widgets/item_detail_dialog.dart b/lib/widgets/item_detail_dialog.dart new file mode 100644 index 0000000..1282719 --- /dev/null +++ b/lib/widgets/item_detail_dialog.dart @@ -0,0 +1,77 @@ +import 'package:flutter/material.dart'; + +import '../models/listitem.dart'; +import '../services/dbhelper.dart'; + +class ItemDetailDialog extends StatelessWidget { + const ItemDetailDialog({ + super.key, + this.item, + required this.checklistId, + }); + final Item? item; + final int checklistId; + + @override + Widget build(BuildContext context) { + final TextEditingController titleCon = TextEditingController(); + final TextEditingController descCon = TextEditingController(); + bool isOwner = true; + String dialogTitle = 'Add item'; + + if (item != null) { + titleCon.text = item!.title; + descCon.text = item!.description; + isOwner = DbHelper.isOwner(item!.ownerId); + isOwner ? dialogTitle = 'Edit Item' : dialogTitle = item!.title; + } + + return AlertDialog( + title: Text(dialogTitle), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (isOwner) + TextFormField( + controller: titleCon, + textInputAction: TextInputAction.next, + decoration: const InputDecoration( + label: Text('Title'), + ), + ), + if (isOwner) + TextFormField( + controller: descCon, + onFieldSubmitted: (value) { + _itemSaved(titleCon.text, descCon.text); + Navigator.of(context).pop(); + }, + decoration: const InputDecoration( + label: Text('Description'), + ), + ), + if (!isOwner) Text(item!.description), + ], + ), + actions: [ + TextButton( + onPressed: () { + _itemSaved(titleCon.text, descCon.text); + Navigator.of(context).pop(); + }, + child: const Text('save'), + ), + ], + ); + } + + void _itemSaved(String title, String description) { + DbHelper.addOrUpdateItem( + checklistId, + title, + description, + item?.id, + ); + } +}