feat(realtime): Implement real-time updates for shopping list

Introduces WebSocket-based real-time updates for the shopping list.
Changes to items (add, mark, delete) are now instantly reflected
across all connected user sessions without requiring a page refresh.

This commit:
- Extends the WebSocket ConnectionManager to broadcast item updates.
- Modifies item manipulation endpoints (add, mark, delete) to trigger broadcasts.
- Updates the frontend to listen for update broadcasts and refresh the list.
- Updates README.md to reflect the new real-time update feature.
This commit is contained in:
2025-11-05 13:42:07 +01:00
parent c627813296
commit a7d88be89c
3 changed files with 13 additions and 1 deletions

View File

@@ -11,7 +11,7 @@ This web service is the final development step for the accompanying **Android ap
## Features
* **Multi-user access:** Log in to manage the list. Includes an admin panel for user management.
* **Real-time updates:** The list updates in real-time for all connected users.
* **Real-time updates:** Changes to the shopping list (add, mark, delete) are instantly reflected across all connected user sessions.
* **Automatic Language Detection:** The user interface automatically adapts to your browser's language. Currently, English and German are supported. Simply refresh the page after changing your browser or OS language settings.
* **Gotify Notifications:** Trigger notifications to your Gotify server.
* **Suggestion Box:** Get suggestions for items as you type.

10
main.py
View File

@@ -89,6 +89,10 @@ class ConnectionManager:
for connection in self.active_connections:
await connection.send_json({"type": "user_list", "users": user_list})
async def broadcast_update(self):
for connection in self.active_connections:
await connection.send_json({"type": "update"})
manager = ConnectionManager()
@@ -501,8 +505,11 @@ async def add_item(item: Item, current_user: UserInDB = Depends(get_current_acti
conn.close()
if added_count > 0:
await manager.broadcast_update()
conn.close()
return {"status": "ok", "added": added_count}
else:
conn.close()
return {"status": "exists"}
@@ -512,6 +519,7 @@ async def mark_item(item_id: int, current_user: User = Depends(get_current_activ
cursor = conn.cursor()
cursor.execute("UPDATE items SET marked = NOT marked WHERE id = ?", (item_id,))
conn.commit()
await manager.broadcast_update()
conn.close()
return {"status": "ok"}
@@ -538,6 +546,7 @@ async def delete_marked_items(request: Optional[DeletionRequest] = None, current
cursor = conn.cursor()
cursor.execute("DELETE FROM items WHERE marked = 1")
conn.commit()
await manager.broadcast_update()
conn.close()
return {"status": "ok"}
@@ -585,6 +594,7 @@ async def delete_item(item_id: int, current_user: User = Depends(get_current_act
cursor.execute("DELETE FROM items WHERE id = ?", (item_id,))
conn.commit()
conn.close()
await manager.broadcast_update()
return {"status": "ok"}

View File

@@ -493,6 +493,8 @@
} else {
onlineUsersContainer.style.display = 'none';
}
} else if (data.type === 'update') {
fetchItems();
}
};