Skip to content

Commit

Permalink
feature: Add real-time synchronization to shopping lists
Browse files Browse the repository at this point in the history
Adding or removing shopping lists now immediately
updates them in the available shopping list selector
in the "add items" overview.
  • Loading branch information
RedX2501 authored and TomBursch committed Dec 13, 2024
1 parent 566c9eb commit 3015952
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 16 deletions.
19 changes: 17 additions & 2 deletions backend/app/controller/shoppinglist/shoppinglist_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,17 @@
@authorize_household()
@validate_args(CreateList)
def createShoppinglist(args, household_id):
return jsonify(
Shoppinglist(name=args["name"], household_id=household_id).save().obj_to_dict()
shoppinglist = Shoppinglist(name=args["name"], household_id=household_id)
shoppinglist.save()
shoppinglist_dict = shoppinglist.obj_to_dict()
socketio.emit(
"shoppinglist:add",
{
"shoppinglist": shoppinglist_dict
},
to=household_id
)
return jsonify(shoppinglist_dict)


@shoppinglistHousehold.route("", methods=["GET"])
Expand Down Expand Up @@ -108,6 +116,13 @@ def deleteShoppinglist(id):
if shoppinglist.isDefault():
raise InvalidUsage()
shoppinglist.delete()
socketio.emit(
"shoppinglist:delete",
{
"shoppinglist": shoppinglist.obj_to_dict()
},
to=shoppinglist.household_id
)

return jsonify({"msg": "DONE"})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ class HouseholdUpdateCubit
ApiService.getInstance()
.getSupportedLanguages()
.then((value) => emit(state.copyWith(supportedLanguages: value)));
ApiService.getInstance().onShoppinglistAdd(onShoppinglistAdd);
ApiService.getInstance().onShoppinglistDelete(onShoppinglistDelete);
}

void onShoppinglistAdd(dynamic data) {
refresh();
}

void onShoppinglistDelete(dynamic data) {
refresh();
}

Future<void> refresh() async {
Expand Down
44 changes: 30 additions & 14 deletions kitchenowl/lib/cubits/shoppinglist_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,30 @@ class ShoppinglistCubit extends Cubit<ShoppinglistCubitState> {
});
_initialLoad();
refresh();
ApiService.getInstance().onShoppinglistAdd(onShoppinglistAdd);
ApiService.getInstance().onShoppinglistDelete(onShoppinglistDelete);
ApiService.getInstance().onShoppinglistItemAdd(onShoppinglistItemAdd);
ApiService.getInstance().onShoppinglistItemRemove(onShoppinglistItemRemove);
}

@override
Future<void> close() async {
ApiService.getInstance().offShoppinglistAdd(onShoppinglistAdd);
ApiService.getInstance().offShoppinglistDelete(onShoppinglistDelete);
ApiService.getInstance().offShoppinglistItemAdd(onShoppinglistItemAdd);
ApiService.getInstance()
.offShoppinglistItemRemove(onShoppinglistItemRemove);
super.close();
}

void onShoppinglistAdd(dynamic data) {
refresh();
}

void onShoppinglistDelete(dynamic data) {
refresh();
}

void onShoppinglistItemAdd(dynamic data) {
final item = ShoppinglistItem.fromJson(data["item"]);
TransactionHandler.getInstance().runTransaction(
Expand Down Expand Up @@ -289,16 +301,29 @@ class ShoppinglistCubit extends Cubit<ShoppinglistCubitState> {
return _refreshThread!;
}

Future<void> _initialLoad() async {
Future<Map<int, ShoppingList>> fetchShoppingLists(bool forceOffline) async {
final shoppingLists = await TransactionHandler.getInstance()
.runTransaction(
TransactionShoppingListGet(household: household),
forceOffline: true,
forceOffline: forceOffline,
)
.then((lists) => Map.fromEntries(lists
.map((e) => e.id != null ? MapEntry(e.id!, e) : null)
.nonNulls));

return shoppingLists;
}

Future<List<Category>> fetchCategories(bool forceOffline) {
return TransactionHandler.getInstance().runTransaction(
TransactionCategoriesGet(household: household),
forceOffline: true,
);
}

Future<void> _initialLoad() async {
final shoppingLists = await fetchShoppingLists(true);

ShoppingList? shoppingList = state.selectedShoppinglist;
if (await PreferenceStorage.getInstance()
.readBool(key: "restoreLastShoppingList") ??
Expand All @@ -314,11 +339,7 @@ class ShoppinglistCubit extends Cubit<ShoppinglistCubitState> {

if (shoppingList == null) return;

Future<List<Category>> categories =
TransactionHandler.getInstance().runTransaction(
TransactionCategoriesGet(household: household),
forceOffline: true,
);
Future<List<Category>> categories = fetchCategories(true);

final resState = LoadingShoppinglistCubitState(
shoppinglists: shoppingLists,
Expand Down Expand Up @@ -353,11 +374,7 @@ class ShoppinglistCubit extends Cubit<ShoppinglistCubitState> {
));
}

final shoppingLists = await TransactionHandler.getInstance()
.runTransaction(TransactionShoppingListGet(household: household))
.then((lists) => Map.fromEntries(lists
.map((e) => e.id != null ? MapEntry(e.id!, e) : null)
.nonNulls));
final shoppingLists = await fetchShoppingLists(false);

int? selectedShoppinglistId = state.selectedShoppinglistId;
if (selectedShoppinglistId != null &&
Expand All @@ -370,8 +387,7 @@ class ShoppinglistCubit extends Cubit<ShoppinglistCubitState> {

final shoppinglist = shoppingLists[selectedShoppinglistId];

Future<List<Category>> categories = TransactionHandler.getInstance()
.runTransaction(TransactionCategoriesGet(household: household));
Future<List<Category>> categories = fetchCategories(false);

if (query != null && query.isNotEmpty) {
// Split query into name and description
Expand Down
16 changes: 16 additions & 0 deletions kitchenowl/lib/services/api/shoppinglist.dart
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,22 @@ extension ShoppinglistApi on ApiService {
return res.statusCode == 200;
}

void onShoppinglistAdd(dynamic Function(dynamic) handler){
socket.on("shoppinglist:add", handler);
}

void offShoppinglistAdd(dynamic Function(dynamic) handler){
socket.off("shoppinglist:add", handler);
}

void onShoppinglistDelete(dynamic Function(dynamic) handler){
socket.on("shoppinglist:delete", handler);
}

void offShoppinglistDelete(dynamic Function(dynamic) handler){
socket.off("shoppinglist:delete", handler);
}

void onShoppinglistItemAdd(dynamic Function(dynamic) handler) {
socket.on("shoppinglist_item:add", handler);
}
Expand Down

0 comments on commit 3015952

Please sign in to comment.