freshroute-server
Version:
Local development server for FreshRoute extension with API mocking and extended functionality
180 lines (170 loc) • 4.97 kB
JavaScript
;
let activeToasts = /* @__PURE__ */ new Set();
let notificationsEnabled = true;
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === "ruleApplied" && notificationsEnabled) {
showToastNotification(request.ruleName, request.ruleType);
} else if (request.action === "updateNotificationSettings") {
notificationsEnabled = request.enabled;
}
});
chrome.storage.sync.get(["notificationsEnabled"]).then((result) => {
notificationsEnabled = result.notificationsEnabled !== false;
});
chrome.storage.onChanged.addListener((changes, namespace) => {
if (namespace === "sync" && changes.notificationsEnabled) {
notificationsEnabled = changes.notificationsEnabled.newValue;
}
});
function showToastNotification(ruleName, ruleType) {
const toastId = `${ruleType}-${ruleName}`;
if (activeToasts.has(toastId)) {
return;
}
activeToasts.add(toastId);
let toastContainer = document.getElementById("url-rewriter-toast-container");
if (!toastContainer) {
toastContainer = createToastContainer();
document.body.appendChild(toastContainer);
}
const toast = createToastElement(ruleName, ruleType, toastId);
toastContainer.appendChild(toast);
setTimeout(() => {
toast.classList.add("show");
}, 10);
setTimeout(() => {
removeToast(toast, toastId);
}, 5e3);
}
function createToastContainer() {
const container = document.createElement("div");
container.id = "url-rewriter-toast-container";
container.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
z-index: 2147483647;
pointer-events: none;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
`;
return container;
}
function createToastElement(ruleName, ruleType, toastId) {
const toast = document.createElement("div");
toast.className = "url-rewriter-toast";
toast.dataset.toastId = toastId;
const icon = ruleType === "url_rewrite" ? "🔗" : "📝";
const typeText = ruleType === "url_rewrite" ? "URL Rewrite" : "Header Modification";
toast.innerHTML = `
<div class="toast-content">
<div class="toast-icon">${icon}</div>
<div class="toast-text">
<div class="toast-title">${typeText} Applied</div>
<div class="toast-rule">${escapeHtml(ruleName)}</div>
</div>
<button class="toast-close">×</button>
</div>
<div class="toast-progress"></div>
`;
toast.style.cssText = `
background: #fff;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
margin-bottom: 10px;
min-width: 300px;
max-width: 400px;
pointer-events: auto;
transform: translateX(100%);
transition: transform 0.3s ease-out, opacity 0.3s ease-out;
opacity: 0;
overflow: hidden;
border-left: 4px solid #1976d2;
`;
const style = document.createElement("style");
style.textContent = `
.url-rewriter-toast.show {
transform: translateX(0) !important;
opacity: 1 !important;
}
.toast-content {
display: flex;
align-items: center;
padding: 12px 16px;
gap: 12px;
}
.toast-icon {
font-size: 20px;
flex-shrink: 0;
}
.toast-text {
flex: 1;
min-width: 0;
}
.toast-title {
font-weight: 600;
font-size: 14px;
color: #333;
margin-bottom: 2px;
}
.toast-rule {
font-size: 12px;
color: #666;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.toast-close {
background: none;
border: none;
font-size: 18px;
color: #999;
cursor: pointer;
padding: 0;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.toast-close:hover {
color: #333;
}
.toast-progress {
height: 3px;
background: linear-gradient(90deg, #1976d2, #42a5f5);
transform-origin: left;
animation: toast-progress 5s linear forwards;
}
@keyframes toast-progress {
from { transform: scaleX(1); }
to { transform: scaleX(0); }
}
`;
if (!document.getElementById("url-rewriter-toast-styles")) {
style.id = "url-rewriter-toast-styles";
document.head.appendChild(style);
}
const closeBtn = toast.querySelector(".toast-close");
closeBtn.addEventListener("click", (e) => {
e.stopPropagation();
removeToast(toast, toastId);
});
return toast;
}
function removeToast(toast, toastId) {
if (!toast || !toast.parentNode) return;
toast.style.transform = "translateX(100%)";
toast.style.opacity = "0";
setTimeout(() => {
if (toast.parentNode) {
toast.parentNode.removeChild(toast);
}
activeToasts.delete(toastId);
}, 300);
}
function escapeHtml(text) {
const div = document.createElement("div");
div.textContent = text;
return div.innerHTML;
}