Make extention acceptable by Edge Add-Ons website
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.yarn
|
||||||
|
node_modules
|
||||||
|
build
|
BIN
.yarn/install-state.gz
Normal file
1
.yarnrc.yml
Normal file
@ -0,0 +1 @@
|
|||||||
|
nodeLinker: node-modules
|
23
README.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# A Toasty Owa <img src="public/icons/icon_128.png" style="float:right">
|
||||||
|
|
||||||
|
Show desktop notifications for new mail and calendar alerts.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Show notifications for new mail and calendar alerts
|
||||||
|
- Add quick switch buttons for Inbox, Calendar, People and Tasks
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
### Chrome
|
||||||
|
### Edge
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
### Development
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Contribution
|
||||||
|
|
||||||
|
Suggestions and pull requests are welcomed!.
|
@ -1,80 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
console.log("Loading background script....");
|
|
||||||
|
|
||||||
let notificationMap = {};
|
|
||||||
|
|
||||||
chrome.runtime.onMessage.addListener(
|
|
||||||
function (message, sender) {
|
|
||||||
if (message.type === 'quick-links') {
|
|
||||||
chrome.storage.sync.get('showQuickLinks', (data) => {
|
|
||||||
if (data.showQuickLinks) {
|
|
||||||
chrome.tabs.executeScript(sender.tab.id, { code: "$('#quick-links').show()" });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (message.type === 'email') {
|
|
||||||
const { from, subject, body } = message;
|
|
||||||
chrome.notifications.create(null, {
|
|
||||||
type: "basic",
|
|
||||||
iconUrl: "images/email.png",
|
|
||||||
title: subject,
|
|
||||||
message: `From: ${from}\n${body}`,
|
|
||||||
requireInteraction: true
|
|
||||||
}, (notificationId) => {
|
|
||||||
notificationMap[notificationId] = { type: 'email', tabId: sender.tab.id, windowId: sender.tab.windowId };
|
|
||||||
startCloseNotificationTimer(notificationId, 'email');
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const { title, duration } = message;
|
|
||||||
chrome.notifications.create(null, {
|
|
||||||
type: "basic",
|
|
||||||
iconUrl: "images/calendar.png",
|
|
||||||
title,
|
|
||||||
message: duration,
|
|
||||||
requireInteraction: true
|
|
||||||
}, (notificationId) => {
|
|
||||||
notificationMap[notificationId] = { type: 'calendar', tabId: sender.tab.id, windowId: sender.tab.windowId };
|
|
||||||
startCloseNotificationTimer(notificationId, 'calendar');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
chrome.notifications.onClicked.addListener((notificationId) => {
|
|
||||||
if (notificationMap[notificationId]) {
|
|
||||||
const { type, tabId, windowId } = notificationMap[notificationId];
|
|
||||||
chrome.tabs.update(tabId, { active: true, highlighted: true });
|
|
||||||
chrome.windows.update(windowId, { focused: true });
|
|
||||||
chrome.notifications.clear(notificationId);
|
|
||||||
if (type === 'email') {
|
|
||||||
chrome.tabs.executeScript(tabId, { code: `$('#quick-link-mail')[0].click()` });
|
|
||||||
} else {
|
|
||||||
chrome.tabs.executeScript(tabId, { code: `$('#quick-link-calendar')[0].click()` });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
chrome.runtime.onInstalled.addListener(function() {
|
|
||||||
chrome.storage.sync.set({emailDelay: -1, calendarDelay: -1, showQuickLinks: true});
|
|
||||||
|
|
||||||
chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
|
|
||||||
chrome.declarativeContent.onPageChanged.addRules([{
|
|
||||||
conditions: [new chrome.declarativeContent.PageStateMatcher({
|
|
||||||
css: ["body[aria-label='Outlook']"]
|
|
||||||
})],
|
|
||||||
actions: [new chrome.declarativeContent.ShowPageAction()]
|
|
||||||
}]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function startCloseNotificationTimer(notificationId, type) {
|
|
||||||
chrome.storage.sync.get(`${type}Delay`, (data) => {
|
|
||||||
const delay = data[`${type}Delay`];
|
|
||||||
if (delay > 0 ) {
|
|
||||||
console.log(`Showing notification for ${delay} seconds`);
|
|
||||||
setTimeout(() => {
|
|
||||||
chrome.notifications.clear(notificationId);
|
|
||||||
}, delay * 1000);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
10
config/paths.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const PATHS = {
|
||||||
|
src: path.resolve(__dirname, '../src'),
|
||||||
|
build: path.resolve(__dirname, '../build'),
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = PATHS;
|
58
config/webpack.common.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const SizePlugin = require('size-plugin');
|
||||||
|
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||||
|
|
||||||
|
const PATHS = require('./paths');
|
||||||
|
const { ProvidePlugin } = require('webpack');
|
||||||
|
|
||||||
|
// To re-use webpack configuration across templates,
|
||||||
|
// CLI maintains a common webpack configuration file - `webpack.common.js`.
|
||||||
|
// Whenever user creates an extension, CLI adds `webpack.common.js` file
|
||||||
|
// in template's `config` folder
|
||||||
|
const common = {
|
||||||
|
output: {
|
||||||
|
// the build folder to output bundles and assets in.
|
||||||
|
path: PATHS.build,
|
||||||
|
// the filename template for entry chunks
|
||||||
|
filename: '[name].js',
|
||||||
|
},
|
||||||
|
devtool: 'source-map',
|
||||||
|
stats: {
|
||||||
|
all: false,
|
||||||
|
errors: true,
|
||||||
|
builtAt: true,
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
// Check for images imported in .js files and
|
||||||
|
{
|
||||||
|
test: /\.(png|jpe?g|gif)$/i,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: 'file-loader',
|
||||||
|
options: {
|
||||||
|
outputPath: 'images',
|
||||||
|
name: '[name].[ext]',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
// Print file sizes
|
||||||
|
new SizePlugin(),
|
||||||
|
// Copy static assets from `public` folder to `build` folder
|
||||||
|
new CopyWebpackPlugin({
|
||||||
|
patterns: [
|
||||||
|
{
|
||||||
|
from: '**/*',
|
||||||
|
context: 'public',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = common;
|
18
config/webpack.config.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { merge } = require('webpack-merge');
|
||||||
|
|
||||||
|
const common = require('./webpack.common.js');
|
||||||
|
const PATHS = require('./paths');
|
||||||
|
|
||||||
|
// Merge webpack configuration files
|
||||||
|
const config = merge(common, {
|
||||||
|
entry: {
|
||||||
|
popup: PATHS.src + '/popup.js',
|
||||||
|
service_worker: PATHS.src + '/service_worker.js',
|
||||||
|
contentScript: PATHS.src + '/contentScript.js',
|
||||||
|
jquery: 'jquery',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = config;
|
22
package.json
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "a-toasty-owa",
|
||||||
|
"version": "1.0.2",
|
||||||
|
"description": "Show desktop notifications for new mail and calendar alerts",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"watch": "yarn run webpack --mode=development --watch --config config/webpack.config.js",
|
||||||
|
"build": "yarn run webpack --mode=production --config config/webpack.config.js"
|
||||||
|
},
|
||||||
|
"packageManager": "yarn@4.4.1",
|
||||||
|
"devDependencies": {
|
||||||
|
"copy-webpack-plugin": "^6.4.1",
|
||||||
|
"file-loader": "^6.2.0",
|
||||||
|
"size-plugin": "^2.0.2",
|
||||||
|
"webpack": "^5.94.0",
|
||||||
|
"webpack-cli": "^4.10.0",
|
||||||
|
"webpack-merge": "^5.10.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"jquery": "^3.7.1"
|
||||||
|
}
|
||||||
|
}
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 717 B After Width: | Height: | Size: 717 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 429 B After Width: | Height: | Size: 429 B |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
@ -1,23 +1,27 @@
|
|||||||
{
|
{
|
||||||
|
"manifest_version": 3,
|
||||||
"name": "A Toasty OWA",
|
"name": "A Toasty OWA",
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"description": "Show desktop notifications for new mail and calendar alerts",
|
"description": "Show desktop notifications for new mail and calendar alerts",
|
||||||
|
"icons": {
|
||||||
|
"32": "icons/icon_32.png",
|
||||||
|
"48": "icons/icon_48.png",
|
||||||
|
"128": "icons/icon_128.png"
|
||||||
|
},
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"activeTab",
|
"activeTab",
|
||||||
"declarativeContent",
|
"declarativeContent",
|
||||||
"storage",
|
"storage",
|
||||||
"notifications",
|
"notifications",
|
||||||
"tabs",
|
"scripting"
|
||||||
"http://*/*",
|
],
|
||||||
"https://*/*"
|
"host_permissions": [
|
||||||
|
"https://*/owa/*"
|
||||||
],
|
],
|
||||||
"background": {
|
"background": {
|
||||||
"scripts": [
|
"service_worker": "service_worker.js"
|
||||||
"background.js"
|
|
||||||
],
|
|
||||||
"persistent": false
|
|
||||||
},
|
},
|
||||||
"page_action": {
|
"action": {
|
||||||
"default_popup": "popup.html"
|
"default_popup": "popup.html"
|
||||||
},
|
},
|
||||||
"content_scripts": [
|
"content_scripts": [
|
||||||
@ -26,15 +30,8 @@
|
|||||||
"https://*/owa/*"
|
"https://*/owa/*"
|
||||||
],
|
],
|
||||||
"js": [
|
"js": [
|
||||||
"jquery.3.3.1.slim.js",
|
|
||||||
"contentScript.js"
|
"contentScript.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"icons": {
|
|
||||||
"32": "images/icon_32.png",
|
|
||||||
"48": "images/icon_48.png",
|
|
||||||
"128": "images/icon_128.png"
|
|
||||||
},
|
|
||||||
"manifest_version": 2
|
|
||||||
}
|
}
|
@ -4,7 +4,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
font-family: 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif
|
font-family: 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif;
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
width: 220px;
|
width: 220px;
|
||||||
}
|
}
|
1
size-plugin.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
[{"timestamp":1725286622851,"files":[{"filename":"options.html","previous":530,"size":530,"diff":0},{"filename":"popup.html","previous":543,"size":543,"diff":0},{"filename":"popup.js","previous":287,"size":30655,"diff":30368},{"filename":"service_worker.js","previous":849,"size":815,"diff":-34},{"filename":"contentScript.js","previous":0,"size":31387,"diff":31387},{"filename":"jquery.js","previous":0,"size":30459,"diff":30459}]},{"timestamp":1724674251968,"files":[{"filename":"options.html","previous":530,"size":530,"diff":0},{"filename":"popup.html","previous":543,"size":543,"diff":0},{"filename":"popup.js","previous":287,"size":287,"diff":0},{"filename":"service_worker.js","previous":804,"size":849,"diff":45}]},{"timestamp":1724672835507,"files":[{"filename":"app.js","previous":286,"size":0,"diff":-286},{"filename":"options.html","previous":530,"size":530,"diff":0},{"filename":"popup.html","previous":543,"size":543,"diff":0},{"filename":"background.js","previous":797,"size":0,"diff":-797},{"filename":"popup.js","previous":0,"size":287,"diff":287},{"filename":"service_worker.js","previous":0,"size":804,"diff":804}]},{"timestamp":1724672752620,"files":[{"filename":"app.js","previous":286,"size":286,"diff":0},{"filename":"options.html","previous":530,"size":530,"diff":0},{"filename":"popup.html","previous":543,"size":543,"diff":0},{"filename":"service_worker.js","previous":797,"size":0,"diff":-797},{"filename":"background.js","previous":0,"size":797,"diff":797}]},{"timestamp":1724672366631,"files":[{"filename":"app.js","previous":286,"size":286,"diff":0},{"filename":"options.html","previous":530,"size":530,"diff":0},{"filename":"popup.html","previous":543,"size":543,"diff":0},{"filename":"service_worker.js","previous":800,"size":797,"diff":-3}]},{"timestamp":1724672146257,"files":[{"filename":"app.js","previous":286,"size":286,"diff":0},{"filename":"service_worker.js","previous":800,"size":0,"diff":-800},{"filename":"options.html","previous":530,"size":530,"diff":0},{"filename":"popup.html","previous":543,"size":543,"diff":0},{"filename":"service_worker.js","previous":0,"size":800,"diff":800}]},{"timestamp":1724327326539,"files":[{"filename":"app.js","previous":0,"size":286,"diff":286},{"filename":"service_worker.js","previous":0,"size":800,"diff":800},{"filename":"options.html","previous":0,"size":530,"diff":530},{"filename":"popup.html","previous":0,"size":543,"diff":543}]}]
|
@ -1,3 +1,5 @@
|
|||||||
|
const $ = require('jquery');
|
||||||
|
|
||||||
const MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
|
const MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
|
||||||
const bodyObserver = new MutationObserver(bodyMutationHandler);
|
const bodyObserver = new MutationObserver(bodyMutationHandler);
|
||||||
const popUpObserver = new MutationObserver(popUpAreaHandler);
|
const popUpObserver = new MutationObserver(popUpAreaHandler);
|
||||||
@ -5,8 +7,10 @@ const obsConfig = { childList: true };
|
|||||||
let insertedQuickLinks = false;
|
let insertedQuickLinks = false;
|
||||||
|
|
||||||
bodyObserver.observe($('body').get()[0], obsConfig);
|
bodyObserver.observe($('body').get()[0], obsConfig);
|
||||||
|
window.$ = $;
|
||||||
|
|
||||||
$('body').append(`
|
$('body').append(`
|
||||||
|
<script src="jquery.js"></script>
|
||||||
<style>
|
<style>
|
||||||
.quick-links-container {
|
.quick-links-container {
|
||||||
display: none;
|
display: none;
|
@ -1,5 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const $ = require('jquery');
|
||||||
|
|
||||||
$('#emailDelay').change((event) => {
|
$('#emailDelay').change((event) => {
|
||||||
chrome.storage.sync.set({ emailDelay: parseInt(event.target.value) });
|
chrome.storage.sync.set({ emailDelay: parseInt(event.target.value) });
|
||||||
});
|
});
|
@ -1,5 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const $ = require('jquery');
|
||||||
|
|
||||||
$('#emailDelay').change((event) => {
|
$('#emailDelay').change((event) => {
|
||||||
chrome.storage.sync.set({ emailDelay: parseInt(event.target.value) });
|
chrome.storage.sync.set({ emailDelay: parseInt(event.target.value) });
|
||||||
});
|
});
|
89
src/service_worker.js
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
console.log("Loading service worker....");
|
||||||
|
|
||||||
|
let notificationMap = {};
|
||||||
|
|
||||||
|
chrome.runtime.onInstalled.addListener(() => {
|
||||||
|
chrome.storage.sync.set({ emailDelay: -1, calendarDelay: -1, showQuickLinks: true });
|
||||||
|
|
||||||
|
chrome.declarativeContent.onPageChanged.removeRules(undefined, function () {
|
||||||
|
chrome.declarativeContent.onPageChanged.addRules([{
|
||||||
|
conditions: [new chrome.declarativeContent.PageStateMatcher({
|
||||||
|
css: ["body[aria-label='Outlook']"]
|
||||||
|
})],
|
||||||
|
actions: [new chrome.declarativeContent.ShowPageAction()]
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
chrome.runtime.onMessage.addListener((message, sender) => {
|
||||||
|
if (message.type === 'quick-links') {
|
||||||
|
chrome.storage.sync.get('showQuickLinks', (data) => {
|
||||||
|
if (data.showQuickLinks) {
|
||||||
|
chrome.scripting.executeScript({
|
||||||
|
target: { tabId: sender.tab.id },
|
||||||
|
func: () => { $('#quick-links').show() }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (message.type === 'email') {
|
||||||
|
const { from, subject, body } = message;
|
||||||
|
chrome.notifications.create(null, {
|
||||||
|
type: "basic",
|
||||||
|
iconUrl: "images/email.png",
|
||||||
|
title: subject,
|
||||||
|
message: `From: ${from}\n${body}`,
|
||||||
|
requireInteraction: true
|
||||||
|
}, (notificationId) => {
|
||||||
|
notificationMap[notificationId] = { type: 'email', tabId: sender.tab.id, windowId: sender.tab.windowId };
|
||||||
|
startCloseNotificationTimer(notificationId, 'email');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const { title, duration } = message;
|
||||||
|
chrome.notifications.create(null, {
|
||||||
|
type: "basic",
|
||||||
|
iconUrl: "images/calendar.png",
|
||||||
|
title,
|
||||||
|
message: duration,
|
||||||
|
requireInteraction: true
|
||||||
|
}, (notificationId) => {
|
||||||
|
notificationMap[notificationId] = { type: 'calendar', tabId: sender.tab.id, windowId: sender.tab.windowId };
|
||||||
|
startCloseNotificationTimer(notificationId, 'calendar');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
chrome.notifications.onClicked.addListener((notificationId) => {
|
||||||
|
if (notificationMap[notificationId]) {
|
||||||
|
const { type, tabId, windowId } = notificationMap[notificationId];
|
||||||
|
chrome.tabs.update(tabId, { active: true, highlighted: true });
|
||||||
|
chrome.windows.update(windowId, { focused: true });
|
||||||
|
chrome.notifications.clear(notificationId);
|
||||||
|
if (type === 'email') {
|
||||||
|
chrome.scripting.executeScript({
|
||||||
|
target: { tabId: tabId },
|
||||||
|
func: () => { $('#quick-link-mail')[0].click() }
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
chrome.scripting.executeScript({
|
||||||
|
target: { tabId: tabId },
|
||||||
|
tabId,
|
||||||
|
func: () => { $('#quick-link-calendar')[0].click() }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function startCloseNotificationTimer(notificationId, type) {
|
||||||
|
chrome.storage.sync.get(`${type}Delay`, (data) => {
|
||||||
|
const delay = data[`${type}Delay`];
|
||||||
|
if (delay > 0) {
|
||||||
|
console.log(`Showing notification for ${delay} seconds`);
|
||||||
|
setTimeout(() => {
|
||||||
|
chrome.notifications.clear(notificationId);
|
||||||
|
}, delay * 1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|