Skip to content

Commit 393355a

Browse files
committed
Fix settings persistence across different browsers and devices
- Added server-side storage for user settings - Created API endpoints for user settings - Updated client-side code to save and load settings from server - Improved synchronization logic for all settings - Updated CHANGELOG.md
1 parent 6e34e68 commit 393355a

File tree

12 files changed

+386
-73
lines changed

12 files changed

+386
-73
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2323
- Added visual indicators showing whether Slack webhook is configured
2424
- Added visual indicators showing whether Email notifications are configured
2525
- Improved user experience with clear configuration status
26+
- Server-side storage for user settings:
27+
- Added user_settings table to store user-specific settings
28+
- Created API endpoints for retrieving and updating user settings
29+
- Implemented server-side storage for session timeout settings
30+
- Ensured settings persistence across different browsers and devices
2631

2732
### Fixed
2833
- Fixed automatic logout not working correctly on mobile devices:
@@ -32,6 +37,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3237
- Added localStorage-based timestamp tracking for more reliable inactivity detection
3338
- Improved logging for better debugging of activity detection
3439
- Updated tests to cover new mobile-specific functionality
40+
- Fixed settings not being saved properly across different browsers and devices:
41+
- Implemented server-side storage for session timeout settings
42+
- Improved synchronization logic for notification settings
43+
- Added explicit save functionality to ensure settings are properly persisted
44+
- Prioritized server settings over localStorage settings
45+
- Enhanced error handling for failed server requests
3546

3647
### Improved
3748
- Enhanced documentation organization:
@@ -42,6 +53,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4253
- Added dedicated tab for application information
4354
- Improved navigation with clear tab labels and icons
4455
- Added status badges to notification settings for better visibility
56+
- Improved settings synchronization:
57+
- Better handling of settings loading from server
58+
- More reliable saving of settings to server
59+
- Clearer feedback when settings are saved successfully
4560

4661
## [1.8.2] - 2025-02-28
4762

