Files
nodecrypt/web/util.settings.js
T
2025-05-19 23:11:26 +08:00

233 lines
7.5 KiB
JavaScript

// util.settings.js
// 设置面板逻辑(仅UI和本地存储,功能待实现)
const DEFAULT_SETTINGS = {
notify: false, // 桌面通知
sound: false // 声音通知
};
function loadSettings() {
let s = localStorage.getItem('settings');
try {
s = s ? JSON.parse(s) : {};
} catch { s = {}; }
return { ...DEFAULT_SETTINGS, ...s };
}
function saveSettings(settings) {
const { notify, sound } = settings;
localStorage.setItem('settings', JSON.stringify({ notify, sound }));
}
function applySettings(settings) {
// 主题切换逻辑已移除
// 强制英文
document.documentElement.lang = 'en';
}
// 兼容各浏览器的 requestPermission 调用
function askNotificationPermission(callback) {
if (Notification.requestPermission.length === 0) {
// 返回 promise
Notification.requestPermission().then(callback);
} else {
// 接受 callback
Notification.requestPermission(callback);
}
}
function setupSettingsPanel() {
let panel = document.getElementById('settings-panel');
if (!panel) {
panel = document.createElement('div');
panel.id = 'settings-panel';
panel.className = 'settings-panel';
panel.innerHTML = `
<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();
// 初始化UI
panel.querySelector('#settings-notify').checked = !!settings.notify;
panel.querySelector('#settings-sound').checked = !!settings.sound;
// 事件
panel.querySelector('#settings-notify').onchange = 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;
panel.querySelector('#settings-sound').checked = false;
saveSettings(settings); applySettings(settings);
// 测试推送
new Notification('Notifications enabled', { body: 'You will receive alerts here.' });
} 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);
}
};
panel.querySelector('#settings-sound').onchange = e => {
settings.sound = e.target.checked;
// 互斥:启用声音时关闭通知
if (settings.sound) {
settings.notify = false;
panel.querySelector('#settings-notify').checked = false;
}
saveSettings(settings); applySettings(settings);
};
}
function openSettingsPanel() {
setupSettingsPanel();
const panel = document.getElementById('settings-panel');
const btn = document.getElementById('settings-btn');
if (!btn || !panel) return;
if (panel.style.display === 'block') return;
// 定位到按钮下方,始终与按钮对齐
const btnRect = btn.getBoundingClientRect();
panel.style.display = 'block';
const card = panel.querySelector('.settings-panel-card');
card.style.position = 'fixed';
card.style.left = btnRect.left + 'px';
card.style.top = (btnRect.bottom + 8) + 'px';
card.style.transform = 'translateX(0)';
card.classList.remove('close-anim');
card.classList.add('open-anim');
// 监听鼠标移出自动关闭(只绑定一次)
function onMouseMove(ev) {
const cardRect = card.getBoundingClientRect();
const safe = 60;
const mx = ev.clientX, my = ev.clientY;
const inCard = mx >= cardRect.left - safe && mx <= cardRect.right + safe && my >= cardRect.top - safe && my <= cardRect.bottom + safe;
const inBtn = mx >= btnRect.left - safe && mx <= btnRect.right + safe && my >= btnRect.top - safe && my <= btnRect.bottom + safe;
if (!inCard && !inBtn) {
closeSettingsPanel();
}
}
function bindMouseMove() {
if (!panel._mousemoveBound) {
window.addEventListener('mousemove', onMouseMove);
panel._mousemoveBound = true;
}
}
function unbindMouseMove() {
if (panel._mousemoveBound) {
window.removeEventListener('mousemove', onMouseMove);
panel._mousemoveBound = false;
}
}
bindMouseMove();
panel._unbind = unbindMouseMove;
}
function closeSettingsPanel() {
const panel = document.getElementById('settings-panel');
if (!panel) return;
const card = panel.querySelector('.settings-panel-card');
if (!card) return;
card.classList.remove('open-anim');
card.classList.add('close-anim');
if (panel._unbind) panel._unbind();
setTimeout(() => {
panel.style.display = 'none';
card.classList.remove('close-anim');
}, 180);
}
function initSettings() {
const settings = loadSettings();
applySettings(settings);
}
// 新增通知逻辑
const MAX_NOTIFY_TEXT_LEN = 100;
function truncateText(text) {
return text.length > MAX_NOTIFY_TEXT_LEN ? text.slice(0, MAX_NOTIFY_TEXT_LEN) + '...' : text;
}
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);
}
}
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 { // Fallback for other types, e.g., 'system'
body = truncateText(text);
}
new Notification(`#${roomName}`, { body });
}
/**
* 通知入口:根据设置决定桌面通知或声音通知
* @param {string} roomName 房间名
* @param {string} msgType 消息类型 'text' | 'image'
* @param {string} text 消息文本
* @param {string} sender 发消息人
*/
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 };