Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 17 additions & 17 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -968,19 +968,19 @@ pub fn run() {
});

let show_item =
MenuItem::with_id(app, "show", "Mostra Presto", true, None::<&str>)?;
MenuItem::with_id(app, "show", "Show Presto", true, None::<&str>)?;
let start_session_item = MenuItem::with_id(
app,
"start_session",
"Inizia sessione",
"Start Session",
false,
None::<&str>,
)?;
let pause_item = MenuItem::with_id(app, "pause", "Pausa", false, None::<&str>)?;
let pause_item = MenuItem::with_id(app, "pause", "Pause", false, None::<&str>)?;
let skip_item =
MenuItem::with_id(app, "skip", "Salta sessione", false, None::<&str>)?;
let cancel_item = MenuItem::with_id(app, "cancel", "Annulla", false, None::<&str>)?;
let quit_item = MenuItem::with_id(app, "quit", "Esci", true, None::<&str>)?;
MenuItem::with_id(app, "skip", "Skip Session", false, None::<&str>)?;
let cancel_item = MenuItem::with_id(app, "cancel", "Cancel", false, None::<&str>)?;
let quit_item = MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)?;
let menu = Menu::with_items(
app,
&[
Expand Down Expand Up @@ -1285,43 +1285,43 @@ async fn update_tray_menu(
let tray = app.tray_by_id("main");

if let Some(tray) = tray {
let show_item = MenuItem::with_id(&app, "show", "Mostra Presto", true, None::<&str>)
let show_item = MenuItem::with_id(&app, "show", "Show Presto", true, None::<&str>)
.map_err(|e| format!("Failed to create show item: {}", e))?;

// Inizia sessione: abilitato solo se non è in esecuzione
// Start Session: enabled only if not running
let start_session_item = MenuItem::with_id(
&app,
"start_session",
"Inizia sessione",
"Start Session",
!is_running,
None::<&str>,
)
.map_err(|e| format!("Failed to create start session item: {}", e))?;

// Pausa: abilitata solo se è in esecuzione e non in pausa
// Pause: enabled only if running and not paused
let pause_item = MenuItem::with_id(
&app,
"pause",
"Pausa",
"Pause",
is_running && !is_paused,
None::<&str>,
)
.map_err(|e| format!("Failed to create pause item: {}", e))?;

// Skip: abilitato solo se è in esecuzione
let skip_item = MenuItem::with_id(&app, "skip", "Salta sessione", is_running, None::<&str>)
// Skip: enabled only if running
let skip_item = MenuItem::with_id(&app, "skip", "Skip Session", is_running, None::<&str>)
.map_err(|e| format!("Failed to create skip item: {}", e))?;

// Annulla: abilitato se è in modalità focus, disabilitato in break/longBreak (undo)
// Cancel: enabled if in focus mode, disabled in break/longBreak (undo)
let cancel_text = if current_mode == "focus" {
"Annulla"
"Cancel"
} else {
"Annulla ultima"
"Cancel Last"
};
let cancel_item = MenuItem::with_id(&app, "cancel", cancel_text, true, None::<&str>)
.map_err(|e| format!("Failed to create cancel item: {}", e))?;

let quit_item = MenuItem::with_id(&app, "quit", "Esci", true, None::<&str>)
let quit_item = MenuItem::with_id(&app, "quit", "Quit", true, None::<&str>)
.map_err(|e| format!("Failed to create quit item: {}", e))?;

let new_menu = Menu::with_items(
Expand Down
226 changes: 197 additions & 29 deletions src/core/pomodoro-timer.js
Original file line number Diff line number Diff line change
Expand Up @@ -778,10 +778,12 @@ export class PomodoroTimer {
);

// Show desktop notification if enabled
NotificationUtils.showDesktopNotification(
'Session Time Limit Reached',
`Your session has been automatically paused after ${maxTimeInMinutes} minutes. Consider taking a break!`
);
if (this.enableDesktopNotifications) {
NotificationUtils.showDesktopNotification(
'Session Time Limit Reached',
`Your session has been automatically paused after ${maxTimeInMinutes} minutes. Consider taking a break!`
);
}
}
}

Expand Down Expand Up @@ -2297,56 +2299,222 @@ export class PomodoroTimer {
}
}

// Simple notification system
// Enhanced notification system with better error handling and debugging
async showNotification() {
// Only show desktop notifications if the setting is enabled
if (!this.enableDesktopNotifications) {
console.log('🔔 Desktop notifications are disabled in settings');
return;
}

const messages = {
focus: 'Break time! Take a rest 😌',
break: 'Break over! Time to focus 🍅',
longBreak: 'Long break over! Ready for more focus? 🚀'
};

const notificationTitle = 'Presto - Pomodoro Timer';
const notificationBody = messages[this.currentMode];

console.log(`🔔 Attempting to show desktop notification: "${notificationBody}"`);

try {
// Check if we're in a Tauri context and use Tauri notifications
if (window.__TAURI__ && window.__TAURI__.notification) {
console.log('🔔 Using Tauri notification system');
const { isPermissionGranted, requestPermission, sendNotification } = window.__TAURI__.notification;

// Check if permission is granted
let permissionGranted = await isPermissionGranted();
console.log(`🔔 Tauri notification permission status: ${permissionGranted}`);

// If not granted, request permission
if (!permissionGranted) {
console.log('🔔 Requesting Tauri notification permission...');
const permission = await requestPermission();
permissionGranted = permission === 'granted';
console.log(`🔔 Permission request result: ${permission} (granted: ${permissionGranted})`);

if (!permissionGranted) {
console.warn('❌ Tauri notification permission was denied');
NotificationUtils.showNotificationPing('Desktop notifications are disabled. Enable them in system settings to get timer alerts! 🔔', 'warning', this.currentMode);
return;
}
}

// Send notification if permission is granted
if (permissionGranted) {
const messages = {
focus: 'Break time! Take a rest 😌',
break: 'Break over! Time to focus 🍅',
longBreak: 'Long break over! Ready for more focus? 🚀'
};

console.log('🔔 Sending Tauri notification...');
await sendNotification({
title: 'Presto - Pomodoro Timer',
body: messages[this.currentMode],
title: notificationTitle,
body: notificationBody,
icon: '/assets/tauri.svg'
});
console.log('✅ Tauri notification sent successfully');
} else {
console.warn('❌ Tauri notification permission not available');
this.fallbackToWebNotifications(notificationTitle, notificationBody);
}
} else {
// Fallback to Web Notification API
if ('Notification' in window && Notification.permission === 'granted') {
const messages = {
focus: 'Break time! Take a rest 😌',
break: 'Break over! Time to focus 🍅',
longBreak: 'Long break over! Ready for more focus? 🚀'
};

NotificationUtils.showDesktopNotification('Presto - Pomodoro Timer', messages[this.currentMode]);
console.log('🔔 Tauri not available, falling back to Web Notification API');
this.fallbackToWebNotifications(notificationTitle, notificationBody);
}
} catch (error) {
console.error('❌ Failed to show Tauri notification:', error);
console.log('🔄 Attempting fallback to Web Notification API...');
this.fallbackToWebNotifications(notificationTitle, notificationBody);
}
}

// Fallback to Web Notification API with improved error handling
async fallbackToWebNotifications(title, body) {
try {
if ('Notification' in window) {
console.log(`🔔 Web Notification API available, permission: ${Notification.permission}`);

if (Notification.permission === 'granted') {
console.log('🔔 Sending Web notification...');
NotificationUtils.showDesktopNotification(title, body);
console.log('✅ Web notification sent successfully');
} else if (Notification.permission === 'default') {
console.log('🔔 Requesting Web notification permission...');
const permission = await Notification.requestPermission();
console.log(`🔔 Web permission request result: ${permission}`);

if (permission === 'granted') {
NotificationUtils.showDesktopNotification(title, body);
console.log('✅ Web notification sent after permission granted');
} else {
console.warn('❌ Web notification permission was denied');
NotificationUtils.showNotificationPing('Desktop notifications are disabled. Enable them in your browser to get timer alerts! 🔔', 'warning', this.currentMode);
}
} else {
console.warn('❌ Web notification permission was previously denied');
NotificationUtils.showNotificationPing('Desktop notifications are disabled. Enable them in your browser settings to get timer alerts! 🔔', 'warning', this.currentMode);
}
} else {
console.warn('❌ Web Notification API not supported');
this.fallbackToInAppNotification(body);
}
} catch (error) {
console.error('Failed to show notification:', error);
// Fallback to in-app notification
this.showNotificationPing(
this.currentMode === 'focus' ? 'Break time! Take a rest 😌' :
this.currentMode === 'break' ? 'Break over! Time to focus 🍅' :
'Long break over! Ready for more focus? 🚀'
);
console.error('❌ Failed to show Web notification:', error);
this.fallbackToInAppNotification(body);
}
}

// Final fallback to in-app notification
fallbackToInAppNotification(message) {
console.log('🔔 Using in-app notification as final fallback');
NotificationUtils.showNotificationPing(message, 'info', this.currentMode);
}

// Test notification function for debugging
// Usage: Open browser console and type: window.pomodoroTimer.testNotification()
async testNotification() {
console.log('🧪 Testing notification system...');
console.log('📝 Instructions: This will test the notification system and show debug info in the console');
console.log(`🔧 Current settings: desktop notifications = ${this.enableDesktopNotifications}`);

// Detect if we're in development mode
const isDevMode = window.location.protocol === 'tauri:' ? false : true;
const bundleId = 'com.presto.app';

console.log(`🔧 Environment: ${isDevMode ? 'Development (tauri dev)' : 'Production (built app)'}`);
console.log(`🔧 Bundle ID: ${bundleId}`);

if (isDevMode) {
console.log('⚠️ IMPORTANT: You\'re running in development mode (tauri dev)');
console.log('⚠️ On macOS, Tauri notifications often don\'t work in dev mode due to:');
console.log(' 1. Tauri uses Terminal.app for dev mode, which may not have notification permissions');
console.log(' 2. Bundle identifier is handled differently in dev vs production');
console.log(' 3. macOS requires proper app bundle registration for notifications');
console.log('');
console.log('🔧 To test notifications properly:');
console.log(' 1. Run: npm run tauri build');
console.log(' 2. Install the built app from src-tauri/target/release/bundle/');
console.log(' 3. Test notifications in the installed production app');
console.log('');
console.log('🔧 For dev mode, check Terminal.app permissions:');
console.log(' - System Preferences > Notifications & Focus > Terminal');
console.log(' - Make sure "Allow Notifications" is enabled');
console.log('');
}

// Show in-app notification first
NotificationUtils.showNotificationPing('Testing notification system... 🧪', 'info', this.currentMode);

// Test desktop notification
const originalSetting = this.enableDesktopNotifications;
this.enableDesktopNotifications = true; // Temporarily enable for testing

try {
await this.showNotification();
console.log('✅ Test notification API call completed - check console logs above for detailed debug info');
console.log('🔍 Look for messages starting with 🔔 for notification flow details');

if (isDevMode) {
console.log('');
console.log('⚠️ If you see "✅ Tauri notification sent successfully" but no notification appeared:');
console.log(' - This is NORMAL in development mode on macOS');
console.log(' - Test with a production build to verify notifications work');
console.log('');
console.log('🔄 Trying Web Notification API as fallback...');
await this.testWebNotificationFallback();
}
} catch (error) {
console.error('❌ Test notification failed:', error);
console.log('💡 Troubleshooting steps:');
if (isDevMode) {
console.log(' 1. This is likely due to dev mode limitations on macOS');
console.log(' 2. Check Terminal.app notification permissions in System Preferences');
console.log(' 3. Test with a production build: npm run tauri build');
} else {
console.log(' 1. Check if notifications are enabled in System Preferences > Notifications');
console.log(' 2. Look for "presto" or "com.presto.app" in the notifications list');
console.log(' 3. Ensure "Allow Notifications" is enabled for the app');
}
} finally {
// Restore original setting
this.enableDesktopNotifications = originalSetting;
}
}

// Test Web Notification API fallback
async testWebNotificationFallback() {
try {
if ('Notification' in window) {
console.log('🌐 Web Notification API available');
console.log(`🌐 Current permission: ${Notification.permission}`);

if (Notification.permission === 'default') {
console.log('🌐 Requesting Web notification permission...');
const permission = await Notification.requestPermission();
console.log(`🌐 Permission result: ${permission}`);
}

if (Notification.permission === 'granted') {
console.log('🌐 Sending Web notification...');
const notification = new Notification('Presto - Test Web Notification', {
body: 'This is a fallback Web notification test',
icon: '/assets/tauri.svg'
});

notification.onshow = () => console.log('✅ Web notification displayed');
notification.onerror = (error) => console.error('❌ Web notification error:', error);

// Auto-close after 5 seconds
setTimeout(() => {
notification.close();
console.log('🌐 Web notification closed automatically');
}, 5000);
} else {
console.log('❌ Web notification permission denied');
}
} else {
console.log('❌ Web Notification API not available');
}
} catch (error) {
console.error('❌ Web notification test failed:', error);
}
}

Expand Down
8 changes: 6 additions & 2 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -606,8 +606,12 @@ <h3>Notification Types</h3>
Desktop Notifications
</label>
<p class="setting-description">Show system notifications when timer completes. Browser permission will
be
requested when enabled.</p>
be requested when enabled.<br>
<strong>Note:</strong> On macOS, notifications may not work in development mode. Test with a production build for full functionality.</p>
<div id="notification-status" class="notification-status" style="margin-top: 8px; padding: 8px 12px; border-radius: 6px; font-size: 13px; display: none;">
<span id="notification-status-text"></span>
<button id="test-notifications-btn" style="margin-left: 8px; padding: 4px 8px; border: none; border-radius: 4px; font-size: 12px; cursor: pointer;">Test</button>
</div>
</div>

<div class="setting-item">
Expand Down
Loading