Add unread indication to subscription cards

This commit is contained in:
Sami Abuzakuk
2025-10-14 20:23:21 +02:00
parent 6460cab465
commit 3c6acc9359
7 changed files with 46 additions and 13 deletions

1
.gitignore vendored
View File

@@ -3,3 +3,4 @@ __pycache__
*.db *.db
.envrc .envrc
exec_folder/ exec_folder/
.env

View File

@@ -52,15 +52,6 @@ def hello():
return {"message": "Welcome to the Project Monitor API"} return {"message": "Welcome to the Project Monitor API"}
# Subscriptions API Endpoints
@app.get("/subscriptions")
def list_subscriptions():
db = SessionLocal()
subscriptions = db.query(Subscription).all()
db.close()
return subscriptions
class SubscriptionCreate(BaseModel): class SubscriptionCreate(BaseModel):
topic: str topic: str
@@ -69,10 +60,33 @@ class SubscriptionResponse(BaseModel):
id: int id: int
topic: str topic: str
created_at: datetime created_at: datetime
has_unread: bool
model_config = {"from_attributes": True} model_config = {"from_attributes": True}
# Subscriptions API Endpoints
@app.get("/subscriptions", response_model=list[SubscriptionResponse])
def list_subscriptions():
db = SessionLocal()
subscriptions = db.query(Subscription).all()
# TODO: find a better way to do this
for subscription in subscriptions:
not_viewed_count = (
db.query(Notification)
.filter(
Notification.subscription_id == subscription.id,
~Notification.viewed,
)
.count()
)
subscription.has_unread = not_viewed_count > 0
db.close()
return subscriptions
@app.get("/subscriptions/{subscription_id}", response_model=SubscriptionResponse) @app.get("/subscriptions/{subscription_id}", response_model=SubscriptionResponse)
def get_subscription(subscription_id: int): def get_subscription(subscription_id: int):
db = SessionLocal() db = SessionLocal()
@@ -82,6 +96,17 @@ def get_subscription(subscription_id: int):
if not subscription: if not subscription:
db.close() db.close()
raise HTTPException(status_code=404, detail="Subscription not found") raise HTTPException(status_code=404, detail="Subscription not found")
# checking if subscription has unread messages
subscription.has_unread = (
db.query(Notification)
.filter(
Notification.subscription_id == subscription_id and not Notification.viewed
)
.count()
> 0
)
db.close() db.close()
return subscription return subscription

View File

@@ -18,6 +18,7 @@ def fetch_ntfy_notifications(base_url, subscriptions):
notifications = [] notifications = []
for subscription in subscriptions: for subscription in subscriptions:
print(f"Fetching notifications for {subscription.topic}")
topic = subscription.topic topic = subscription.topic
last_message_id = subscription.last_message_id last_message_id = subscription.last_message_id
since_param = "all" if last_message_id is None else last_message_id since_param = "all" if last_message_id is None else last_message_id

View File

@@ -6,7 +6,10 @@ from sqlalchemy.sql.sqltypes import DateTime
import os import os
# Initialize the database # Initialize the database
DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///./project_monitor.db") DATABASE_URL = os.getenv("DATABASE_URL")
if not DATABASE_URL:
raise ValueError("DATABASE_URL environment variable is not set")
# SQLAlchemy setup # SQLAlchemy setup
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False}) engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})

View File

@@ -1,6 +1,6 @@
import { env } from '$env/dynamic/public'; import { env } from '$env/dynamic/public';
export const API_URL = env.PUBLIC_API_URL || 'http://localhost:8080'; export const API_URL = env.PUBLIC_API_URL || 'http://localhost:8000';
/** /**
* Type definitions for Subscriptions and Notifications * Type definitions for Subscriptions and Notifications
@@ -9,6 +9,7 @@ export interface Subscription {
id: number; id: number;
topic: string; topic: string;
created_at: string; created_at: string;
has_unread: boolean;
} }
export interface Notification { export interface Notification {

View File

@@ -41,7 +41,6 @@
<div class="container mx-auto flex justify-between items-center p-4"> <div class="container mx-auto flex justify-between items-center p-4">
<a href="/" class="text-2xl font-bold hover:text-gray-400">Project Monitor</a> <a href="/" class="text-2xl font-bold hover:text-gray-400">Project Monitor</a>
<div class="flex space-x-6"> <div class="flex space-x-6">
<a href="/" class="text-lg hover:text-gray-400">Home</a>
<a href="/scripts" class="text-lg hover:text-gray-400">Scripts</a> <a href="/scripts" class="text-lg hover:text-gray-400">Scripts</a>
<a href="/notifications" class="text-lg hover:text-gray-400">Notifications</a> <a href="/notifications" class="text-lg hover:text-gray-400">Notifications</a>
<a href="/settings" class="text-lg hover:text-gray-400"> <a href="/settings" class="text-lg hover:text-gray-400">

View File

@@ -39,8 +39,11 @@
{#each subscriptions as subscription (subscription.id)} {#each subscriptions as subscription (subscription.id)}
<a <a
href={`/notifications/${subscription.id}`} href={`/notifications/${subscription.id}`}
class="block p-4 border rounded bg-white hover:bg-gray-100 shadow-lg shadow-blue-500/50" class="relative block p-4 border rounded bg-white hover:bg-gray-100 shadow-lg shadow-blue-500/50"
> >
{#if subscription.has_unread}
<span class="absolute top-2 right-2 w-3 h-3 bg-green-500 rounded-full"></span>
{/if}
<h2 class="text-lg font-semibold text-gray-800">{subscription.topic}</h2> <h2 class="text-lg font-semibold text-gray-800">{subscription.topic}</h2>
</a> </a>
{/each} {/each}