Skip to content

Commit

Permalink
feat: add content script to show floating notification
Browse files Browse the repository at this point in the history
BREAKING CHANGE: remove enable Notification checkbox
  • Loading branch information
krau committed Jul 19, 2024
1 parent 22e1057 commit ad0d8b6
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 86 deletions.
1 change: 1 addition & 0 deletions config/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const PATHS = require('./paths');
const config = merge(common, {
entry: {
background: PATHS.src + '/background.js',
content: PATHS.src + '/content.js',
},
});

Expand Down
11 changes: 11 additions & 0 deletions public/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,16 @@
"storage",
"notifications",
"contextMenus"
],
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": [
"content.js"
],
"run_at": "document_end"
}
]
}
37 changes: 10 additions & 27 deletions public/popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -76,32 +76,25 @@
transition: opacity 0.5s ease;
}

.checkbox-group {
display: flex;
flex-direction: column;
margin-bottom: 15px;
}

/* 现代化的复选框样式 */
.modern-checkbox {
.custom-checkbox {
display: flex;
align-items: center;
margin-bottom: 10px;
}

.modern-checkbox input[type="checkbox"] {
.custom-checkbox input[type="checkbox"] {
display: none;
}

.modern-checkbox label {
.custom-checkbox label {
position: relative;
padding-left: 30px;
cursor: pointer;
font-size: 14px;
color: #333;
}

.modern-checkbox label:before {
.custom-checkbox label:before {
content: "";
position: absolute;
left: 0;
Expand All @@ -114,11 +107,11 @@
transition: all 0.3s ease;
}

.modern-checkbox input[type="checkbox"]:checked + label:before {
.custom-checkbox input[type="checkbox"]:checked + label:before {
background-color: #4caf50;
}

.modern-checkbox label:after {
.custom-checkbox label:after {
content: "\2714";
position: absolute;
top: 0;
Expand All @@ -129,7 +122,7 @@
opacity: 0;
}

.modern-checkbox input[type="checkbox"]:checked + label:after {
.custom-checkbox input[type="checkbox"]:checked + label:after {
opacity: 1;
}
</style>
Expand All @@ -143,19 +136,9 @@ <h2>Gopeed Settings</h2>
<input type="text" id="host" name="host" />
<label for="token">Token:</label>
<input type="text" id="token" name="token" />
<div class="checkbox-group">
<div class="modern-checkbox">
<input type="checkbox" id="enabled" name="enabled" />
<label for="enabled">自动接管</label>
</div>
<div class="modern-checkbox">
<input
type="checkbox"
id="enableNotification"
name="enableNotification"
/>
<label for="enableNotification">启用通知</label>
</div>
<div class="custom-checkbox">
<input type="checkbox" id="enabled" name="enabled" />
<label for="enabled">自动接管</label>
</div>
</div>
<button type="button" id="saveButton">保存</button>
Expand Down
11 changes: 2 additions & 9 deletions public/popup.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
document.addEventListener('DOMContentLoaded', function () {
const saveButton = document.getElementById('saveButton');
const successMessage = document.getElementById('successMessage');
chrome.storage.local.get(['enableNotification', 'enabled'], function (result) {
if (result.enableNotification === undefined) {
chrome.storage.local.set({ enableNotification: true });
document.getElementById('enableNotification').checked = true;
} else {
document.getElementById('enableNotification').checked = result.enableNotification;
}
chrome.storage.local.get(['enabled'], function (result) {
if (result.enabled === undefined) {
chrome.storage.local.set({ enabled: true });
document.getElementById('enabled').checked = true;
Expand All @@ -18,9 +12,8 @@ document.addEventListener('DOMContentLoaded', function () {
saveButton.addEventListener('click', function () {
const host = document.getElementById('host').value;
const token = document.getElementById('token').value;
const enableNotification = document.getElementById('enableNotification').checked;
const enabled = document.getElementById('enabled').checked;
chrome.storage.local.set({ host: host, token: token, enableNotification: enableNotification, enabled: enabled }, function () {
chrome.storage.local.set({ host: host, token: token, enabled: enabled }, function () {
successMessage.style.display = 'block';
setTimeout(function () {
successMessage.style.opacity = '1';
Expand Down
98 changes: 48 additions & 50 deletions src/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { Client } from "../node_modules/@gopeed/rest";
const Settings = {
host: 'http://localhost:39666',
token: 'qwqowo',
enableNotification: true,
enabled: true,
}

Expand All @@ -19,35 +18,13 @@ const initStorage = chrome.storage.local.get().then((items) => {
});
});

const sendSuccessNotification = (message) => {
chrome.notifications.create({
type: 'basic',
iconUrl: "icons/icon_48.png",
title: '已创建下载任务',
message: message,
});
}

const sendErrorNotification = (message) => {
chrome.notifications.create({
type: 'basic',
iconUrl: "icons/icon_48.png",
title: '创建下载任务失败',
message: message,
});
}


chrome.storage.onChanged.addListener((changes) => {
if (changes.host) {
Settings.host = changes.host.newValue;
}
if (changes.token) {
Settings.token = changes.token.newValue;
}
if (changes.enableNotification) {
Settings.enableNotification = changes.enableNotification.newValue;
}
if (changes.enabled) {
Settings.enabled = changes.enabled.newValue;
}
Expand All @@ -57,8 +34,12 @@ chrome.storage.onChanged.addListener((changes) => {
});
});

const INFOCOLOR = '#6699FF';
const ERRORCOLOR = '#FF3366';

chrome.downloads.onDeterminingFilename.addListener(async function (item) {
if (item.mime === "application/octet-stream") {
console.log(item);
if (item.mime === "application/octet-stream" && item.finalUrl.startsWith("blob:")) {
return;
}
await initStorage;
Expand All @@ -81,11 +62,21 @@ chrome.downloads.onDeterminingFilename.addListener(async function (item) {
name: item.filename,
}
});
if (Settings.enableNotification) {
sendSuccessNotification('文件大小: ' + (item.fileSize / (1024 * 1024)).toFixed(2) + 'MB');
}
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, {
action: 'showNotification',
message: `正在下载: ${item.filename}, 文件大小: ${(item.fileSize / (1024 * 1024)).toFixed(2)}MB`,
})
});
} catch (error) {
sendErrorNotification('错误信息: ' + error.message);
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, {
action: 'showNotification',
message: `下载${item.filename}失败: ${error.message}`,
color: ERRORCOLOR,
timeout: 4000,
})
});
}
});

Expand All @@ -98,22 +89,26 @@ chrome.contextMenus.create({

chrome.contextMenus.onClicked.addListener(async function (info, tab) {
await initStorage;

let downloadUrl = info.linkUrl || info.srcUrl || info.frameUrl;
if (info.mediaType) {
downloadUrl = info.frameUrl || downloadUrl;
}
if (!downloadUrl) {
chrome.tabs.sendMessage(tab.id, {
action: 'showNotification',
message: '下载失败, 无法获取下载链接',
color: ERRORCOLOR,
})
return;
}
chrome.tabs.sendMessage(tab.id, {
action: 'showNotification',
message: '正在获取下载链接...',
color: INFOCOLOR,
timeout: 1500,
})
try {
let downloadUrl = info.linkUrl || info.srcUrl || info.frameUrl;
if (info.mediaType) {
downloadUrl = info.frameUrl || downloadUrl;
}
if (!downloadUrl) {
chrome.notifications.create({
type: 'basic',
iconUrl: "icons/icon_48.png",
title: '创建下载任务失败',
message: '无法获取下载链接',
});
}
chrome.action.setBadgeText({
text: '🔗',
});
const resolveResult = await client.resolve({
url: downloadUrl,
extra: {
Expand All @@ -128,13 +123,16 @@ chrome.contextMenus.onClicked.addListener(async function (info, tab) {
name: resolveResult.res.files[0].name
}
})
chrome.action.setBadgeText({
text: '',
});
if (Settings.enableNotification) {
sendSuccessNotification('文件大小: ' + (resolveResult.res.files[0].size / (1024 * 1024)).toFixed(2) + 'MB');
}
chrome.tabs.sendMessage(tab.id, {
action: 'showNotification',
message: `正在下载: ${resolveResult.res.files[0].name}, 文件大小: ${(resolveResult.res.files[0].size / (1024 * 1024)).toFixed(2)}MB`,
})
} catch (error) {
sendErrorNotification('错误信息: ' + error.message);
chrome.tabs.sendMessage(tab.id, {
action: 'showNotification',
message: `下载${downloadUrl}失败: ${error.message}`,
color: ERRORCOLOR,
timeout: 4000,
})
}
});
88 changes: 88 additions & 0 deletions src/content.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
const notificationContainer = document.createElement('div');
notificationContainer.style.cssText = `
position: fixed;
bottom: 30px;
right: 30px;
z-index: 999999;
`;
document.body.appendChild(notificationContainer);

let notificationCount = 0;
const maxNotifications = 5;

function createNotification(message, color) {
const notification = document.createElement('div');
notification.style.cssText = `
padding: 12px 24px;
color: #fff;
border-radius: 10px;
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
transition: all 0.3s ease;
opacity: 0;
transform: translateY(20px);
font-family: Arial, sans-serif;
min-width: 250px;
max-width: 400px;
margin-top: 10px;
`;
notification.style.background = color || '#4CAF50';
const title = document.createElement('div');
title.textContent = 'Gopeed Extension';
title.style.cssText = `
font-size: 12px;
font-weight: normal;
opacity: 0.8;
margin-bottom: 8px;
text-transform: uppercase;
letter-spacing: 0.5px;
`;

const content = document.createElement('div');
content.textContent = message;
content.style.cssText = `
font-size: 16px;
font-weight: bold;
`;

notification.appendChild(title);
notification.appendChild(content);
return notification;
}

function showNotification(message, color, timeout) {
const notification = createNotification(message, color);
notificationContainer.insertBefore(notification, notificationContainer.firstChild);
notificationCount++;

while (notificationContainer.children.length > maxNotifications) {
notificationContainer.removeChild(notificationContainer.lastChild);
notificationCount--;
}

Array.from(notificationContainer.children).forEach((notif, index) => {
notif.style.transform = `translateY(${(notificationCount - index - 1) * 100}%)`;
});

setTimeout(() => {
notification.style.opacity = '1';
notification.style.transform = 'translateY(0)';
}, 10);

setTimeout(() => {
notification.style.opacity = '0';
notification.style.transform = 'translateY(20px)';
setTimeout(() => {
notificationContainer.removeChild(notification);
notificationCount--;
Array.from(notificationContainer.children).forEach((notif, index) => {
notif.style.transform = `translateY(${(notificationCount - index - 1) * 100}%)`;
});
}, 300);
}, timeout || 3000);
}

chrome.runtime.onMessage.addListener(function (message, _sender, _sendResponse) {
if (message.action === 'showNotification') {
showNotification(message.message, message.color, message.timeout);
}
});

0 comments on commit ad0d8b6

Please sign in to comment.