Skip to content

Commit

Permalink
Fixes and features (#201)
Browse files Browse the repository at this point in the history
* resolved package.json conflict

* fix window-controlled require electron to match recent changed in mainstream.

* fix package json about-window requirement

* #189 Force nodeIntegration and  contextIsolation to let require works again.

* Undo Force nodeIntegration and  contextIsolation to let require works again.

* fix package.json doubled lines

* #189 forced contextIsolation: false and nodeIntegration: true to let "require" workson client side.

* UnreadNumberObserver is now in a separated js file

* UnreadNumberObserver is now in a separated js file fix performance cache

* UnreadNumberObserver better tassonomy

* client js and css are now in a separated  file. Using clientInjector to load, cache and inject them.

* Multiple unreadMessageObserver handler.

* Integrated did-create-window and child-window.js suggested by @joax

* Better client injector strategy. Moved all clinet files into /public dir

* #122, #173 Added context menu wilth Editing actions.

* #122 Context menu will show first 50 chars of the clicked link url

* context-menu: undo will appear only on editable element.

* try fix clear unreadmessageicon in some cases OWA

* unread-observer "custom_2": attempting to be multilanguage

* #10 fixed Right click on child windows.

* #10 fixed Right click on child windows.

* update electron to "^16.0.5" in order to fix annoying bug described here electron/electron#31152

* update electron to "^16.0.9" in order to fix annoying bug described here electron/electron#31152

---------

Co-authored-by: Julian Alarcon <jalarcon@lendingfront.com>
Co-authored-by: Julian Alarcon <alarconj@gmail.com>
  • Loading branch information
3 people committed Jul 24, 2023
1 parent 04e96a1 commit d3f474a
Show file tree
Hide file tree
Showing 10 changed files with 7,485 additions and 2,001 deletions.
5,288 changes: 5,288 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 3 additions & 4 deletions package.json
@@ -1,7 +1,7 @@
{
"name": "prospect-mail",
"productName": "Prospect Mail",
"version": "0.5.0-beta",
"version": "0.5.1-beta",
"main": "src/main.js",
"description": "Unofficial desktop client for Microsoft Outlook",
"homepage": "https://github.com/julian-alarcon/prospect-mail",
Expand Down Expand Up @@ -46,11 +46,10 @@
"dependencies": {
"about-window": "^1.15.2",
"electron-debug": "^3.2.0",
"electron-settings": "^4.0.2",
"about-window": "^1.15.2"
"electron-settings": "^4.0.2"
},
"devDependencies": {
"electron": "^16.0.2",
"electron": "^16.0.9",
"electron-builder": "^22.14.5"
},
"build": {
Expand Down
30 changes: 30 additions & 0 deletions public/child-window.js
@@ -0,0 +1,30 @@
setTimeout(() => {
let removeTopBar = ['to-do.office.com/tasks']
let url = window.location;

// Remove Top Bar
if (new RegExp(removeTopBar.join('|')).test(url)) {
var topBar = document.querySelectorAll('#O365ShellHeader')
topBar[0].style.display = 'none';
}
// Close Button
let closeButton = document.createElement('div');

closeButton.className = 'ms-Button';
closeButton.style.position = 'absolute';
closeButton.style.right = '0px';
closeButton.style.top = '0px';
closeButton.style.lineHeight = '35px';
closeButton.style.width = '35px';
closeButton.style.height = '35px';
closeButton.style.background = 'rgba(0,0,0,0.3)';
closeButton.style.textAlign = 'center';
closeButton.style.color = 'white';
closeButton.style.cursor = 'pointer';
closeButton.style.fontSize = '20px';
closeButton.append('✖');
closeButton.addEventListener('click', () => {
window.close();
});
document.body.append(closeButton);
}, 3000);
14 changes: 14 additions & 0 deletions public/main.css
@@ -0,0 +1,14 @@
/* hide the vertical ad bar */
._1_ag99JsBHxI6S4FP5ayPv {
display: none !important;
}

/* hide the small ad bar in other email page */
._2a6h2L3Tl12cnq2P7ZG9y_ {
display: none !important;
}

/* hide the upgrade premium ad bar */
._1ZEdP0-JdMOVtjBb5ZcM6M {
display: none !important;
}
11 changes: 11 additions & 0 deletions public/no-frame.css
@@ -0,0 +1,11 @@
/* make the header higher and dragable */
._1Kg3ffZABPxXxDqcmoxkBA {
padding-top: 30px !important;
-webkit-app-region: drag;
}

/* make the clickable component in header not dragable */
.ms-FocusZone,
._3Nd2PGu67wifhuPZp2Sfj5 {
-webkit-app-region: no-drag;
}
207 changes: 207 additions & 0 deletions public/unread-number-observer.js
@@ -0,0 +1,207 @@
let owa_timer;
const observeUnreadHandlers = {
consumer: () => {
const unreadSpan = document.querySelector('._2iKri0mE1PM9vmRn--wKyI');
if (!unreadSpan) {
return false
}

//Default standard outlook url-site
require('electron').ipcRenderer.send('updateUnread', unreadSpan.hasChildNodes());
let observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
console.log('Observer Changed.');
require('electron').ipcRenderer.send('updateUnread', unreadSpan.hasChildNodes());

// Scrape messages and pop up a notification
var messages = document.querySelectorAll('div[role="listbox"][aria-label="Message list"]');
if (messages.length) {
var unread = messages[0].querySelectorAll('div[aria-label^="Unread"]');
var body = "";
for (var i = 0; i < unread.length; i++) {
if (body.length) {
body += "\\n";
}
body += unread[i].getAttribute("aria-label").substring(7, 127);
}
if (unread.length) {
var notification = new Notification(unread.length + " New Messages", {
body: body,
icon: "assets/outlook_linux_black.png"
});
notification.onclick = () => {
require('electron').ipcRenderer.send('show');
};
}
}
});
});

observer.observe(unreadSpan, { childList: true });

// If the div containing reminders gets taller we probably got a new
// reminder, so force the window to the top.
let reminders = document.getElementsByClassName("_1BWPyOkN5zNVyfbTDKK1gM");
let height = 0;
let reminderObserver = new MutationObserver(mutations => {
mutations.forEach(mutation => {
if (reminders[0].clientHeight > height) {
require('electron').ipcRenderer.send('show');
}
height = reminders[0].clientHeight;
});
});

if (reminders.length) {
reminderObserver.observe(reminders[0], { childList: true });
}
return true //successfully attached
},
// @joax implmenetation, maybe this is an update or consumer
consumer_2: () => {
let unreadSpan = document.querySelector('._2HtVv8aUAL5e8b05Rc4I8v');
if (!unreadSpan) {
return false;
}
require('electron').ipcRenderer.send('updateUnread', unreadSpan.hasChildNodes());
console.log(unreadSpan, unreadSpan.hasChildNodes())
let observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
console.log('Observer Changed.');
require('electron').ipcRenderer.send('updateUnread', unreadSpan.hasChildNodes());
// Scrape messages and pop up a notification
var messages = document.querySelectorAll('div[aria-label] [role="listbox"]');
if (messages.length) {
console.log('Unread messages found');
//we need to be multilanguage
var unread =
messages[0].querySelectorAll('div[aria-label^="Unread"]')
|| messages[0].querySelectorAll('div[aria-label^="Da leggere"]');
var body = "";
for (var i = 0; i < unread.length; i++) {
if (body.length) {
body += "\\n";
}
body += unread[i].getAttribute("aria-label").substring(7, 127);
}
if (unread.length) {
var notification = {
title: "Outlook (" + unread.length + ") new messages",
subtitle: "You have new messages in your inbox",
body: body,
icon: "assets/outlook_linux_black.png"
};
// Show system notification
require('electron').ipcRenderer.send('unread-messages-notification', notification);
}
}
});
});
observer.observe(unreadSpan, { childList: true });
// If the div containing reminders gets taller we probably got a new
// reminder, so force the window to the top.
let reminders = document.getElementsByClassName("_3PvwGqXAizENgzsKVa_JPJ");
let height = 0;
let reminderObserver = new MutationObserver(mutations => {
mutations.forEach(mutation => {
if (reminders[0].clientHeight > height) {
require('electron').ipcRenderer.send('show');
}
height = reminders[0].clientHeight;
});
});
if (reminders.length) {
reminderObserver.observe(reminders[0], { childList: true });
}
return true //successfully attached
},
owa: () => {
const unreadSpan = document.querySelector('._n_J4._n_F4 .ms-fcl-tp');
if (!unreadSpan) {
return false
}
let lastcheck
const checkOwa = (checkonlyzerounread) => {
const unread = document.querySelectorAll('._n_J4._n_F4 .ms-fcl-tp').length;

if (unread > 0 || !checkonlyzerounread) {

require('electron').ipcRenderer.send('updateUnread', unread);

if (unread > 0 && !checkonlyzerounread) {
//do not spam notification
if (!lastcheck || (new Date() - lastcheck) > 500) {

if (!document.hasFocus()) {

var notification = new Notification("New Messages", {
body: 'There are ' + unread + ' unread messages.',
icon: "assets/outlook_linux_black.png"
});
notification.onclick = () => {
require('electron').ipcRenderer.send('show');
};
}
lastcheck = new Date()
}
}
}
}

const leftPanel = document.querySelector('.ms-bgc-nlr')
console.log('Begin observe leftPanel: ', leftPanel)
const observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
waitForFinalEvent(checkOwa, 1000, 'mutation detected')
})
})

