Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix notifications workaround #2051

Merged
merged 10 commits into from
Sep 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 72 additions & 25 deletions source/browser/conversation-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,39 +169,85 @@ export async function sendConversationList(): Promise<void> {
ipc.callMain('conversations', conversationsToRender);
}

function genStringFromNode(element: Element): string | undefined {
const cloneElement = element.cloneNode(true) as Element;
let emojiString;

const images = cloneElement.querySelectorAll('img');
for (const image of images) {
emojiString = image.alt;
// Replace facebook's thumbs up with emoji
if (emojiString === '(Y)' || emojiString === '(y)') {
emojiString = '👍';
}

image.parentElement?.replaceWith(document.createTextNode(emojiString));
}

return cloneElement.textContent ?? undefined;
}

function countUnread(mutationsList: MutationRecord[]): void {
// Look through the mutations for the one with the unread dot
const unreadMutations = mutationsList.filter(mutation => mutation.type === 'childList' && mutation.addedNodes.length > 0 && ((mutation.addedNodes[0] as Element).getAttribute('aria-label') === 'Mark as read'));

for (const mutation of unreadMutations) {
let curr = (mutation.addedNodes[0] as Element).parentElement;
// Find the parent element with the message preview and sender
while (curr?.className !== 'rq0escxv l9j0dhe7 du4w35lb j83agx80 pfnyh3mw i1fnvgqd bp9cbjyn owycx6da btwxx1t3') {
curr = curr?.parentElement ?? null;
const alreadyChecked: string[] = [];

const unreadMutations = mutationsList.filter(mutation =>
// When a conversations "becomes unread".
(
mutation.type === 'childList'
&& mutation.addedNodes.length > 0
&& ((mutation.addedNodes[0] as Element).className === selectors.conversationSidebarUnreadDot)
)
// When text is received
|| (
mutation.type === 'characterData'
// Make sure the text corresponds to a conversation
&& mutation.target.parentElement?.parentElement?.parentElement?.className === selectors.conversationSidebarTextParent
)
// When an emoji is received, node(s) are added
|| (
mutation.type === 'childList'
// Make sure the mutation corresponds to a conversation
&& mutation.target.parentElement?.parentElement?.className === selectors.conversationSidebarTextParent
)
// Emoji change
|| (
mutation.type === 'attributes'
&& mutation.target.parentElement?.parentElement?.parentElement?.parentElement?.className === selectors.conversationSidebarTextParent
));

// Check latest mutation first
for (const mutation of unreadMutations.reverse()) {
const curr = (mutation.target.parentElement as Element).closest(selectors.conversationSidebarSelector)!;

const href = curr.closest('[role="link"]')?.getAttribute('href');

if (!href) {
continue;
}

// It is possible to have multiple mutations for the same conversation, but we only want one notification.
// So if the current conversation has already been checked, continue.
// Additionally if the conversation is not unread, then also continue.
if (alreadyChecked.includes(href) || !curr.querySelector('.' + selectors.conversationSidebarUnreadDot.replace(/ /g, '.'))) {
continue;
}

alreadyChecked.push(href);

// Get the image data URI from the parent of the author/text
const imgUrl = curr.parentElement?.getElementsByTagName('img')[0].dataset.caprineIcon;
const imgUrl = curr.querySelector('img')?.dataset.caprineIcon;
const textOptions = curr.querySelectorAll(selectors.conversationSidebarTextSelector);
// Get the author and text of the new message
// const titleText = curr.querySelectorAll('.d2edcug0.hpfvmrgz.qv66sw1b.c1et5uql.b0tq1wua.jq4qci2q.a3bd9o3v.lrazzd5p.oo9gr5id')[0].textContent;
let titleTextOptions = curr.querySelectorAll('.a8c37x1j.d2edcug0.ni8dbmo4.ltmttdrg.g0qnabr5');
if (titleTextOptions.length === 0) {
titleTextOptions = curr.querySelectorAll('.a8c37x1j.ni8dbmo4.stjgntxs.l9j0dhe7.ltmttdrg.g0qnabr5');
}
const titleTextNode = textOptions[0];
const bodyTextNode = textOptions[1];

const titleText = titleTextOptions[0].textContent;
let bodyTextOptions = curr.querySelectorAll('.a8c37x1j.ni8dbmo4.stjgntxs.l9j0dhe7.ltmttdrg.g0qnabr5');
if (bodyTextOptions.length === 0) {
bodyTextOptions = curr.querySelectorAll('.a8c37x1j.d2edcug0.ni8dbmo4.ltmttdrg.g0qnabr5');
}
const titleText = genStringFromNode(titleTextNode);
const bodyText = genStringFromNode(bodyTextNode);

let loc = 0;
if (bodyTextOptions.length >= 2) {
loc = 1;
if (!bodyText || !titleText || !imgUrl) {
continue;
}

const bodyText = bodyTextOptions[loc].textContent;

// Send a notification
ipc.callMain('notification', {
id: 0,
Expand Down Expand Up @@ -239,10 +285,11 @@ window.addEventListener('load', async () => {
});

conversationCountObserver.observe(sidebar, {
characterData: true,
subtree: true,
childList: true,
attributes: true,
attributeFilter: ['class'],
attributeFilter: ['src', 'alt'],
});
}

Expand Down
6 changes: 5 additions & 1 deletion source/browser/selectors.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
export default {
leftSidebar: '[class="x9f619 x1n2onr6 x1ja2u2z x78zum5 xdt5ytf x2lah0s x193iq5w xeuugli xycxndf xexx8yu x18d9i69 xkhd6sd x4uap5"]', // ! Tray icon dependency
chatsIcon: '[class="x1i10hfl xjqpnuy xa49m3k xqeqjp1 x2hbi6w x13fuv20 xu3j5b3 x1q0q8m5 x26u7qi x972fbf xcfux6l x1qhh985 xm0m39n x9f619 x1ypdohk xdl72j9 x2lah0s xe8uvvx x2lwn1j xeuugli x4uap5 xkhd6sd x1n2onr6 x16tdsg8 x1hl2dhg xggy1nq x1ja2u2z x1t137rt x87ps6o x1lku1pv x1a2a7pz x6s0dn4 x1q0g3np xn3w4p2 x1nn3v0j x1120s5i x1av1boa x1lq5wgf xgqcy7u x30kzoy x9jhf4c xdj266r x11i5rnm xat24cr x1mh8g0r x78zum5"]', // ! Tray icon dependency
conversationList: '[role=navigation] [role=grid]',
conversationList: '[role=navigation] [role=grid] [class="x1n2onr6"]',
conversationSelector: '[role=main] [role=grid]',
conversationSidebarUnreadDot: 'x1i10hfl x1qjc9v5 xjbqb8w xjqpnuy xa49m3k xqeqjp1 x2hbi6w x13fuv20 xu3j5b3 x1q0q8m5 x26u7qi x972fbf xcfux6l x1qhh985 xm0m39n x9f619 x1ypdohk xdl72j9 x2lah0s xe8uvvx xdj266r x11i5rnm xat24cr x1mh8g0r x2lwn1j xeuugli xexx8yu x4uap5 x18d9i69 xkhd6sd x1n2onr6 x16tdsg8 x1hl2dhg xggy1nq x1ja2u2z x1t137rt x1o1ewxj x3x9cwd x1e5q0jg x13rtm0m x1q0g3np x87ps6o x1lku1pv x78zum5 x1a2a7pz',
conversationSidebarTextParent: 'html-span xdj266r x11i5rnm xat24cr x1mh8g0r xexx8yu x18d9i69 xkhd6sd x1hl2dhg x16tdsg8 x1vvkbs x6s0dn4 x78zum5 x193iq5w xeuugli xg83lxy', // Parent element of the conversation text element (needed for notifications)
conversationSidebarTextSelector: '[class="x1lliihq x193iq5w x6ikm8r x10wlt62 xlyipyv xuxw1ft"]', // Generic selector for the text contents of all conversations
conversationSidebarSelector: '[class="x9f619 x1n2onr6 x1ja2u2z x78zum5 x2lah0s x1qughib x6s0dn4 xozqiw3 x1q0g3np"]', // Selector for the top level element of a single conversation (children contain text content of the conversation and conversation image)
notificationCheckbox: '._374b:nth-of-type(4) ._4ng2 input',
rightSidebarButtons: '.rq0escxv.l9j0dhe7.du4w35lb.j83agx80.cbu4d94t.g5gj957u.f4tghd1a.ifue306u.kuivcneq.t63ysoy8 [role=button]',
rightSidebarSegments: '.oajrlxb2.gs1a9yip.g5ia77u1.mtkw9kbi.tlpljxtp.qensuy8j.ppp5ayq2.goun2846.ccm00jje.s44p3ltw.mk2mc5f4.rt8b4zig.n8ej3o3l.agehan2d.sk4xxmp2.rq0escxv.nhd2j8a9.mg4g778l.pfnyh3mw.p7hjln8o.kvgmc6g5.cxmmr5t8.oygrvhab.hcukyx3x.tgvbjcpo.hpfvmrgz.jb3vyjys.rz4wbd8a.qt6c0cv9.a8nywdso.l9j0dhe7.i1ao9s8h.esuyzwwr.f1sip0of.du4w35lb.btwxx1t3.abiwlrkh.p8dawk7l.j83agx80.lzcic4wl.beltcj47.p86d2i9g.aot14ch1.kzx2olss',
Expand Down
5 changes: 5 additions & 0 deletions source/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,11 @@ const notifications = new Map();
ipc.answerRenderer(
'notification',
({id, title, body, icon, silent}: {id: number; title: string; body: string; icon: string; silent: boolean}) => {
// Don't send notifications when the window is focused
if (mainWindow.isFocused()) {
return;
}

const notification = new Notification({
title,
body: config.get('notificationMessagePreview') ? body : 'You have a new message',
Expand Down