@@ -51,6 +51,8 @@ export class GmailElementReplacer extends WebmailElementReplacer {
5151 msgInner : 'div.a3s:visible:not(.undefined), .message_inner_body:visible' ,
5252 msgInnerText : 'table.cf.An' ,
5353 msgInnerContainingPgp : "div.a3s:not(.undefined):contains('" + PgpArmor . headers ( 'null' ) . begin + "')" ,
54+ msgActionsBtn : '.J-J5-Ji.aap' ,
55+ msgActionsMenu : '.b7.J-M' ,
5456 attachmentsContainerOuter : 'div.hq.gt' ,
5557 attachmentsContainerInner : 'div.aQH' ,
5658 translatePrompt : '.adI, .wl4W9b' ,
@@ -85,10 +87,12 @@ export class GmailElementReplacer extends WebmailElementReplacer {
8587 ] ;
8688 } ;
8789
88- public setReplyBoxEditable = async ( ) => {
90+ public setReplyBoxEditable = async ( replyOption ?: ReplyOption ) => {
8991 const replyContainerIframe = $ ( '.reply_message_iframe_container > iframe' ) . last ( ) ;
9092 if ( replyContainerIframe . length ) {
91- $ ( replyContainerIframe ) . replaceWith ( this . factory . embeddedReply ( this . getLastMsgReplyParams ( this . getConvoRootEl ( replyContainerIframe [ 0 ] ) ) , true ) ) ; // xss-safe-value
93+ $ ( replyContainerIframe ) . replaceWith (
94+ this . factory . embeddedReply ( this . getLastMsgReplyParams ( this . getConvoRootEl ( replyContainerIframe [ 0 ] ) , replyOption ) , true )
95+ ) ; // xss-safe-value
9296 } else {
9397 await this . replaceStandardReplyBox ( undefined , true ) ;
9498 }
@@ -147,6 +151,7 @@ export class GmailElementReplacer extends WebmailElementReplacer {
147151 this . replaceArmoredBlocks ( ) . catch ( Catch . reportErr ) ;
148152 this . replaceAttachments ( ) . catch ( Catch . reportErr ) ;
149153 this . replaceComposeDraftLinks ( ) ;
154+ this . replaceActionsMenu ( ) ;
150155 this . replaceConvoBtns ( ) ;
151156 this . replaceStandardReplyBox ( ) . catch ( Catch . reportErr ) ;
152157 this . evaluateStandardComposeRecipients ( ) . catch ( Catch . reportErr ) ;
@@ -276,6 +281,22 @@ export class GmailElementReplacer extends WebmailElementReplacer {
276281 return ! ! $ ( 'iframe.pgp_block' ) . filter ( ':visible' ) . length ;
277282 } ;
278283
284+ private addMenuButton = ( action : 'reply' | 'forward' , selector : string ) => {
285+ const gmailActionsMenuContainer = $ ( this . sel . msgActionsMenu ) . find ( selector ) ;
286+ const button = $ ( this . factory . actionsMenuBtn ( action ) ) . insertAfter ( gmailActionsMenuContainer ) ; // xss-safe-factory
287+ button . on (
288+ 'click' ,
289+ Ui . event . handle ( ( el , ev : JQuery . Event ) => this . actionActivateSecureReplyHandler ( el , ev ) )
290+ ) ;
291+ } ;
292+
293+ private replaceActionsMenu = ( ) => {
294+ if ( $ ( '.action_menu_message_button' ) . length <= 0 ) {
295+ this . addMenuButton ( 'reply' , '#r' ) ;
296+ this . addMenuButton ( 'forward' , '#r3' ) ;
297+ }
298+ } ;
299+
279300 private replaceConvoBtns = ( force = false ) => {
280301 const convoUpperIconsContainer = $ ( 'div.hj:visible' ) ;
281302 const convoUpperIcons = $ ( 'span.pYTkkf-JX-ank-Rtc0Jf' ) ;
@@ -346,20 +367,26 @@ export class GmailElementReplacer extends WebmailElementReplacer {
346367
347368 private actionActivateSecureReplyHandler = async ( btn : HTMLElement , event : JQuery . Event ) => {
348369 event . stopImmediatePropagation ( ) ;
370+ const secureReplyInvokedFromMenu = btn . className . includes ( 'action_menu_message_button' ) ;
371+ const replyOption : ReplyOption = btn . className . includes ( 'reply' ) ? 'a_reply' : 'a_forward' ;
349372 if ( $ ( '#switch_to_encrypted_reply' ) . length ) {
350373 $ ( '#switch_to_encrypted_reply' ) . trigger ( 'click' ) ;
351374 return ;
352375 }
353376 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
354- const messageContainer = $ ( btn . closest ( '.h7' ) ! ) ;
377+ const messageContainer = secureReplyInvokedFromMenu ? $ ( '.T-I-JO.T-I-Kq' ) . closest ( '.h7' ) : $ ( btn . closest ( '.h7' ) ! ) ;
355378 if ( messageContainer . is ( ':last-child' ) ) {
356379 if ( this . isEncrypted ( ) ) {
357- await this . setReplyBoxEditable ( ) ;
380+ await this . setReplyBoxEditable ( replyOption ) ;
358381 } else {
359382 await this . replaceStandardReplyBox ( undefined , true ) ;
360383 }
361384 } else {
362- this . insertEncryptedReplyBox ( messageContainer ) ;
385+ this . insertEncryptedReplyBox ( messageContainer , replyOption ) ;
386+ }
387+ if ( secureReplyInvokedFromMenu ) {
388+ $ ( this . sel . msgActionsBtn ) . removeClass ( 'T-I-JO T-I-Kq' ) ;
389+ $ ( this . sel . msgActionsMenu ) . hide ( ) ;
363390 }
364391 } ;
365392
@@ -589,18 +616,18 @@ export class GmailElementReplacer extends WebmailElementReplacer {
589616 return from ? Str . parseEmail ( from ) : undefined ;
590617 } ;
591618
592- private getLastMsgReplyParams = ( convoRootEl : JQuery ) : FactoryReplyParams => {
593- return { replyMsgId : this . determineMsgId ( $ ( convoRootEl ) . find ( this . sel . msgInner ) . last ( ) ) } ;
619+ private getLastMsgReplyParams = ( convoRootEl : JQuery , replyOption ?: ReplyOption ) : FactoryReplyParams => {
620+ return { replyMsgId : this . determineMsgId ( $ ( convoRootEl ) . find ( this . sel . msgInner ) . last ( ) ) , replyOption } ;
594621 } ;
595622
596623 private getConvoRootEl = ( anyInnerElement : HTMLElement ) => {
597624 return $ ( anyInnerElement ) . closest ( 'div.if, div.aHU, td.Bu' ) . first ( ) ;
598625 } ;
599626
600- private insertEncryptedReplyBox = ( messageContainer : JQuery < Element > ) => {
627+ private insertEncryptedReplyBox = ( messageContainer : JQuery < Element > , replyOption : ReplyOption ) => {
601628 const msgIdElement = messageContainer . find ( '[data-legacy-message-id], [data-message-id]' ) ;
602629 const msgId = msgIdElement . attr ( 'data-legacy-message-id' ) || msgIdElement . attr ( 'data-message-id' ) ;
603- const replyParams : FactoryReplyParams = { replyMsgId : msgId , removeAfterClose : true } ;
630+ const replyParams : FactoryReplyParams = { replyMsgId : msgId , removeAfterClose : true , replyOption } ;
604631 const secureReplyBoxXssSafe = /* xss-safe-factory */ `<div class="remove_borders reply_message_iframe_container inserted">${ this . factory . embeddedReply (
605632 replyParams ,
606633 true ,
0 commit comments