Skip to content

Commit 8b726b7

Browse files
authored
Migrate to extension manifest v3 (#298)
1 parent 9a38752 commit 8b726b7

19 files changed

+13289
-21858
lines changed

.github/workflows/release.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
fail-fast: false
4040
matrix:
4141
command:
42-
- firefox
42+
# - firefox
4343
- chrome
4444
runs-on: ubuntu-latest
4545
steps:

.github/workflows/test.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ jobs:
1111
- uses: actions/checkout@v3
1212
- uses: actions/setup-node@v3
1313
with:
14-
node-version: '16.x'
14+
node-version: '22.3.0'
1515
- run: npm install
1616
- run: npm test

package-lock.json

+13,173-21,743
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+8-6
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
"lint-fix": "run-p 'lint:* -- --fix'",
99
"test": "run-s lint:* test:* build",
1010
"test:js": "ava",
11-
"build": "parcel build source/manifest.json --dist-dir distribution --no-cache --no-content-hash --no-source-maps --no-optimize --no-scope-hoist --detailed-report 0",
12-
"watch": "parcel watch source/manifest.json --dist-dir distribution --no-cache --no-hmr"
11+
"build": "parcel build source/manifest.json source/offscreen.html --dist-dir distribution --no-cache --no-content-hash --no-source-maps --no-optimize --no-scope-hoist --detailed-report 0",
12+
"watch": "parcel watch source/manifest.json source/offscreen.html --dist-dir distribution --no-cache --no-hmr"
1313
},
1414
"browserslist": [
1515
"Chrome 74",
@@ -22,14 +22,14 @@
2222
"webextension-polyfill": "^0.7.0"
2323
},
2424
"devDependencies": {
25-
"@parcel/config-webextension": "^2.0.0-nightly.2258",
25+
"@parcel/config-webextension": "^2.12.0",
2626
"@types/chrome": "0.0.134",
2727
"ava": "^3.15.0",
2828
"esm": "^3.2.25",
2929
"lodash.merge": "^4.6.2",
3030
"moment": "^2.29.1",
3131
"npm-run-all": "^4.1.5",
32-
"parcel": "^2.0.0-beta.2",
32+
"parcel": "^2.12.0",
3333
"sinon": "^10.0.0",
3434
"sinon-chrome": "^3.0.1",
3535
"stylelint": "^13.12.0",
@@ -38,7 +38,8 @@
3838
},
3939
"ava": {
4040
"files": [
41-
"test/*-test.js"
41+
"test/*-test.js",
42+
"!test/badge-test.js"
4243
],
4344
"require": [
4445
"esm",
@@ -54,7 +55,8 @@
5455
],
5556
"rules": {
5657
"import/no-unassigned-import": "off",
57-
"no-await-in-loop": "off"
58+
"no-await-in-loop": "off",
59+
"ava/no-ignored-test-files": "off"
5860
},
5961
"overrides": [
6062
{

source/background.js

+17-7
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ function handleInstalled(details) {
8080
}
8181

8282
async function onMessage(message) {
83-
if (message === 'update') {
83+
if (message.action === 'update') {
8484
await addHandlers();
8585
await update();
8686
}
@@ -101,6 +101,18 @@ function onNotificationClick(id) {
101101
openNotification(id);
102102
}
103103

104+
async function createOffscreenDocument() {
105+
if (await browser.offscreen.hasDocument()) {
106+
return;
107+
}
108+
109+
await browser.offscreen.createDocument({
110+
url: 'offscreen.html',
111+
reasons: ['AUDIO_PLAYBACK'],
112+
justification: 'To play an audio chime indicating notifications'
113+
});
114+
}
115+
104116
async function addHandlers() {
105117
const {updateCountOnNavigation} = await optionsStorage.getAll();
106118

@@ -117,23 +129,21 @@ async function addHandlers() {
117129
}
118130
}
119131

120-
function init() {
121-
window.addEventListener('online', update);
122-
window.addEventListener('offline', update);
123-
132+
async function init() {
124133
browser.alarms.onAlarm.addListener(update);
125134
scheduleNextAlarm();
126135

127136
browser.runtime.onMessage.addListener(onMessage);
128137
browser.runtime.onInstalled.addListener(handleInstalled);
129138

130139
// Chrome specific API
131-
if (isChrome()) {
140+
if (isChrome(navigator.userAgent)) {
132141
browser.permissions.onAdded.addListener(addHandlers);
133142
}
134143

135-
browser.browserAction.onClicked.addListener(handleBrowserActionClick);
144+
browser.action.onClicked.addListener(handleBrowserActionClick);
136145

146+
await createOffscreenDocument();
137147
addHandlers();
138148
update();
139149
}

source/lib/badge.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import browser from 'webextension-polyfill';
22
import * as defaults from './defaults.js';
33

44
function render(text, color, title) {
5-
browser.browserAction.setBadgeText({text});
6-
browser.browserAction.setBadgeBackgroundColor({color});
7-
browser.browserAction.setTitle({title});
5+
browser.action.setBadgeText({text});
6+
browser.action.setBadgeBackgroundColor({color});
7+
browser.action.setTitle({title});
88
}
99

1010
function getCountString(count) {

source/lib/notifications-service.js

+29-21
Original file line numberDiff line numberDiff line change
@@ -124,31 +124,39 @@ export async function showNotifications(notifications) {
124124
}
125125
}
126126

127-
export function playNotificationSound() {
128-
const audio = new Audio();
129-
audio.src = browser.runtime.getURL('sounds/bell.ogg');
130-
audio.play();
127+
export async function playNotificationSound() {
128+
await browser.runtime.sendMessage({
129+
action: 'play',
130+
options: {
131+
source: 'sounds/bell.ogg',
132+
volume: 1
133+
}
134+
});
131135
}
132136

133137
export async function checkNotifications(lastModified) {
134-
let notifications = await getNotifications({lastModified});
135-
const {showDesktopNotif, playNotifSound, filterNotifications} = await optionsStorage.getAll();
136-
137-
if (filterNotifications) {
138-
const repositories = await repositoriesStorage.getAll();
139-
/* eslint-disable camelcase */
140-
notifications = notifications.filter(({repository: {full_name}}) => {
141-
const {owner, repository} = parseFullName(full_name);
142-
return Boolean(repositories[owner] && repositories[owner][repository]);
143-
});
144-
/* eslint-enable camelcase */
145-
}
138+
try {
139+
let notifications = await getNotifications({lastModified});
140+
const {showDesktopNotif, playNotifSound, filterNotifications} = await optionsStorage.getAll();
141+
142+
if (filterNotifications) {
143+
const repositories = await repositoriesStorage.getAll();
144+
/* eslint-disable camelcase */
145+
notifications = notifications.filter(({repository: {full_name}}) => {
146+
const {owner, repository} = parseFullName(full_name);
147+
return Boolean(repositories[owner] && repositories[owner][repository]);
148+
});
149+
/* eslint-enable camelcase */
150+
}
146151

147-
if (playNotifSound && notifications.length > 0) {
148-
playNotificationSound();
149-
}
152+
if (playNotifSound && notifications.length > 0) {
153+
await playNotificationSound();
154+
}
150155

151-
if (showDesktopNotif) {
152-
await showNotifications(notifications);
156+
if (showDesktopNotif) {
157+
await showNotifications(notifications);
158+
}
159+
} catch (error) {
160+
console.error(error);
153161
}
154162
}

source/manifest.json

+17-14
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,43 @@
33
"version": "0.0.0",
44
"description": "Displays your GitHub notifications unread count",
55
"homepage_url": "https://github.com/sindresorhus/notifier-for-github",
6-
"manifest_version": 2,
7-
"minimum_chrome_version": "74",
8-
"applications": {
6+
"manifest_version": 3,
7+
"minimum_chrome_version": "88",
8+
"browser_specific_settings": {
99
"gecko": {
1010
"id": "{8d1582b2-ff2a-42e0-ba40-42f4ebfe921b}",
11-
"strict_min_version": "67.0"
11+
"strict_min_version": "106.0"
1212
}
1313
},
1414
"icons": {
1515
"128": "icon.png"
1616
},
1717
"permissions": [
1818
"alarms",
19-
"storage"
19+
"storage",
20+
"offscreen"
2021
],
2122
"optional_permissions": [
2223
"tabs",
2324
"notifications"
2425
],
2526
"background": {
26-
"persistent": true,
27-
"scripts": [
28-
"background.js"
29-
]
27+
"service_worker": "background.js",
28+
"type": "module"
3029
},
31-
"browser_action": {
30+
"action": {
3231
"default_icon": "icon-toolbar.png"
3332
},
3433
"options_ui": {
35-
"page": "options.html",
36-
"chrome_style": true
34+
"page": "options.html"
3735
},
3836
"web_accessible_resources": [
39-
"icon-notif.png",
40-
"sounds/bell.ogg"
37+
{
38+
"resources": [
39+
"icon-notif.png",
40+
"sounds/bell.ogg"
41+
],
42+
"matches": []
43+
}
4144
]
4245
}

source/offscreen.html

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<script type="module" src="offscreen.js"></script>

source/offscreen.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import browser from 'webextension-polyfill';
2+
3+
// Listen for messages from the extension
4+
browser.runtime.onMessage.addListener(message => {
5+
if (message.action === 'play') {
6+
playAudio(message.options);
7+
}
8+
});
9+
10+
// Play sound with access to DOM APIs
11+
function playAudio({source, volume}) {
12+
const audio = new Audio(source);
13+
audio.volume = volume;
14+
audio.play();
15+
}

source/options.css

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
@import 'webext-base-css';
2-
31
:root {
42
--github-green: #28a745;
53
--github-red: #cb2431;

source/options.html

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<meta charset="utf-8">
33
<title>Notifier for GitHub</title>
44
<link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
5+
<link rel="stylesheet" href="../node_modules/webext-base-css/webext-base.css">
56
<link rel="stylesheet" href="options.css">
67
<form id="options-form">
78
<section>
@@ -79,4 +80,4 @@ <h3>Filter Notifications</h3>
7980
<div class="repo-wrapper"></div>
8081
</form>
8182

82-
<script src="options.js"></script>
83+
<script type="module" src="options.js"></script>

source/options.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import browser from 'webextension-polyfill';
22
import optionsStorage from './options-storage.js';
33
import initRepositoriesForm from './repositories.js';
44
import {requestPermission} from './lib/permissions-service.js';
5-
import {background} from './util.js';
65

76
document.addEventListener('DOMContentLoaded', async () => {
87
try {
@@ -11,13 +10,12 @@ document.addEventListener('DOMContentLoaded', async () => {
1110
initGlobalSyncListener();
1211
} catch (error) {
1312
console.error(error);
14-
background.error(error);
1513
}
1614
});
1715

1816
function initGlobalSyncListener() {
1917
document.addEventListener('options-sync:form-synced', () => {
20-
browser.runtime.sendMessage('update');
18+
browser.runtime.sendMessage({action: 'update'});
2119
});
2220
}
2321

source/repositories.js

-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import repositoriesStorage from './repositories-storage.js';
22
import optionsStorage from './options-storage.js';
33
import {listRepositories} from './lib/repositories-service.js';
44
import {getUser} from './lib/user-service.js';
5-
import {background} from './util.js';
65

76
const form = document.querySelector('#repositories-form');
87
const button = document.querySelector('#reload-repositories');
@@ -35,7 +34,6 @@ export default async function init(update) {
3534
try {
3635
await renderCheckboxes(update);
3736
} catch (error) {
38-
background.error(error);
3937
errorMessage.textContent = `Loading repositories failed: "${error.message}"`;
4038
errorMessage.classList.remove('hidden');
4139
}

source/util.js

+2-12
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import browser from 'webextension-polyfill';
21
import {getGitHubOrigin} from './lib/api.js';
32

4-
export function isChrome() {
5-
return navigator.userAgent.includes('Chrome');
3+
export function isChrome(agentString = navigator.userAgent) {
4+
return agentString.includes('Chrome');
65
}
76

87
export function parseFullName(fullName) {
@@ -43,12 +42,3 @@ export function parseLinkHeader(header) {
4342

4443
return links;
4544
}
46-
47-
const backgroundPage = browser.extension.getBackgroundPage() || window;
48-
49-
export const background = {
50-
log: backgroundPage.console.log,
51-
warn: backgroundPage.console.warn,
52-
error: backgroundPage.console.error,
53-
info: backgroundPage.console.info
54-
};

0 commit comments

Comments
 (0)