Files
nodecrypt/client/js/util.settings.js
T
2025-05-25 01:01:43 +08:00

244 lines
6.5 KiB
JavaScript

// Import DOM utility functions
// 导入 DOM 工具函数
import {
$,
$$,
$id,
createElement,
on,
off,
addClass,
removeClass
} from './util.dom.js';
// Default settings
// 默认设置
const DEFAULT_SETTINGS = {
notify: false,
sound: false
};
// Load settings from localStorage
// 从 localStorage 加载设置
function loadSettings() {
let s = localStorage.getItem('settings');
try {
s = s ? JSON.parse(s) : {}
} catch {
s = {}
}
return {
...DEFAULT_SETTINGS,
...s
}
}
// Save settings to localStorage
// 保存设置到 localStorage
function saveSettings(settings) {
const {
notify,
sound
} = settings;
localStorage.setItem('settings', JSON.stringify({
notify,
sound
}))
}
// Apply settings to the document
// 应用设置到文档
function applySettings(settings) {
document.documentElement.lang = 'en'
}
// Ask for browser notification permission
// 请求浏览器通知权限
function askNotificationPermission(callback) {
if (Notification.requestPermission.length === 0) {
Notification.requestPermission().then(callback)
} else {
Notification.requestPermission(callback)
}
}
// Setup the settings panel UI
// 设置设置面板 UI
function setupSettingsPanel() {
let panel = $id('settings-panel');
if (!panel) {
panel = createElement('div', {
id: 'settings-panel',
class: 'settings-panel'
}, `<div class="settings-panel-card"><div class="settings-title">Settings</div><div class="settings-item"><span>Desktop Notification</span><label class="switch"><input type="checkbox"id="settings-notify"><span class="slider"></span></label></div><div class="settings-item"><span>Sound Notification</span><label class="switch"><input type="checkbox"id="settings-sound"><span class="slider"></span></label></div></div>`);
document.body.appendChild(panel)
}
const settings = loadSettings();
const notifyCheckbox = $('#settings-notify', panel);
const soundCheckbox = $('#settings-sound', panel);
if (notifyCheckbox) notifyCheckbox.checked = !!settings.notify;
if (soundCheckbox) soundCheckbox.checked = !!settings.sound;
on(notifyCheckbox, 'change', e => {
const checked = e.target.checked;
if (checked) {
if (!('Notification' in window)) {
alert('Notifications are not supported by your browser.');
e.target.checked = false;
return
}
askNotificationPermission(permission => {
if (permission === 'granted') {
settings.notify = true;
settings.sound = false;
if (soundCheckbox) soundCheckbox.checked = false;
saveSettings(settings);
applySettings(settings);
// 防止重复通知,添加一个标志位
if (!panel._notificationShown) {
new Notification('Notifications enabled', {
body: 'You will receive alerts here.'
});
panel._notificationShown = true; // 设置标志位
}
} else {
settings.notify = false;
e.target.checked = false;
saveSettings(settings);
applySettings(settings);
alert('Please allow notifications in your browser settings.')
}
})
} else {
settings.notify = false;
saveSettings(settings);
applySettings(settings);
// 重置标志位
if (panel._notificationShown) {
panel._notificationShown = false;
}
}
});
on(soundCheckbox, 'change', e => {
settings.sound = e.target.checked;
if (settings.sound) {
settings.notify = false;
if (notifyCheckbox) notifyCheckbox.checked = false;
}
saveSettings(settings);
applySettings(settings)
});
}
// Open the settings panel
// 打开设置面板
function openSettingsPanel() {
setupSettingsPanel();
const panel = $id('settings-panel');
const btn = $id('settings-btn');
if (!btn || !panel) return;
if (panel.style.display === 'block') return;
const btnRect = btn.getBoundingClientRect();
panel.style.display = 'block';
const card = $('.settings-panel-card', panel);
if (!card) return;
card.style.position = 'fixed';
card.style.left = btnRect.left + 'px';
card.style.top = (btnRect.bottom + 8) + 'px';
card.style.transform = 'translateX(0)';
removeClass(card, 'close-anim');
// 强制触发重绘,然后添加打开动画
card.offsetHeight; // 强制重绘
addClass(card, 'open-anim');
}
// Close the settings panel
// 关闭设置面板
function closeSettingsPanel() {
const panel = $id('settings-panel');
if (!panel) return;
const card = $('.settings-panel-card', panel);
if (!card) return;
removeClass(card, 'open-anim');
addClass(card, 'close-anim');
setTimeout(() => {
panel.style.display = 'none';
removeClass(card, 'close-anim')
}, 300)
}
// Initialize settings on page load
// 页面加载时初始化设置
function initSettings() {
const settings = loadSettings();
applySettings(settings)
}
// Maximum notification text length
// 通知文本最大长度
const MAX_NOTIFY_TEXT_LEN = 100;
// Truncate text for notifications
// 截断通知文本
function truncateText(text) {
return text.length > MAX_NOTIFY_TEXT_LEN ? text.slice(0, MAX_NOTIFY_TEXT_LEN) + '...' : text
}
// Play sound notification
// 播放声音通知
function playSoundNotification() {
try {
const ctx = new(window.AudioContext || window.webkitAudioContext)();
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.frequency.value = 1000;
osc.connect(gain);
gain.connect(ctx.destination);
osc.start();
gain.gain.exponentialRampToValueAtTime(0.0001, ctx.currentTime + 0.5);
setTimeout(() => {
osc.stop();
ctx.close()
}, 600)
} catch (e) {
console.error('Sound notification failed', e)
}
}
// Show desktop notification
// 显示桌面通知
function showDesktopNotification(roomName, text, msgType, sender) {
if (!('Notification' in window) || Notification.permission !== 'granted') return;
let body;
const senderPrefix = sender ? `${sender}:` : '';
if (msgType === 'image' || msgType === 'private image') {
body = `${senderPrefix}[image]`;
if (msgType === 'private image') {
body = `(Private)${body}`
}
} else if (msgType === 'text' || msgType === 'private text') {
body = `${senderPrefix}${truncateText(text)}`;
if (msgType === 'private text') {
body = `(Private)${body}`
}
} else {
body = truncateText(text)
}
new Notification(`#${roomName}`, {
body
})
}
// Notify message entry point
// 通知消息主入口
export function notifyMessage(roomName, msgType, text, sender) {
const settings = loadSettings();
if (settings.notify) {
showDesktopNotification(roomName, text, msgType, sender)
} else if (settings.sound) {
playSoundNotification()
}
}
export {
openSettingsPanel,
closeSettingsPanel,
initSettings
};