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 Conversation Controls and Search Function #2198

Merged
merged 6 commits into from
Nov 29, 2024
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
7 changes: 1 addition & 6 deletions css/browser.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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 {
Expand Down
97 changes: 52 additions & 45 deletions source/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
Expand Down Expand Up @@ -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<HTMLElement>('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<HTMLElement>(':scope > div:only-child'));

if (rightSidebarIsClosed) {
document.documentElement.classList.add('hide-r-sidebar');
document.querySelector<HTMLElement>('.j9ispegn.pmk7jnqg.k4urcfbm.datstx6m.b5wmifdl.kr520xx4.mdpwds66.b2cqd1jy.n13yt9zj.eh67sqbx')?.click();
document.querySelector<HTMLElement>(selectors.rightSidebarMenu)?.click();
}

await elementReady(selectors.rightSidebarSegments, {stopOnDomReady: false});
const segments = document.querySelectorAll<HTMLElement>(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<HTMLElement>(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<HTMLElement>('.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', () => {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -609,7 +590,7 @@ async function setZoom(zoomFactor: number): Promise<void> {
async function withConversationMenu(callback: () => void): Promise<void> {
// eslint-disable-next-line @typescript-eslint/ban-types
let menuButton: HTMLElement | null = null;
const conversation = document.querySelector<HTMLElement>(`${selectors.selectedConversation}`)?.parentElement?.parentElement?.parentElement?.parentElement;
const conversation = document.querySelector<HTMLElement>(selectors.selectedConversation)!.closest(`${selectors.conversationList} > div`);

menuButton = conversation?.querySelector('[aria-label=Menu][role=button]') ?? null;

Expand All @@ -625,27 +606,53 @@ async function openMuteModal(): Promise<void> {
}

/*
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<HTMLElement>(`${selectors.conversationMenuSelectorNewDesign} [role=menuitem]:nth-child(4)`));
// Individual conversations include an entry for "View Profile", which is type `a`
return !document.querySelector<HTMLElement>(`${selectors.conversationMenuSelectorNewDesign} a[role=menuitem]`);
}

async function hideSelectedConversation(): Promise<void> {
function isSelectedConversationMetaAI(): boolean {
// Meta AI menu only has 1 separator of type `hr`
return !document.querySelector<HTMLElement>(`${selectors.conversationMenuSelectorNewDesign} hr:nth-of-type(2)`);
}

async function archiveSelectedConversation(): Promise<void> {
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<void> {
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);
});
}

Expand Down
6 changes: 3 additions & 3 deletions source/browser/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 2 additions & 14 deletions source/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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');
Expand Down