From ab84f685c00f40f3fbe5fe437da24ee5ac4ffb87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20W=C3=B3jcik?= Date: Sun, 14 Sep 2025 06:34:43 +0000 Subject: [PATCH] =?UTF-8?q?BTHLABS-51:=20Chrome=20Web=20Extension=20Co-aut?= =?UTF-8?q?hored-by:=20Tomek=20W=C3=B3jcik=20=20Co-co?= =?UTF-8?q?mmitted-by:=20Tomek=20W=C3=B3jcik=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hotpocket_backend/apps/ui/constants.py | 1 + .../apps/ui/templatetags/ui.py | 4 ++++ .../hotpocket_backend/settings/webapp.py | 1 + services/extension/.gitignore | 1 + .../assets/_locales/en/messages.json | 4 ++++ services/extension/assets/images/icon-16.png | Bin 0 -> 874 bytes services/extension/assets/images/icon-32.png | Bin 0 -> 2197 bytes .../assets/images/toolbar-icon-16.png | Bin 0 -> 594 bytes .../assets/images/toolbar-icon-32.png | Bin 0 -> 1269 bytes services/extension/eslint.config.js | 6 +++--- services/extension/package.json | 5 ++++- services/extension/rollup.config.js | 10 ++++++++- services/extension/secrets/.placeholder | 0 services/extension/src/background/chrome.js | 6 ++++++ services/extension/src/content/chrome.js | 6 ++++++ services/extension/src/manifest/chrome.json | 13 ++++++++++++ services/extension/src/manifest/common.json | 6 ++---- services/extension/src/manifest/safari.json | 4 ++++ services/extension/tasks.py | 20 ++++++++++++++++++ .../hotpocket_common/constants/accounts.py | 1 + .../soa/hotpocket_soa/dto/accounts.py | 3 +++ 21 files changed, 82 insertions(+), 9 deletions(-) create mode 100644 services/extension/assets/images/icon-16.png create mode 100644 services/extension/assets/images/icon-32.png create mode 100644 services/extension/assets/images/toolbar-icon-16.png create mode 100644 services/extension/assets/images/toolbar-icon-32.png create mode 100644 services/extension/secrets/.placeholder create mode 100644 services/extension/src/background/chrome.js create mode 100644 services/extension/src/content/chrome.js create mode 100644 services/extension/src/manifest/chrome.json diff --git a/services/backend/hotpocket_backend/apps/ui/constants.py b/services/backend/hotpocket_backend/apps/ui/constants.py index df440d2..206b1aa 100644 --- a/services/backend/hotpocket_backend/apps/ui/constants.py +++ b/services/backend/hotpocket_backend/apps/ui/constants.py @@ -21,3 +21,4 @@ class StarUnstarAssociationViewMode(enum.Enum): class UIAccessTokenOriginApp(enum.Enum): SAFARI_WEB_EXTENSION = _('Safari Web Extension') + CHROME_EXTENSION = _('Chrome Extension') diff --git a/services/backend/hotpocket_backend/apps/ui/templatetags/ui.py b/services/backend/hotpocket_backend/apps/ui/templatetags/ui.py index 076a084..392b5b8 100644 --- a/services/backend/hotpocket_backend/apps/ui/templatetags/ui.py +++ b/services/backend/hotpocket_backend/apps/ui/templatetags/ui.py @@ -137,6 +137,10 @@ def render_access_token_app(access_token: AccessTokenOut) -> str: app = UIAccessTokenOriginApp[origin_app.value].value variant = 'info' + case AccessTokenOriginApp.CHROME_EXTENSION: + app = UIAccessTokenOriginApp[origin_app.value].value + variant = 'info' + return format_html( '{}', variant, diff --git a/services/backend/hotpocket_backend/settings/webapp.py b/services/backend/hotpocket_backend/settings/webapp.py index fd2c2ca..46e48bf 100644 --- a/services/backend/hotpocket_backend/settings/webapp.py +++ b/services/backend/hotpocket_backend/settings/webapp.py @@ -72,6 +72,7 @@ JSONRPC_METHOD_MODULES = [ CORS_ALLOWED_ORIGIN_REGEXES = [ r'safari-web-extension:\/\/.+?', + r'chrome-extension:\/\/.+?', ] CORS_ALLOW_CREDENTIALS = True CORS_ALLOW_HEADERS = ( diff --git a/services/extension/.gitignore b/services/extension/.gitignore index b947077..a9ef9e1 100644 --- a/services/extension/.gitignore +++ b/services/extension/.gitignore @@ -1,2 +1,3 @@ node_modules/ dist/ +secrets/*.pem diff --git a/services/extension/assets/_locales/en/messages.json b/services/extension/assets/_locales/en/messages.json index 411dea0..13ca3ef 100644 --- a/services/extension/assets/_locales/en/messages.json +++ b/services/extension/assets/_locales/en/messages.json @@ -3,6 +3,10 @@ "message": "Save to HotPocket", "description": "The display name for the extension." }, + "extension_name_development": { + "message": "HotPocket Development", + "description": "The display name for the extension." + }, "extension_description": { "message": "Save to HotPocket. Straight from the toolbar!", "description": "Description of what the extension does." diff --git a/services/extension/assets/images/icon-16.png b/services/extension/assets/images/icon-16.png new file mode 100644 index 0000000000000000000000000000000000000000..c74bd8624b56bc8e252a6779d1d28d4e8a78c0bc GIT binary patch literal 874 zcmV-w1C{)VP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR915TFA91ONa40RR915C8xG05nx`@&Et=)Ja4^R5%fZlh1D(MHI(BGrQhh z+p!Wmv7FHKkQlWWgb)!#s1hec2&YQ4=bm$FRXB6)r3b2BdaC#bsKfzPsssXB1uBA! zDk{-Z11d?KCTZftUjN$c3~#on=mjy}xBF)I`+YyYZ>#`s;lq#4E6a-buZsQOvI1yY z?6se7-uO%{ES>(iH$HbcE*?i-J|`bN{wtDFiemR~3I~6F;>%{GTbQNVx*|-~9S+2L9F}VkA7Ca#$S_Z^ zc1sYJ;hXOf{NBJz`)I*FXYp?Z;a~@orXpCK`0T|jXms;>mUXnajJMmuYj5Ezjnay4 zhIlD}8)2$_f>cE8>=FF<6%2ax&s-q*b(OH$KnXdXsNp*#6H%$D$* z_aGOb_qPdeTt!PpH%^m_kV8j#Yfbc*?q$hcG1XdSzxoEsqs zRGeXJZkaS!M5hDVf}PrwkqcK&B37q~s;+;>Fjr!CdQm3vlxVQc&it}K6t(rQrJF7G zYHv|#-6cp4vUWrZs}?>u{YXw$-54X|O2rKQ!ZiD}1;*MBC5a*~pQOCqAc{9-D+V7wfU2QR_cA1Df=MLToO}iKO!kqGvMUrUHrpX{Flh$r`BHbHk%KAs?|?)wAS+i zC_9v_ELb?$C6qsu+h?Y=2&$cae>n0lx4yjc4=%DP`g)OP3;+NC07*qoM6N<$f)%NQ AL;wH) literal 0 HcmV?d00001 diff --git a/services/extension/assets/images/icon-32.png b/services/extension/assets/images/icon-32.png new file mode 100644 index 0000000000000000000000000000000000000000..dab6b99e13b5652a8cceb277272c894ffc26c574 GIT binary patch literal 2197 zcmV;G2x|9Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91AfN*P1ONa40RR91AOHXW0IY^$^8f$`|4BqaR9FeMS6OUaR~i1!ojZ$X zyr*_NSsXWZ(m1h{ro=9Gix5j7Zc9r-U#LV1yulMRkt!-tA&`&|ElXe70?GrRq@hxp z)FG`}RSl3-X+zV-B+la4#CGhl$M)E>-h27}|IXOsj{5@T$oKlJ|M>xnoe)x*vYL z4=xVDppVl{e5|tN|E25l@FYB(rNrmXzkKv;Ntj{BboBR!>P_^=#>02Kj}NqjvEn}g zUC-bbNpJC)p;M=FwyX*`**)d)C4bVi?{`0zx1m|35!~YIDPqd-kom;keGeLA-taun zdbj6?unQSk_`kJ+9l^clo&_;oVt(2tn}gKW@?4MLX>^|Z5os39@ZZvq^TyYBg$T}F z?)g3hi*^u89JGyvUYYPbSFP_k4szjI%&+Ovv$fNg5zNjYRksD%sKlk;jOU$b91t%-*#V z)yb=H4a0;>P9O}&R=?>!At0eG&9ST44{)0YtGyk$?p_oYW)b}BpHawGVWEDjuC2NK zmhKlUH4?EU2$^ciKo{jDRH~C#&~)pJ_=@HP>Jpa#J_1q-DBVj*b@^fBcJ#tbk0aC5 zhseP15q|qL&swMwPqfL-vZl(a!j>%pKiq1kKVVb@6SANUA26cDdp~)Q)v!bcRR?O(Y(C0~nGYQpz zFAb=H8l-zI7X{*#xTo_)~-i4kIvn6_H~vAisMLoV9JLL^v}= z|0j7?%(1d*9Vnj?lV@Uacy<`E)CiXh+)xOG#<+q7A~Dz_SKu^nLUz|@VHW2RNQ|n8 z(8V_(kDSRqGz+(}8D?q(>AFp@3VB2p$IGF#ziO#nsNalK!`9+v`aGL~J^C-CzVIaG zp7d+dl-nDE5K!9RRZNZ#oLk@(UZRe zsD($6?4~Z{I(98>3*7v`2Mz>a@$u^KVk`s^n4ekN^HH4&0)9~XL8fm_8K&2rVN?zL zfz~xhKk+p3G`}=MkRSulP-}~iu>x@0cfy)Wz?zxxaa9XZ#Fy(&`tJ$?8sOZgn~MkB znl;GX(~ZDCPQscPEHdCpe`-HO8;R0N@Hh%Ai34McRW z5J~ra2~OiW1SdxsoFSc;mtj_f-aPJ`pO%pT&UQV>0NsGdsAporR; zK|RBpu%cDCK&qS*jOruxQY;X>{?Ceo_UR+AuU{evMbP}|zDG4fB5w~Ma^Y14SC??z z3B^=ic?B&Ath4}El1;ndvc5Nsobhk2*{RZgAfJPM;@1eCI|YOyD0JSB+yjroX=!8S zJB;wf*M0D!OXT2mybo4>5v99eS=dL!0P7}Pi}#dTz-66hiDfDV7+BWX6ZBCn@{fHB zPFF7wjw1N}S;Ss^0fAI<8Jq#hfq0Pd6l5VPhh6ShSU}P+HggTbJHCzrZ@)%o2zdsf zXzpz5hC_|P0wd;+oH~rqwRd1Sg(9#NB0n1h+@lgB?fZF^ccAUPUr97ABNuzN_@y-M zS|Q9gbfSir(U$W+p)2l%l^KV1`WWn)5tz9Nn3+2)Fw@L=79HF6b6+bUC5gt=+_l13 ztl5AlOL;7L4eJO0L^Bk4&2Q&gSr!pHu&$%?2eh=Zt4<7J;_iOTwDh2gsS}$XgyFqD zXRpUZd=G5a{AglGtDVJL&k9K4S$;Ty>6TAoc-sNk`FT{&@M22+8<}Vvx3+%`i*=jP zdie-~Tq^~r*}Zo^UG2i~>H<|}`JolYPZX~5*xeR@r|zV{my z;8#B1*OFm3JD+6u6cE4hGJ=^Is3u9dKrtnEpNAfSudyf&6B82ys!N!rrhL)&!A!a?`Rr+jRbS_^m2R%3ALQm4}KY& z?*9Gb0QPE{d7)U9MSLP{ax00000NkvXXu0mjfz0nqT literal 0 HcmV?d00001 diff --git a/services/extension/assets/images/toolbar-icon-16.png b/services/extension/assets/images/toolbar-icon-16.png new file mode 100644 index 0000000000000000000000000000000000000000..830ab9d58b5ce53756b2fb82c019435de0b53f82 GIT binary patch literal 594 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uuz(rC1}QWNE&K#jw#U=OF~mY} z?j=W!=0b_%AHTc$aHq)#ue|@<-Z*&Wm34^{>_7ZXo?KY^Oma&CZ=Q+c z#<_lOo9D)#Xe79Q&$|6C<(Kj$^PPO;bjy1ok6x=dB4h* zYL;?jNc4zR*#l=u zu9?py#jnhIA!fCD-Wlc)>y17e8GBc@uQ||Z)s_6bdDA|HqozVVxs#qR(A=^-+vjk? z`lcsGIfVlHw+e3UDtY@TLCDYLjqy>PHwT~3*?N8PHI|%~2M_1H&v`LB?A<}F*Ue8{ zc{bF$UNe#Vx}t2|2gkcdlh|)|`<5hg?sA*(OnK9+tCzX|9$(Wc(H*+NjUl=0?IylI zKMq$(-&xUGl~ffa@hkLYuT-JD;BTubFCTmmnW}a*f4T50&e?x9HZi-3D0?Icu}T~j zKlZUj@kwHnxXq;hZ2X^+rT(7SzVh0v|K;v!P4}f`@>15GkDLFzch{}?KiK9pKbo%j Sf0imJK0RIiT-G@yGywos=JX-} literal 0 HcmV?d00001 diff --git a/services/extension/assets/images/toolbar-icon-32.png b/services/extension/assets/images/toolbar-icon-32.png new file mode 100644 index 0000000000000000000000000000000000000000..195627d1dccd9e3160bdec98ec02441a51ab72fe GIT binary patch literal 1269 zcmVPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91AfN*P1ONa40RR91AOHXW0IY^$^8f$@Ur9tkR9FecmRD#MK@^7X)mTU@ z2`a>f#!j$ddkGdWC`c5Qpbv^Z7z-+b>cV;u$yElmiJ@D;0bLN~gvpYMp8;!@~FC&pi8~7jcGRg$vjGP0N|9A$O zjQ1eT3|bkDM&JC)2_)`260sBT#>QEKG2BdKH0~uG3$n1~J=F18s-V zs(l-J!2y^Gl5^xOPMCoS;MCv>z6$psLU1($6k`S_P@#S!ns|HLKu?Dm&e_2a+<>;*bEEd2ke4oZUx%;a9KWd7Q|&p>;x8LcXE?Va3}l| zLi|zFe?~=+%7pT+kGSrKwl)vDGkA|5ojd^>=xPGfp$xQ^zR=Et$gRy5qGuQ~9oN;{V8?75oylG3?e6FJE;_brcA@dybp-8t zCtwoRqR$w68~OzdGi|9Ij_sLDQU{Ti>wK`!ZPz;ivwm7%9W0fg^=t`N9%G!g4M&g0 zR^BPcmt@vE0W$VMHa4LlfR-)Q@}ZNrp&M+1a!jExRfTgvNRAGuFK`yT?H7zOwdRQCu41)n8faa1=w$417tuoNUTn@;_-JFM_~@=->yy}rZs3O z^^}$=8120S_6c;ucZEC955;k8t+778d*K_rfoGsQyanaN{y^6$)Y2|coc=L%=JY48 z3@z#IT?twV`fhK9S}zanN$?c(Np8rYWotGvIm(^nIn9N3HfSu38M6spXzT_(+8AWm zBrbVL8EaydGttIjYe{3EG4$f<{i!WDhd?P*z-;gi614tw^nP52;LoXF zM13YkvEK5c8-r@8W+@Eu6H*xjjfXpM8w!GP_=@2WbcK$v9aez_ zi6};~%4uQy{#a8?Uot)FkFXB>vtG9GsZTK-{&>Jt1t{JZPQq(gVvsFg-&A>j-Ope; f*RLA#e|LbNIGo!f0FWLz00000NkvXXu0mjf&jTrr literal 0 HcmV?d00001 diff --git a/services/extension/eslint.config.js b/services/extension/eslint.config.js index 4edc473..30f4666 100644 --- a/services/extension/eslint.config.js +++ b/services/extension/eslint.config.js @@ -5,9 +5,9 @@ import globals from 'globals'; export default defineConfig([ { - files: [ - 'eslint.config.js', - 'src/**/*.js', + ignores: [ + 'dist/**', + 'rollup.config.js', ], plugins: { js, diff --git a/services/extension/package.json b/services/extension/package.json index fdfd76e..04f0aa5 100644 --- a/services/extension/package.json +++ b/services/extension/package.json @@ -12,7 +12,10 @@ "eslint": "npx eslint .", "build:safari": "NODE_ENV=production HOTPOCKET_EXTENSION_TARGET=safari npx rollup -c rollup.config.js", "dev:safari": "HOTPOCKET_EXTENSION_TARGET=safari npx rollup -c rollup.config.js", - "watch:safari": "HOTPOCKET_EXTENSION_TARGET=safari npx rollup -c rollup.config.js -w" + "watch:safari": "HOTPOCKET_EXTENSION_TARGET=safari npx rollup -c rollup.config.js -w", + "build:chrome": "NODE_ENV=production HOTPOCKET_EXTENSION_TARGET=chrome npx rollup -c rollup.config.js", + "dev:chrome": "HOTPOCKET_EXTENSION_TARGET=chrome npx rollup -c rollup.config.js", + "watch:chrome": "HOTPOCKET_EXTENSION_TARGET=chrome npx rollup -c rollup.config.js -w" }, "devDependencies": { "@eslint/js": "9.33.0", diff --git a/services/extension/rollup.config.js b/services/extension/rollup.config.js index 9e6c303..cc2377b 100644 --- a/services/extension/rollup.config.js +++ b/services/extension/rollup.config.js @@ -3,6 +3,7 @@ import copy from 'rollup-plugin-copy'; import {string} from 'rollup-plugin-string'; import packageJSON from './package.json' with {type: 'json'}; +import manifestChrome from './src/manifest/chrome.json' with {type: 'json'}; import manifestCommon from './src/manifest/common.json' with {type: 'json'}; import manifestSafari from './src/manifest/safari.json' with {type: 'json'}; @@ -62,12 +63,17 @@ const manifestJsonOutputPlugin = () => { ...result, ...manifestSafari, }; + } else if (TARGET == 'chrome') { + result = { + ...result, + ...manifestChrome, + }; } result.version = packageJSON.version; if (IS_PRODUCTION === false) { - result.name = 'HotPocket Development'; + result.name = '__MSG_extension_name_development__'; } return JSON.stringify(result, null, 2); @@ -78,6 +84,8 @@ const manifestJsonOutputPlugin = () => { let OUTPUT_PATH = `dist/${TARGET}`; if (TARGET === 'safari') { OUTPUT_PATH = '../apple/Shared (Extension)/Resources'; +} else if (IS_PRODUCTION === true) { + OUTPUT_PATH = `dist/${TARGET}-production`; } export default [ diff --git a/services/extension/secrets/.placeholder b/services/extension/secrets/.placeholder new file mode 100644 index 0000000..e69de29 diff --git a/services/extension/src/background/chrome.js b/services/extension/src/background/chrome.js new file mode 100644 index 0000000..0daf884 --- /dev/null +++ b/services/extension/src/background/chrome.js @@ -0,0 +1,6 @@ +import main from './main'; + +main({ + platform: 'Chrome', + api: chrome, +}); diff --git a/services/extension/src/content/chrome.js b/services/extension/src/content/chrome.js new file mode 100644 index 0000000..666fcc0 --- /dev/null +++ b/services/extension/src/content/chrome.js @@ -0,0 +1,6 @@ +import main from './main'; + +main({ + platform: 'Chrome', + api: window.chrome, +}); diff --git a/services/extension/src/manifest/chrome.json b/services/extension/src/manifest/chrome.json new file mode 100644 index 0000000..0fb2e42 --- /dev/null +++ b/services/extension/src/manifest/chrome.json @@ -0,0 +1,13 @@ +{ + "action": { + "default_title": "__MSG_extension_name__", + "default_icon": { + "16": "images/toolbar-icon-16.png", + "32": "images/toolbar-icon-32.png" + } + }, + "background": { + "service_worker": "background-bundle.js", + "type": "module" + } +} diff --git a/services/extension/src/manifest/common.json b/services/extension/src/manifest/common.json index 320c91a..fe256bf 100644 --- a/services/extension/src/manifest/common.json +++ b/services/extension/src/manifest/common.json @@ -5,6 +5,8 @@ "description": "__MSG_extension_description__", "version": "25.9.12", "icons": { + "16": "images/icon-16.png", + "32": "images/icon-32.png", "48": "images/icon-48.png", "64": "images/icon-64.png", "96": "images/icon-96.png", @@ -22,10 +24,6 @@ ] } ], - "action": { - "default_title": "__MSG_extension_name__", - "default_icon": "images/toolbar-icon.svg" - }, "permissions": [ "storage", "activeTab", diff --git a/services/extension/src/manifest/safari.json b/services/extension/src/manifest/safari.json index d83d415..c7baa68 100644 --- a/services/extension/src/manifest/safari.json +++ b/services/extension/src/manifest/safari.json @@ -1,5 +1,9 @@ { "description": "__MSG_extension_description_Safari__", + "action": { + "default_title": "__MSG_extension_name__", + "default_icon": "images/toolbar-icon.svg" + }, "background": { "scripts": [ "background-bundle.js" diff --git a/services/extension/tasks.py b/services/extension/tasks.py index 37c3a6a..e7dbf01 100644 --- a/services/extension/tasks.py +++ b/services/extension/tasks.py @@ -110,3 +110,23 @@ def start_web(ctx): @task def start_safari(ctx): ctx.run('yarn watch:safari') + + +@task(pre=[clean]) +def start_chrome(ctx): + ctx.run('yarn watch:chrome') + + +@task +def build_safari(ctx): + ctx.run('yarn build:safari') + + +@task(pre=[clean]) +def build_chrome(ctx): + ctx.run('yarn build:chrome') + ctx.run(' '.join([ + r'/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome', + '--pack-extension=dist/chrome-production/', + '--pack-extension-key=secrets/chrome.pem', + ])) diff --git a/services/packages/common/hotpocket_common/constants/accounts.py b/services/packages/common/hotpocket_common/constants/accounts.py index 4b6b6ca..cbd7ee5 100644 --- a/services/packages/common/hotpocket_common/constants/accounts.py +++ b/services/packages/common/hotpocket_common/constants/accounts.py @@ -7,3 +7,4 @@ import enum class AccessTokenOriginApp(enum.Enum): UNKNOWN = 'UNKNOWN' SAFARI_WEB_EXTENSION = 'SAFARI_WEB_EXTENSION' + CHROME_EXTENSION = 'CHROME_EXTENSION' diff --git a/services/packages/soa/hotpocket_soa/dto/accounts.py b/services/packages/soa/hotpocket_soa/dto/accounts.py index a85aaae..3aef33a 100644 --- a/services/packages/soa/hotpocket_soa/dto/accounts.py +++ b/services/packages/soa/hotpocket_soa/dto/accounts.py @@ -26,6 +26,9 @@ class AccessTokenOut(ModelOut): case 'safari-web-extension': return AccessTokenOriginApp.SAFARI_WEB_EXTENSION + case 'chrome-extension': + return AccessTokenOriginApp.CHROME_EXTENSION + case _: return None