diff --git a/css/browser.css b/css/browser.css index f64de76f1..07c9bf498 100644 --- a/css/browser.css +++ b/css/browser.css @@ -102,7 +102,7 @@ body::-webkit-scrollbar { } /* A utility class for temporarily hiding all dropdown menus */ -html.hide-dropdowns [role='menu'].l9j0dhe7.swg4t2nn { +html.hide-dropdowns [role='menu'].x1n2onr6.xi5betq { visibility: hidden !important; } @@ -111,11 +111,6 @@ html.hide-preferences-window div[class='x9f619 x1n2onr6 x1ja2u2z'] > div:nth-of- display: none; } -/* A utility class for temporarily hiding right sidebar */ -html.hide-r-sidebar .rq0escxv.l9j0dhe7.du4w35lb.j83agx80.g5gj957u.rj1gh0hx.buofh1pr.hpfvmrgz.i1fnvgqd.gs1a9yip.owycx6da.btwxx1t3.jb3vyjys.nwf6jgls > div:nth-child(2) { - display: none; -} - /* -- Private mode -- */ /* Preferences button: profile picture */ html.private-mode [role='navigation'] .qi72231t.o9w3sbdw.nu7423ey.tav9wjvu.flwp5yud.tghlliq5.gkg15gwv.s9ok87oh.s9ljgwtm.lxqftegz.bf1zulr9.frfouenu.bonavkto.djs4p424.r7bn319e.bdao358l.fsf7x5fv.tgm57n0e.jez8cy9q.s5oniofx.m8h3af8h.l7ghb35v.kjdc1dyq.kmwttqpk.dnr7xe2t.aeinzg81.srn514ro.oxkhqvkx.rl78xhln.nch0832m.om3e55n1.cr00lzj9.rn8ck1ys.s3jn8y49.g4tp4svg.o9erhkwx.dzqi5evh.hupbnkgi.hvb2xoa8.fxk3tzhb.jl2a5g8c.f14ij5to.l3ldwz01.icdlwmnq { diff --git a/source/browser.ts b/source/browser.ts index ab70c526e..0ac6a1074 100644 --- a/source/browser.ts +++ b/source/browser.ts @@ -21,7 +21,8 @@ async function withMenu( menuButtonElement.click(); // Wait for the menu to close before removing the 'hide-dropdowns' class - const menuLayer = document.querySelector('.j83agx80.cbu4d94t.l9j0dhe7.jgljxmt5.be9z9djy > div:nth-child(2) > div'); + await elementReady('.x78zum5.xdt5ytf.x1n2onr6.xat3117.xxzkxad > div:nth-child(2) > div', {stopOnDomReady: false}); + const menuLayer = document.querySelector('.x78zum5.xdt5ytf.x1n2onr6.xat3117.xxzkxad > div:nth-child(2) > div'); if (menuLayer) { const observer = new MutationObserver(() => { @@ -149,45 +150,18 @@ ipc.answerMain('find', () => { }); async function openSearchInConversation() { - const mainView = document.querySelector('.rq0escxv.l9j0dhe7.du4w35lb.j83agx80.rj1gh0hx.buofh1pr.g5gj957u.hpfvmrgz.i1fnvgqd.gs1a9yip.owycx6da.btwxx1t3.jb3vyjys.gitj76qy')!; - const rightSidebarIsClosed = Boolean(mainView.querySelector('div:only-child')); + const mainView = document.querySelector('.x9f619.x1ja2u2z.x78zum5.x1n2onr6.x1r8uery.x1iyjqo2.xs83m0k.xeuugli.x1qughib.x1qjc9v5.xozqiw3.x1q0g3np.xexx8yu.x85a59c')!; + const rightSidebarIsClosed = Boolean(mainView.querySelector(':scope > div:only-child')); if (rightSidebarIsClosed) { - document.documentElement.classList.add('hide-r-sidebar'); - document.querySelector('.j9ispegn.pmk7jnqg.k4urcfbm.datstx6m.b5wmifdl.kr520xx4.mdpwds66.b2cqd1jy.n13yt9zj.eh67sqbx')?.click(); + document.querySelector(selectors.rightSidebarMenu)?.click(); } - await elementReady(selectors.rightSidebarSegments, {stopOnDomReady: false}); - const segments = document.querySelectorAll(selectors.rightSidebarSegments).length; - // If there are three segmetns in right sidebar (two users chat) then button index is 4 - // If there are not three segments (usually four, it's a group chat) then button index is 6 - const buttonIndex = segments === 3 ? 4 : 6; - await elementReady(selectors.rightSidebarButtons, {stopOnDomReady: false}); const buttonList = document.querySelectorAll(selectors.rightSidebarButtons); - if (buttonList.length > buttonIndex) { - buttonList[buttonIndex].click(); - } - - // If right sidebar was closed when shortcut was clicked, then close it back. - if (rightSidebarIsClosed) { - document.querySelector('.j9ispegn.pmk7jnqg.k4urcfbm.datstx6m.b5wmifdl.kr520xx4.mdpwds66.b2cqd1jy.n13yt9zj.eh67sqbx')?.click(); - - // Observe sidebar so when it's hidden, remove the utility class. This prevents split - // display of sidebar. - const sidebarObserver = new MutationObserver(records => { - const removedRecords = records.filter(({removedNodes}) => removedNodes.length > 0 && (removedNodes[0] as HTMLElement).tagName === 'DIV'); - - // In case there is a div removed, hide utility class and stop observing - if (removedRecords.length > 0) { - document.documentElement.classList.remove('hide-r-sidebar'); - sidebarObserver.disconnect(); - } - }); - - sidebarObserver.observe(mainView, {childList: true, subtree: true}); - } + // Search in conversation is the last button + buttonList[buttonList.length - 1].click(); } ipc.answerMain('search', () => { @@ -223,14 +197,21 @@ ipc.answerMain('mute-conversation', async () => { }); ipc.answerMain('delete-conversation', async () => { - await deleteSelectedConversation(); + const index = selectedConversationIndex(); + + if (index !== -1) { + await deleteSelectedConversation(); + + const key = index + 1; + await jumpToConversation(key); + } }); -ipc.answerMain('hide-conversation', async () => { +ipc.answerMain('archive-conversation', async () => { const index = selectedConversationIndex(); if (index !== -1) { - await hideSelectedConversation(); + await archiveSelectedConversation(); const key = index + 1; await jumpToConversation(key); @@ -592,7 +573,7 @@ function selectedConversationIndex(offset = 0): number { return -1; } - const newSelected = selected.parentNode!.parentNode!.parentNode! as HTMLElement; + const newSelected = selected.closest(`${selectors.conversationList} > div`)!; const list = [...newSelected.parentNode!.children]; const index = list.indexOf(newSelected) + offset; @@ -609,7 +590,7 @@ async function setZoom(zoomFactor: number): Promise { async function withConversationMenu(callback: () => void): Promise { // eslint-disable-next-line @typescript-eslint/ban-types let menuButton: HTMLElement | null = null; - const conversation = document.querySelector(`${selectors.selectedConversation}`)?.parentElement?.parentElement?.parentElement?.parentElement; + const conversation = document.querySelector(selectors.selectedConversation)!.closest(`${selectors.conversationList} > div`); menuButton = conversation?.querySelector('[aria-label=Menu][role=button]') ?? null; @@ -625,27 +606,53 @@ async function openMuteModal(): Promise { } /* -This function assumes: +These functions assume: - There is a selected conversation. - That the conversation already has its conversation menu open. In other words, you should only use this function within a callback that is provided to `withConversationMenu()`, because `withConversationMenu()` makes sure to have the conversation menu open before executing the callback and closes the conversation menu afterwards. */ function isSelectedConversationGroup(): boolean { - return Boolean(document.querySelector(`${selectors.conversationMenuSelectorNewDesign} [role=menuitem]:nth-child(4)`)); + // Individual conversations include an entry for "View Profile", which is type `a` + return !document.querySelector(`${selectors.conversationMenuSelectorNewDesign} a[role=menuitem]`); } -async function hideSelectedConversation(): Promise { +function isSelectedConversationMetaAI(): boolean { + // Meta AI menu only has 1 separator of type `hr` + return !document.querySelector(`${selectors.conversationMenuSelectorNewDesign} hr:nth-of-type(2)`); +} + +async function archiveSelectedConversation(): Promise { await withConversationMenu(() => { - const [isGroup, isNotGroup] = [5, 6]; - selectMenuItem(isSelectedConversationGroup() ? isGroup : isNotGroup); + const [isGroup, isNotGroup, isMetaAI] = [-4, -3, -2]; + + let archiveMenuIndex; + if (isSelectedConversationMetaAI()) { + archiveMenuIndex = isMetaAI; + } else if (isSelectedConversationGroup()) { + archiveMenuIndex = isGroup; + } else { + archiveMenuIndex = isNotGroup; + } + + selectMenuItem(archiveMenuIndex); }); } async function deleteSelectedConversation(): Promise { await withConversationMenu(() => { - const [isGroup, isNotGroup] = [6, 7]; - selectMenuItem(isSelectedConversationGroup() ? isGroup : isNotGroup); + const [isGroup, isNotGroup, isMetaAI] = [-3, -2, -1]; + + let deleteMenuIndex; + if (isSelectedConversationMetaAI()) { + deleteMenuIndex = isMetaAI; + } else if (isSelectedConversationGroup()) { + deleteMenuIndex = isGroup; + } else { + deleteMenuIndex = isNotGroup; + } + + selectMenuItem(deleteMenuIndex); }); } diff --git a/source/browser/selectors.ts b/source/browser/selectors.ts index 5d8d0d1ca..4e99164fd 100644 --- a/source/browser/selectors.ts +++ b/source/browser/selectors.ts @@ -8,15 +8,15 @@ export default { 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', + rightSidebarMenu: '.x6s0dn4.x3nfvp2.x1fgtraw.xl56j7k.x1n2onr6.xgd8bvy', + rightSidebarButtons: '.x9f619.x1ja2u2z.x78zum5.x2lah0s.x1n2onr6.xl56j7k.x1qjc9v5.xozqiw3.x1q0g3np.xn6708d.x1ye3gou.x1cnzs8.xdj266r.x11i5rnm.xat24cr.x1mh8g0r > div [role=button]', muteIconNewDesign: 'path[d="M29.676 7.746c.353-.352.44-.92.15-1.324a1 1 0 00-1.524-.129L6.293 28.29a1 1 0 00.129 1.523c.404.29.972.204 1.324-.148l3.082-3.08A2.002 2.002 0 0112.242 26h15.244c.848 0 1.57-.695 1.527-1.541-.084-1.643-1.87-1.145-2.2-3.515l-1.073-8.157-.002-.01a1.976 1.976 0 01.562-1.656l3.376-3.375zm-9.165 20.252H15.51c-.313 0-.565.275-.506.575.274 1.38 1.516 2.422 3.007 2.422 1.49 0 2.731-1.042 3.005-2.422.06-.3-.193-.575-.505-.575zm-10.064-6.719L22.713 9.02a.997.997 0 00-.124-1.51 7.792 7.792 0 00-12.308 5.279l-1.04 7.897c-.089.672.726 1.074 1.206.594z"]', // ! Very fragile selector (most likely cause of hidden dialog issue) closePreferencesButton: 'div[role=dialog] > div > div > div:nth-child(2) > [role=button]', userMenu: '.qi72231t.o9w3sbdw.nu7423ey.tav9wjvu.flwp5yud.tghlliq5.gkg15gwv.s9ok87oh.s9ljgwtm.lxqftegz.bf1zulr9.frfouenu.bonavkto.djs4p424.r7bn319e.bdao358l.fsf7x5fv.tgm57n0e.jez8cy9q.s5oniofx.m8h3af8h.l7ghb35v.kjdc1dyq.kmwttqpk.dnr7xe2t.aeinzg81.srn514ro.oxkhqvkx.rl78xhln.nch0832m.om3e55n1.cr00lzj9.rn8ck1ys.s3jn8y49.g4tp4svg.o9erhkwx.dzqi5evh.hupbnkgi.hvb2xoa8.fxk3tzhb.jl2a5g8c.f14ij5to.l3ldwz01.icdlwmnq > .aglvbi8b.om3e55n1.i8zpp7h3.g4tp4svg', userMenuNewSidebar: '[role=navigation] > div > div:nth-child(2) > div > div > div:nth-child(1) [role=button]', viewsMenu: '.x9f619.x1n2onr6.x1ja2u2z.x78zum5.xdt5ytf.x2lah0s.x193iq5w.xdj266r', - selectedConversation: '[role=navigation] [role=grid] [role=row] [role=gridcell] [role=link][aria-current]', + selectedConversation: '[role=navigation] [role=grid] [role=row] [role=gridcell] [role=link][aria-current=page]', // ! Very fragile selector (most likely cause of hidden dialog issue) preferencesSelector: 'div[role=dialog][class="x1n2onr6 x1ja2u2z x1afcbsf x78zum5 xdt5ytf x1a2a7pz x6ikm8r x10wlt62 x71s49j x1jx94hy x1g2kw80 xxadwq3 x16n5opg x3hh19s xl7ujzl x1kl8bxo xhkep3z xb3b7hn xwhkkir xeb55yp x17omtbh"]', // TODO: Fix this selector for new design diff --git a/source/menu.ts b/source/menu.ts index 6068f3815..8169501aa 100644 --- a/source/menu.ts +++ b/source/menu.ts @@ -584,45 +584,35 @@ Press Command/Ctrl+R in Caprine to see your changes. const conversationSubmenu: MenuItemConstructorOptions[] = [ { - /* TODO: Fix conversation controls */ label: 'Mute Conversation', - visible: is.development, accelerator: 'CommandOrControl+Shift+M', click() { sendAction('mute-conversation'); }, }, { - /* TODO: Fix conversation controls */ - label: 'Hide Conversation', - visible: is.development, + label: 'Archive Conversation', accelerator: 'CommandOrControl+Shift+H', click() { - sendAction('hide-conversation'); + sendAction('archive-conversation'); }, }, { - /* TODO: Fix conversation controls */ label: 'Delete Conversation', - visible: is.development, accelerator: 'CommandOrControl+Shift+D', click() { sendAction('delete-conversation'); }, }, { - /* TODO: Fix conversation controls */ label: 'Select Next Conversation', - visible: is.development, accelerator: 'Control+Tab', click() { sendAction('next-conversation'); }, }, { - /* TODO: Fix conversation controls */ label: 'Select Previous Conversation', - visible: is.development, accelerator: 'Control+Shift+Tab', click() { sendAction('previous-conversation'); @@ -636,9 +626,7 @@ Press Command/Ctrl+R in Caprine to see your changes. }, }, { - /* TODO: Fix conversation controls */ label: 'Search in Conversation', - visible: is.development, accelerator: 'CommandOrControl+F', click() { sendAction('search');