client/src/components/notifications/NotificationSettings.jsx

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import React, { useState } from 'react';
2-
import axios from 'axios';
32
import { FiBell, FiSave, FiChevronDown, FiChevronRight } from 'react-icons/fi';
43
import { useSettings } from '../../context/SettingsContext';
54
import DiscordNotificationSettings from './DiscordNotificationSettings';
@@ -16,7 +15,7 @@ import NotificationHistory from './NotificationHistory';
1615
* sections to reduce visual clutter and improve user experience.
1716
*/
1817
const NotificationSettings = ({ setError, setSuccess }) => {
19-
const { settings, updateSetting } = useSettings();
18+
const { settings, updateSetting, saveNotificationSettings } = useSettings();
2019
const [isSaving, setIsSaving] = useState(false);
2120
const [expandedSections, setExpandedSections] = useState({
2221
discord: false,
@@ -38,27 +37,14 @@ const NotificationSettings = ({ setError, setSuccess }) => {
3837
const handleSaveSettings = async () => {
3938
setIsSaving(true);
4039
try {
41-
// Convert client settings format to server format
42-
const serverSettings = {
43-
discord_webhook_url: settings.discordWebhookUrl,
44-
slack_webhook_url: settings.slackWebhookUrl,
45-
notifications_enabled: settings.notifications,
46-
battery_notifications: settings.batteryNotifications,
47-
low_battery_notifications: settings.lowBatteryNotifications,
48-
email_notifications: settings.emailNotifications,
49-
email_recipients: settings.emailRecipients
50-
};
51-
52-
// Send settings to server
53-
const response = await axios.post('/api/notifications/settings', serverSettings);
40+
// Use the context function to save settings to server
41+
await saveNotificationSettings();
5442

5543
setSuccess('Notification settings saved successfully');
5644
setTimeout(() => setSuccess(null), 3000);
57-
console.log('Saved notification settings to server:', response.data);
5845
} catch (err) {
5946
setError(err.response?.data?.message || 'Failed to save notification settings');
6047
setTimeout(() => setError(null), 5000);
61-
console.error('Error saving settings to server:', err);
6248
} finally {
6349
setIsSaving(false);
6450
}

client/src/components/settings/PollingSettings.jsx

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,27 @@
1-
import React from 'react';
1+
import React, { useState } from 'react';
22
import { FiSave, FiClock } from 'react-icons/fi';
33
import { useSettings } from '../../context/SettingsContext';
44

55
const PollingSettings = ({ pollInterval, setPollInterval, setSuccess }) => {
6-
const { settings, updateSetting } = useSettings();
6+
const { settings, updateSetting, saveUserSettings } = useSettings();
7+
const [isSaving, setIsSaving] = useState(false);
78

8-
const savePollInterval = () => {
9-
updateSetting('pollInterval', pollInterval);
10-
setSuccess('Poll interval updated successfully');
11-
setTimeout(() => setSuccess(null), 3000);
9+
const savePollInterval = async () => {
10+
setIsSaving(true);
11+
try {
12+
// Update local setting
13+
updateSetting('pollInterval', pollInterval);
14+
15+
// Save to server
16+
await saveUserSettings();
17+
18+
setSuccess('Poll interval updated successfully and saved to server');
19+
setTimeout(() => setSuccess(null), 3000);
20+
} catch (error) {
21+
console.error('Error saving poll interval to server:', error);
22+
} finally {
23+
setIsSaving(false);
24+
}
1225
};
1326

1427
return (
@@ -39,10 +52,11 @@ const PollingSettings = ({ pollInterval, setPollInterval, setSuccess }) => {
3952
<span className="text-sm font-medium text-gray-900 dark:text-white">{pollInterval}s</span>
4053
<button
4154
onClick={savePollInterval}
42-
className="inline-flex items-center px-3 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500"
55+
disabled={isSaving}
56+
className="inline-flex items-center px-3 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 disabled:opacity-50 disabled:cursor-not-allowed"
4357
>
4458
<FiSave className="mr-2 -ml-1 h-4 w-4" />
45-
Save
59+
{isSaving ? 'Saving...' : 'Save'}
4660
</button>
4761
</div>
4862
</div>

client/src/components/settings/ResetSettings.jsx

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,28 @@
1-
import React from 'react';
1+
import React, { useState } from 'react';
22
import { FiRefreshCw } from 'react-icons/fi';
33
import { useSettings } from '../../context/SettingsContext';
44

55
const ResetSettings = ({ setPollInterval, setSuccess }) => {
6-
const { resetSettings } = useSettings();
6+
const { resetSettings, saveAllSettings } = useSettings();
7+
const [isResetting, setIsResetting] = useState(false);
78

8-
const handleResetSettings = () => {
9-
resetSettings();
10-
setPollInterval(30); // Default value
11-
setSuccess('Settings reset to defaults');
12-
setTimeout(() => setSuccess(null), 3000);
9+
const handleResetSettings = async () => {
10+
setIsResetting(true);
11+
try {
12+
// Reset local settings
13+
resetSettings();
14+
setPollInterval(30); // Default value
15+
16+
// Save reset settings to server
17+
await saveAllSettings();
18+
19+
setSuccess('Settings reset to defaults and saved to server');
20+
setTimeout(() => setSuccess(null), 3000);
21+
} catch (error) {
22+
console.error('Error saving reset settings to server:', error);
23+
} finally {
24+
setIsResetting(false);
25+
}
1326
};
1427

1528
return (
@@ -21,10 +34,11 @@ const ResetSettings = ({ setPollInterval, setSuccess }) => {
2134
<div className="mt-2">
2235
<button
2336
onClick={handleResetSettings}
24-
className="inline-flex items-center px-3 py-2 border border-gray-300 dark:border-gray-600 shadow-sm text-sm font-medium rounded-md text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500"
37+
disabled={isResetting}
38+
className="inline-flex items-center px-3 py-2 border border-gray-300 dark:border-gray-600 shadow-sm text-sm font-medium rounded-md text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 disabled:opacity-50 disabled:cursor-not-allowed"
2539
>
2640
<FiRefreshCw className="mr-2 -ml-1 h-4 w-4" />
27-
Reset to Defaults
41+
{isResetting ? 'Resetting...' : 'Reset to Defaults'}
2842
</button>
2943
</div>
3044
</div>

client/src/context/AuthContext.jsx

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { createContext, useContext, useState, useEffect, useCallback } from 'react';
22
import useInactivityTimer from '../hooks/useInactivityTimer';
3-
import { authAPI } from '../services/api';
3+
import { authAPI, userSettingsAPI } from '../services/api';
44
import axios from 'axios'; // Still needed for setting default headers
55

66
const AuthContext = createContext();
@@ -11,7 +11,7 @@ export const AuthProvider = ({ children }) => {
1111
const [user, setUser] = useState(null);
1212
const [loading, setLoading] = useState(true);
1313
const [error, setError] = useState(null);
14-
const [inactivityTimeout, setInactivityTimeout] = useState(30); // Default 30 minutes
14+
const [inactivityTimeout, setInactivityTimeout] = useState(30); // Default 30 minutes, will be updated from server
1515

1616
// Logout user
1717
const logout = useCallback(() => {
@@ -29,6 +29,17 @@ export const AuthProvider = ({ children }) => {
2929
if (storedUser && token) {
3030
setUser(JSON.parse(storedUser));
3131
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
32+
33+
// Load user settings from server
34+
userSettingsAPI.getSettings()
35+
.then(response => {
36+
if (response.data && response.data.inactivity_timeout) {
37+
setInactivityTimeout(response.data.inactivity_timeout);
38+
}
39+
})
40+
.catch(error => {
41+
console.error('Error loading user settings:', error);
42+
});
3243
}
3344

3445
setLoading(false);
@@ -44,6 +55,15 @@ export const AuthProvider = ({ children }) => {
4455
// Update inactivity timeout
4556
const updateInactivityTimeout = useCallback((minutes) => {
4657
setInactivityTimeout(minutes);
58+
59+
// Save to server
60+
userSettingsAPI.updateSettings({ inactivity_timeout: minutes })
61+
.then(response => {
62+
console.log('Inactivity timeout saved to server:', response.data);
63+
})
64+
.catch(error => {
65+
console.error('Error saving inactivity timeout to server:', error);
66+
});
4767
}, []);
4868

4969
// Check if this is the first time setup

0 commit comments

Comments
 (0)