Skip to content

Commit 7ed2cc1

Browse files
authored
Fix Conversation Controls and Search Function (#2198)
* Fix 'Search in Conversation' for new design * Fix conversation controls (archive, delete) The search in conversation functionality requires the right sidebar to be open. The method to select the search button is simplified to just the last button.
1 parent df4c054 commit 7ed2cc1

File tree

4 files changed

+58
-68
lines changed

4 files changed

+58
-68
lines changed

css/browser.css

+1-6
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ body::-webkit-scrollbar {
102102
}
103103

104104
/* A utility class for temporarily hiding all dropdown menus */
105-
html.hide-dropdowns [role='menu'].l9j0dhe7.swg4t2nn {
105+
html.hide-dropdowns [role='menu'].x1n2onr6.xi5betq {
106106
visibility: hidden !important;
107107
}
108108

@@ -111,11 +111,6 @@ html.hide-preferences-window div[class='x9f619 x1n2onr6 x1ja2u2z'] > div:nth-of-
111111
display: none;
112112
}
113113

114-
/* A utility class for temporarily hiding right sidebar */
115-
html.hide-r-sidebar .rq0escxv.l9j0dhe7.du4w35lb.j83agx80.g5gj957u.rj1gh0hx.buofh1pr.hpfvmrgz.i1fnvgqd.gs1a9yip.owycx6da.btwxx1t3.jb3vyjys.nwf6jgls > div:nth-child(2) {
116-
display: none;
117-
}
118-
119114
/* -- Private mode -- */
120115
/* Preferences button: profile picture */
121116
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 {

source/browser.ts

+52-45
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ async function withMenu(
2121
menuButtonElement.click();
2222

2323
// Wait for the menu to close before removing the 'hide-dropdowns' class
24-
const menuLayer = document.querySelector('.j83agx80.cbu4d94t.l9j0dhe7.jgljxmt5.be9z9djy > div:nth-child(2) > div');
24+
await elementReady('.x78zum5.xdt5ytf.x1n2onr6.xat3117.xxzkxad > div:nth-child(2) > div', {stopOnDomReady: false});
25+
const menuLayer = document.querySelector('.x78zum5.xdt5ytf.x1n2onr6.xat3117.xxzkxad > div:nth-child(2) > div');
2526

2627
if (menuLayer) {
2728
const observer = new MutationObserver(() => {
@@ -149,45 +150,18 @@ ipc.answerMain('find', () => {
149150
});
150151

151152
async function openSearchInConversation() {
152-
const mainView = document.querySelector('.rq0escxv.l9j0dhe7.du4w35lb.j83agx80.rj1gh0hx.buofh1pr.g5gj957u.hpfvmrgz.i1fnvgqd.gs1a9yip.owycx6da.btwxx1t3.jb3vyjys.gitj76qy')!;
153-
const rightSidebarIsClosed = Boolean(mainView.querySelector<HTMLElement>('div:only-child'));
153+
const mainView = document.querySelector('.x9f619.x1ja2u2z.x78zum5.x1n2onr6.x1r8uery.x1iyjqo2.xs83m0k.xeuugli.x1qughib.x1qjc9v5.xozqiw3.x1q0g3np.xexx8yu.x85a59c')!;
154+
const rightSidebarIsClosed = Boolean(mainView.querySelector<HTMLElement>(':scope > div:only-child'));
154155

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

160-
await elementReady(selectors.rightSidebarSegments, {stopOnDomReady: false});
161-
const segments = document.querySelectorAll<HTMLElement>(selectors.rightSidebarSegments).length;
162-
// If there are three segmetns in right sidebar (two users chat) then button index is 4
163-
// If there are not three segments (usually four, it's a group chat) then button index is 6
164-
const buttonIndex = segments === 3 ? 4 : 6;
165-
166160
await elementReady(selectors.rightSidebarButtons, {stopOnDomReady: false});
167161
const buttonList = document.querySelectorAll<HTMLElement>(selectors.rightSidebarButtons);
168162

169-
if (buttonList.length > buttonIndex) {
170-
buttonList[buttonIndex].click();
171-
}
172-
173-
// If right sidebar was closed when shortcut was clicked, then close it back.
174-
if (rightSidebarIsClosed) {
175-
document.querySelector<HTMLElement>('.j9ispegn.pmk7jnqg.k4urcfbm.datstx6m.b5wmifdl.kr520xx4.mdpwds66.b2cqd1jy.n13yt9zj.eh67sqbx')?.click();
176-
177-
// Observe sidebar so when it's hidden, remove the utility class. This prevents split
178-
// display of sidebar.
179-
const sidebarObserver = new MutationObserver(records => {
180-
const removedRecords = records.filter(({removedNodes}) => removedNodes.length > 0 && (removedNodes[0] as HTMLElement).tagName === 'DIV');
181-
182-
// In case there is a div removed, hide utility class and stop observing
183-
if (removedRecords.length > 0) {
184-
document.documentElement.classList.remove('hide-r-sidebar');
185-
sidebarObserver.disconnect();
186-
}
187-
});
188-
189-
sidebarObserver.observe(mainView, {childList: true, subtree: true});
190-
}
163+
// Search in conversation is the last button
164+
buttonList[buttonList.length - 1].click();
191165
}
192166

193167
ipc.answerMain('search', () => {
@@ -223,14 +197,21 @@ ipc.answerMain('mute-conversation', async () => {
223197
});
224198

225199
ipc.answerMain('delete-conversation', async () => {
226-
await deleteSelectedConversation();
200+
const index = selectedConversationIndex();
201+
202+
if (index !== -1) {
203+
await deleteSelectedConversation();
204+
205+
const key = index + 1;
206+
await jumpToConversation(key);
207+
}
227208
});
228209

229-
ipc.answerMain('hide-conversation', async () => {
210+
ipc.answerMain('archive-conversation', async () => {
230211
const index = selectedConversationIndex();
231212

232213
if (index !== -1) {
233-
await hideSelectedConversation();
214+
await archiveSelectedConversation();
234215

235216
const key = index + 1;
236217
await jumpToConversation(key);
@@ -588,7 +569,7 @@ function selectedConversationIndex(offset = 0): number {
588569
return -1;
589570
}
590571

591-
const newSelected = selected.parentNode!.parentNode!.parentNode! as HTMLElement;
572+
const newSelected = selected.closest(`${selectors.conversationList} > div`)!;
592573

593574
const list = [...newSelected.parentNode!.children];
594575
const index = list.indexOf(newSelected) + offset;
@@ -605,7 +586,7 @@ async function setZoom(zoomFactor: number): Promise<void> {
605586
async function withConversationMenu(callback: () => void): Promise<void> {
606587
// eslint-disable-next-line @typescript-eslint/ban-types
607588
let menuButton: HTMLElement | null = null;
608-
const conversation = document.querySelector<HTMLElement>(`${selectors.selectedConversation}`)?.parentElement?.parentElement?.parentElement?.parentElement;
589+
const conversation = document.querySelector<HTMLElement>(selectors.selectedConversation)!.closest(`${selectors.conversationList} > div`);
609590

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

@@ -621,27 +602,53 @@ async function openMuteModal(): Promise<void> {
621602
}
622603

623604
/*
624-
This function assumes:
605+
These functions assume:
625606
- There is a selected conversation.
626607
- That the conversation already has its conversation menu open.
627608
628609
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.
629610
*/
630611
function isSelectedConversationGroup(): boolean {
631-
return Boolean(document.querySelector<HTMLElement>(`${selectors.conversationMenuSelectorNewDesign} [role=menuitem]:nth-child(4)`));
612+
// Individual conversations include an entry for "View Profile", which is type `a`
613+
return !document.querySelector<HTMLElement>(`${selectors.conversationMenuSelectorNewDesign} a[role=menuitem]`);
632614
}
633615

634-
async function hideSelectedConversation(): Promise<void> {
616+
function isSelectedConversationMetaAI(): boolean {
617+
// Meta AI menu only has 1 separator of type `hr`
618+
return !document.querySelector<HTMLElement>(`${selectors.conversationMenuSelectorNewDesign} hr:nth-of-type(2)`);
619+
}
620+
621+
async function archiveSelectedConversation(): Promise<void> {
635622
await withConversationMenu(() => {
636-
const [isGroup, isNotGroup] = [5, 6];
637-
selectMenuItem(isSelectedConversationGroup() ? isGroup : isNotGroup);
623+
const [isGroup, isNotGroup, isMetaAI] = [-4, -3, -2];
624+
625+
let archiveMenuIndex;
626+
if (isSelectedConversationMetaAI()) {
627+
archiveMenuIndex = isMetaAI;
628+
} else if (isSelectedConversationGroup()) {
629+
archiveMenuIndex = isGroup;
630+
} else {
631+
archiveMenuIndex = isNotGroup;
632+
}
633+
634+
selectMenuItem(archiveMenuIndex);
638635
});
639636
}
640637

641638
async function deleteSelectedConversation(): Promise<void> {
642639
await withConversationMenu(() => {
643-
const [isGroup, isNotGroup] = [6, 7];
644-
selectMenuItem(isSelectedConversationGroup() ? isGroup : isNotGroup);
640+
const [isGroup, isNotGroup, isMetaAI] = [-3, -2, -1];
641+
642+
let deleteMenuIndex;
643+
if (isSelectedConversationMetaAI()) {
644+
deleteMenuIndex = isMetaAI;
645+
} else if (isSelectedConversationGroup()) {
646+
deleteMenuIndex = isGroup;
647+
} else {
648+
deleteMenuIndex = isNotGroup;
649+
}
650+
651+
selectMenuItem(deleteMenuIndex);
645652
});
646653
}
647654

source/browser/selectors.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ export default {
88
conversationSidebarTextSelector: '[class="x1lliihq x193iq5w x6ikm8r x10wlt62 xlyipyv xuxw1ft"]', // Generic selector for the text contents of all conversations
99
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)
1010
notificationCheckbox: '._374b:nth-of-type(4) ._4ng2 input',
11-
rightSidebarButtons: '.rq0escxv.l9j0dhe7.du4w35lb.j83agx80.cbu4d94t.g5gj957u.f4tghd1a.ifue306u.kuivcneq.t63ysoy8 [role=button]',
12-
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',
11+
rightSidebarMenu: '.x6s0dn4.x3nfvp2.x1fgtraw.xl56j7k.x1n2onr6.xgd8bvy',
12+
rightSidebarButtons: '.x9f619.x1ja2u2z.x78zum5.x2lah0s.x1n2onr6.xl56j7k.x1qjc9v5.xozqiw3.x1q0g3np.xn6708d.x1ye3gou.x1cnzs8.xdj266r.x11i5rnm.xat24cr.x1mh8g0r > div [role=button]',
1313
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"]',
1414
// ! Very fragile selector (most likely cause of hidden dialog issue)
1515
closePreferencesButton: 'div[role=dialog] > div > div > div:nth-child(2) > [role=button]',
1616
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',
1717
userMenuNewSidebar: '[role=navigation] > div > div:nth-child(2) > div > div > div:nth-child(1) [role=button]',
1818
viewsMenu: '.x9f619.x1n2onr6.x1ja2u2z.x78zum5.xdt5ytf.x2lah0s.x193iq5w.xdj266r',
19-
selectedConversation: '[role=navigation] [role=grid] [role=row] [role=gridcell] [role=link][aria-current]',
19+
selectedConversation: '[role=navigation] [role=grid] [role=row] [role=gridcell] [role=link][aria-current=page]',
2020
// ! Very fragile selector (most likely cause of hidden dialog issue)
2121
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"]',
2222
// TODO: Fix this selector for new design

source/menu.ts

+2-14
Original file line numberDiff line numberDiff line change
@@ -578,45 +578,35 @@ Press Command/Ctrl+R in Caprine to see your changes.
578578

579579
const conversationSubmenu: MenuItemConstructorOptions[] = [
580580
{
581-
/* TODO: Fix conversation controls */
582581
label: 'Mute Conversation',
583-
visible: is.development,
584582
accelerator: 'CommandOrControl+Shift+M',
585583
click() {
586584
sendAction('mute-conversation');
587585
},
588586
},
589587
{
590-
/* TODO: Fix conversation controls */
591-
label: 'Hide Conversation',
592-
visible: is.development,
588+
label: 'Archive Conversation',
593589
accelerator: 'CommandOrControl+Shift+H',
594590
click() {
595-
sendAction('hide-conversation');
591+
sendAction('archive-conversation');
596592
},
597593
},
598594
{
599-
/* TODO: Fix conversation controls */
600595
label: 'Delete Conversation',
601-
visible: is.development,
602596
accelerator: 'CommandOrControl+Shift+D',
603597
click() {
604598
sendAction('delete-conversation');
605599
},
606600
},
607601
{
608-
/* TODO: Fix conversation controls */
609602
label: 'Select Next Conversation',
610-
visible: is.development,
611603
accelerator: 'Control+Tab',
612604
click() {
613605
sendAction('next-conversation');
614606
},
615607
},
616608
{
617-
/* TODO: Fix conversation controls */
618609
label: 'Select Previous Conversation',
619-
visible: is.development,
620610
accelerator: 'Control+Shift+Tab',
621611
click() {
622612
sendAction('previous-conversation');
@@ -630,9 +620,7 @@ Press Command/Ctrl+R in Caprine to see your changes.
630620
},
631621
},
632622
{
633-
/* TODO: Fix conversation controls */
634623
label: 'Search in Conversation',
635-
visible: is.development,
636624
accelerator: 'CommandOrControl+F',
637625
click() {
638626
sendAction('search');

0 commit comments

Comments
 (0)