observer.observe(leftPanel, { attributes: true, childList: true, subtree: true });

//observer cannot catch all changes, use timer to handle ZERO unreadmessages
if (owa_timer) {
clearInterval(owa_timer);
}
owa_timer = setInterval(() => {
checkOwa(true);
}, 5000);

checkOwa();

return true //successfully attached
}
}

const observeUnreadInit = () => {
let found = false
for (const handlername in observeUnreadHandlers) {
const handler = observeUnreadHandlers[handlername]
found = handler()
if (found) {
console.log(`Handler %o attached.`, handlername)
//handler found no need to cycle again
break;
}
}

if (!found) {
console.log('Missing valid handler, try again in 5 seconds')
setTimeout(observeUnreadInit, 5000);
return
}
}

var waitForFinalEvent = (function() {
var timers = {};
return function(callback, ms, uniqueId) {
if (!uniqueId) {
uniqueId = "Don't call this twice without a uniqueId";
}
if (timers[uniqueId]) {
clearTimeout(timers[uniqueId]);
}
timers[uniqueId] = setTimeout(callback, ms);
};
})();

observeUnreadInit();
20 changes: 20 additions & 0 deletions src/controller/client-injector.js
@@ -0,0 +1,20 @@
const path = require('path')
const fs = require('fs')

const cache = {}
module.exports = (relpath) => {
if (cache[relpath])
{
return cache[relpath]
}
relpath = relpath.trim()
//remove initial . or / to prevent out of bound request
while (['/', '.'].indexOf(relpath.substring(0, 1)) == 0) relpath = relpath.substring(1)
const fullpath = path.resolve(path.join(__dirname,'../../public', relpath))
if (!fs.existsSync(fullpath) || !fs.statSync(fullpath).isFile()) {
throw new Error(`${relpath} is not a valid client file. It must exists in ` + fullpath)
}
console.log(`Prepare %o to be injected.`, relpath)
cache[relpath] = fs.readFileSync(`${fullpath}`).toString()
return cache[relpath]
}

0 comments on commit d3f474a

Please sign in to comment.