notification links
All checks were successful
/ upload (release) Successful in 35s

This commit is contained in:
pavel 2026-02-12 21:26:41 +01:00
commit 42a78c529d
5 changed files with 74 additions and 5 deletions

View file

@ -1,4 +1,4 @@
const CACHE_NAME = 'agency-cache-v2'; const CACHE_NAME = 'agency-cache-v3';
const ASSETS = [ const ASSETS = [
'/', '/',
'/index.html', '/index.html',
@ -34,8 +34,16 @@ self.addEventListener('fetch', (event) => {
}) })
); );
}); });
self.addEventListener('push', (event) => { self.addEventListener('push', (event) => {
const data = event.data ? event.data.json() : { title: 'Notification', body: 'New update from Agency' }; let data = { title: 'Notification', body: 'New update from Agency' };
try {
if (event.data) {
data = event.data.json();
}
} catch (e) {
console.error('Error parsing push data:', e);
}
const options = { const options = {
body: data.body, body: data.body,
@ -44,7 +52,9 @@ self.addEventListener('push', (event) => {
vibrate: [100, 50, 100], vibrate: [100, 50, 100],
data: { data: {
dateOfArrival: Date.now(), dateOfArrival: Date.now(),
primaryKey: '1' primaryKey: '1',
taskId: data.task_id,
runId: data.run_id
} }
}; };
@ -55,7 +65,27 @@ self.addEventListener('push', (event) => {
self.addEventListener('notificationclick', (event) => { self.addEventListener('notificationclick', (event) => {
event.notification.close(); event.notification.close();
const taskId = event.notification.data.taskId;
const runId = event.notification.data.runId;
let url = '/';
if (taskId && runId) {
url = `/?taskId=${taskId}&runId=${runId}`;
}
event.waitUntil( event.waitUntil(
clients.openWindow('/') clients.matchAll({ type: 'window', includeUncontrolled: true }).then((windowClients) => {
// Check if there is already a window open and focus it, or open a new one
for (let client of windowClients) {
if ('focus' in client) {
// Navigate the existing client to the new URL if it's the same app
return client.navigate(url).then(c => c.focus());
}
}
if (clients.openWindow) {
return clients.openWindow(url);
}
})
); );
}); });

View file

@ -159,6 +159,16 @@ async function fetchTasks() {
const response = await fetchWithAuth(`${API_URL}/tasks`); const response = await fetchWithAuth(`${API_URL}/tasks`);
const newTasks = await response.json(); const newTasks = await response.json();
// Check for deep link in URL
const params = new URLSearchParams(window.location.search);
const urlTaskId = params.get('taskId');
const urlRunId = params.get('runId');
if (urlTaskId && !state.selectedTaskId) {
state.selectedTaskId = urlTaskId;
state.selectedRunId = urlRunId;
}
// Check if we should follow the latest run // Check if we should follow the latest run
let newSelectedRunId = state.selectedRunId; let newSelectedRunId = state.selectedRunId;
if (state.selectedTaskId) { if (state.selectedTaskId) {
@ -179,6 +189,12 @@ async function fetchTasks() {
tasks: newTasks, tasks: newTasks,
selectedRunId: newSelectedRunId selectedRunId: newSelectedRunId
}); });
// If we just loaded from a deep link, clear the params and select it
if (urlTaskId) {
window.history.replaceState({}, document.title, "/");
selectTask(urlTaskId, urlRunId);
}
} catch (error) { } catch (error) {
console.error('Error fetching tasks:', error); console.error('Error fetching tasks:', error);
} }
@ -806,6 +822,16 @@ async function initializeApp() {
if (hasSession) { if (hasSession) {
appEl.classList.remove('hidden'); appEl.classList.remove('hidden');
loginOverlay.classList.add('hidden'); loginOverlay.classList.add('hidden');
// Handle deep links from notifications
const params = new URLSearchParams(window.location.search);
const taskId = params.get('taskId');
const runId = params.get('runId');
if (taskId) {
state.selectedTaskId = taskId;
state.selectedRunId = runId;
}
await fetchTasks(); await fetchTasks();
connectWebSocket(); connectWebSocket();
} else { } else {

View file

@ -1,5 +1,6 @@
use crate::error::AppResult; use crate::error::AppResult;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use uuid::Uuid;
use web_push::*; use web_push::*;
pub struct PushSender { pub struct PushSender {
@ -56,6 +57,8 @@ impl PushSender {
subscription: &PushSubscription, subscription: &PushSubscription,
title: &str, title: &str,
body: &str, body: &str,
task_id: Option<Uuid>,
run_id: Option<Uuid>,
) -> AppResult<()> { ) -> AppResult<()> {
let subscription_info = SubscriptionInfo::new( let subscription_info = SubscriptionInfo::new(
subscription.endpoint.clone(), subscription.endpoint.clone(),
@ -81,6 +84,8 @@ impl PushSender {
let payload = serde_json::to_vec(&serde_json::json!({ let payload = serde_json::to_vec(&serde_json::json!({
"title": title, "title": title,
"body": body, "body": body,
"task_id": task_id,
"run_id": run_id,
})) }))
.map_err(|e| crate::error::AppError::Internal(e.to_string()))?; .map_err(|e| crate::error::AppError::Internal(e.to_string()))?;

View file

@ -171,7 +171,13 @@ pub async fn execute_agent_run(
tokio::spawn(async move { tokio::spawn(async move {
if let Err(e) = sender if let Err(e) = sender
.send_notification(&sub_data, &format!("Task Completed: {}", status), &goal) .send_notification(
&sub_data,
&format!("Task Completed: {}", status),
&goal,
Some(task_id),
Some(run_id),
)
.await .await
{ {
tracing::error!("Failed to send notification in background task: {}", e); tracing::error!("Failed to send notification in background task: {}", e);

View file

@ -201,6 +201,8 @@ impl Scheduler {
&sub_data, &sub_data,
&format!("Scheduled Task Completed: {}", status), &format!("Scheduled Task Completed: {}", status),
&goal, &goal,
Some(task_id),
Some(run_id),
) )
.await; .await;
}); });