From 779e81346b3cee0ae35e8c70da88cc33841d0f57 Mon Sep 17 00:00:00 2001 From: Kobe Date: Sat, 31 May 2025 23:08:38 +0200 Subject: [PATCH] unread notifs --- routes/__pycache__/admin.cpython-313.pyc | Bin 10730 -> 10730 bytes routes/__pycache__/auth.cpython-313.pyc | Bin 9272 -> 9807 bytes routes/__pycache__/contacts.cpython-313.pyc | Bin 21152 -> 21616 bytes .../__pycache__/conversations.cpython-313.pyc | Bin 28890 -> 29041 bytes routes/__pycache__/main.cpython-313.pyc | Bin 59337 -> 59803 bytes routes/__pycache__/room_files.cpython-313.pyc | Bin 47799 -> 48302 bytes .../__pycache__/room_members.cpython-313.pyc | Bin 10069 -> 10689 bytes routes/__pycache__/rooms.cpython-313.pyc | Bin 20790 -> 21266 bytes routes/__pycache__/trash.cpython-313.pyc | Bin 8901 -> 9547 bytes routes/auth.py | 15 +++- routes/contacts.py | 13 +++- routes/conversations.py | 5 +- routes/main.py | 9 ++- routes/room_files.py | 11 ++- routes/room_members.py | 16 +++- routes/rooms.py | 13 +++- routes/trash.py | 16 +++- static/js/notifications.js | 72 +++++++++++------- templates/common/base.html | 7 +- templates/notifications/notifications.html | 43 ++++++----- 20 files changed, 153 insertions(+), 67 deletions(-) diff --git a/routes/__pycache__/admin.cpython-313.pyc b/routes/__pycache__/admin.cpython-313.pyc index c055c5f74b648b2777fa957f16d6f58a8a661b2e..b5da3be96417d4a734640a8a0efcd3b14202482a 100644 GIT binary patch delta 23 dcmaDA{3@95GcPX}0}yoQTW7dyZ{)k92>@TE2Xg=b delta 23 dcmaDA{3@95GcPX}0}$}{n`VS)ZRES72>@IM2OR(a diff --git a/routes/__pycache__/auth.cpython-313.pyc b/routes/__pycache__/auth.cpython-313.pyc index 608081d7c9e274d0ec635203b80e99af38eb7646..a08b475dc5f4774fe20929bafc05f9d43c42df24 100644 GIT binary patch delta 2060 zcmZ8h-ES0C6u)<8zdzV^cj>lUx4T`It%C#%P$LDQ6ooQd8p492tPI=TX*#Tu1SPUkN}UPb#G1DF>Ahgp@FZ zl?X(XC`5%`D#w&9utkYOT{V&>?C4TcBd~|3}(pi`WpHxp$vf+Dq)VWxPuvgCLgQ;zqnrGev{G;Mu)kvhJ4sI@GaI>AQG)@;40$r3wE z^BJdWMzhtn0a~r0*3CA*%v)>QvvtC1H03b}`zH2MUzU*bzV)EU`6mK7?Pr65WQV#6 zgH-5Q@-i**Z^eUOS(vtO%iC(YSx@lN|*~?yt@sHD`TPnKL3>){g2K1oXqc?cG z(Pu-m&{N*ob#$bP=~>mXIi*$EerfPRziwR8>KmCn?`?paVGCHhIb0*Z29oyzMgIRe zJ@i%dc6>2@H~s1d(feJcFQUuw;pO1){b=E{Bg>J}vbVIxO~{f;7ib8hg$xJwyJy4) zoD{;|mQK(FyCt0}LJqgPImjN6QQpD9!b~sb#bGDSOeyV$om|z;_IY>E+>+|O;-kCS z{m?eLm;DzS;#dp=9cDMf*|EKN%YFpx2q++ILntC_=iqn^?TWn+NB(xtnKQbvYu0Yg z$$f%a$gsb{B^qbhNcLh0ZBhtZ&@f&qk=7i>I zmaU=)BA_8WBXN*@t^(T^hS{f)9L=)tA|=|t^f0nWY3|mCF<)j6?jrH-;R%$4#VhRd zL?L~MuNPt%|1QvGbj#L2rZB-CByxV?Zi0D|b-ys%%PuFgog;X|YaE;i#;DeL?{ceA z)CA|XDRtU}CT}irZVV5LdtOKB0EdNyVC#uDm$2+QVR*=o5J14`0K)JMl#U^E;)ur) z#t}{+AbC#I;!mknv#fa&8iHj|P%qUQ4JhNWtq4djxcQ)zPTiowIy%hWV-|SI3 z$_@?eDQr}QSFeS`NVTuFG`Gt~@WKP^|C zU~^4|-7b6?zrPyM(`;ySmfNDDox9L3 zG~G7pwrTR61fCV0>1*3MSENuOco4%msEUE0s$ijIcfDg8HlIXl3(PtnEG7UBT`}rJ zz);5}2JDW);6WZkdrmt9?{OW@bqh=USfZ4!l5_XSxfOD5m7KgwPOg%|3Mo7w{SQcX zm5i;Bv0uW;kB)sZemA`RTHqlK-lLh7L7Mr)@-cL?a(BX zJu{(h5rZ#+Ad1|7Kv0mbPZAIxeH45U6a@#Qpie#sVjq0<{_fbVb_hStIrp4<&iS2l z^WB}V)~AjQV?Yt#^7R|L=L?GR8VcVgmRGc z0z5FVS)eigwVG2)LecrJ>Sb2!8`_&Ho8a#zpJXO~l$;XyeUg^02$z5rL=W+Tav$_@v@U$5BLN(~%9yf<&|C`POOPOYn;dFx9Y-%P?Hw>qcG>yK79cO#egUkg?(? z&kw5EX`D^~WYsH>WW-fo9WGsXM(76ySiMH}j^p{FJ4P$~{o%Y82JkP2>zWKRd3Ge% zyN(;477!#QBXzRHENC7Uec#@)wq0tOF_M1I!CBt30BKIZK?d1oPO@Fw^;=CdMIA?OUCi=k2@HkR;PG?9p1ILAT(R0lWwZ`+<}e z5NKV$-gftcgyXp=zvhRNT7?H@a!UBrNwg&KHlp>my>EdF4|NpqiA{TB^eS8Hzc>1m zm_&c`;u_lqMP~FYG2n!r#Zf9uDw#N~Qq~|T#NNo3!fp~!ACq;cP!WbPZe*v~+k8IT z5qUq$GOYJueud5RKk`qN9#2I~uY;Fn`42mGxXiPNpX0X*;~LgNEb?CAVW|S~4q!vT zz5h+N0rL#kisPDm4%2+LIN3WhG1<@Q_MhqOOjBU+v?;m^RgMjFqAK7XKt6yd-G^)e zHU)UF7GjeApNk9ZgZ^;oZ&n2_W+YI_?TSUk-z45Ow$R$@(3;TcRlw_j9RdHHqfmpS z({_AIhI_OpEJOVM_~&ul`;s}z(-VbWklYsUbkAzKJ5D=X895(hle-;gEw^Dey>KzH z#6e=a+phbrD~yA1ldGt9eMcCEV33XD-%2K>*C2&M7E+)uiW|F4`zAe5#5-fc=Ha#f o8D)$eDXTv!t4GSl56Z@oa_JZRa!1PSU$HnFJI*Xe*`mDaAJu?W^Z)<= diff --git a/routes/__pycache__/contacts.cpython-313.pyc b/routes/__pycache__/contacts.cpython-313.pyc index e9f119de968bba3be0cb8440d4c06b642c627448..5033b2b9b0507a66d818ea33c909cc0796e24fe1 100644 GIT binary patch delta 3515 zcmb7GS!^5U5#Hr}EH6rwr@_<`=RK}zY=K)0g4jR z$N6XGzvi2npYQ!xe0)a?ZUzEA0Y9QPKKH_z+rfl*^7c@FJ?#c}O2`UXTgH?2f;a60 zA0OK@{9q}vcp2wm+4G*K^HgrGu`QK=uQbau}{cKxt?qw8+^tNsa(gU z3AUJuvVTXT4ji0ipB(9Aqhi3hXWO(dL)Y8+$?X{vEzRC7L$O5=aK;yKervdC#vN0a zrr1ZJXgrb>#i;dNLxOIQSn%ZIQC98CxlnONfwQA&Rx(c%0v=wJe$7PgF=agoYXW7r} zM?^dOlRed9hD$2?RY6sX`GQuf8qDn|`Ruq6{cl#;vST=`bP2*WC+$w3YXX}NbspP} zU{%P9mxTl39{kSQ=v%f=nXOxER%yd>K{3i&RR6){6i7euP!wkM8I__a6NFeSGKx>BG=k=y&lWuS@sC zgLkBj(Ab7&?7r0Z-l+}$;D%#x6Q>C4ieprq^rHmb1aw1iA()=Bo>z*MauwV-Toa(5 zOub|Za(yG%K>66gLar&XfBm}as8bwf-}O&CMXSmQZUqm`b0|>KO*@9$w3n*}1W4&z ztf{bKx@ODsh6)g25$TLL$X=A5dY&)fGhLwSwRr=E(H)Ku&~4!$!6AYZ2&P|G6)4Q* zaghp)llmxvPJJTP_&_iFgA{dMKxNk99xUuaPeuAESXTn0$WYA#Y*Jt zrM%2nm5Rz&gF*JwL{coX-y~iMQ#c_+KmmdiY`VQ8QYDd=H62C8P~a-N)t=aAon*EQ zy1{7z2YEO#mUxn9N%oM;_ldDzwU6!P7x)q_);Kv_Cr3%gDp*i-eMy5NNUSHBY@^hI z9s-^r{3>yFJ{hk^N!dsi*$d}Mah2d2f*DYk%eqmnmUsa|j&u$>3apUmB@uTs=HO`@ z>t2K>NFo=1Rb9eztQtzefN!Ga3OW83%V$;iD{}zm1++mOGHx>T?0c{IWZ>l`{- zA0Y8XgqzZtV%bPt1dVS@^Fcvab3uh<3LfkV1x5I*>F4a!7BJGP?1r7pf7TSrzWLca z&F~xQng@tvsIW&v85d_xV+Z&4*B40(1e9uW_meD>RwpnBUMApKR3nLppX!hk{67v< zCqIi*ieZD()j2Xq1YaR|jerx$DQ>XIixueD*rytl!aL;tZ3LZ?jM%cefuv%T^9x&1 zeiv;&Ss#}N?MThc$o-&2%|&){d|X^)ua0+J;JZ;qzzD$`1WnSsoHT0H+q-{}94M=J zuE1rIULYVc;Z64E@k`U6#U-5;*O(!%w-HQtVNR)*uri9IQVHH7y=3(>i*NWA={5Go zu~eO?k`GxO%&JAQHCQrz^m1sBKYZY@MJ2U49yyC5&IBj0(KmKy5AkFX%_-;M=CQdc z2z3EoA@i#Qw-8JhFDso|Vnusd-AyLXkS32j(91NLS^=*-h>`SV0%8%C2);wGO3+Hc zlP!+Y5PX%!Un8JAktM4c@LF#Ya+Pg78%utJyr{V0b%L)GSSj<)@wP-S4oxdlwrl!* zRQ+Rp-|=B5X3EvmpW0nK34l$XdoD-R%6873=_{)9s-YJ1Gb>HzwrbQe$*!$}qfLKA zTZNi)p6s$@*Vy0BrFbdIvE9^uj~pfmnibcKCyx#f>M3&JoKl>2g~_6GXPud@t&`fV z5RUK|K8=%gJ3^zFf|gsO@!dh=^7=17uh2`?pB}J}SwW&*DR+4dm;v2TjGCUe@>||+ zKV+xGhuiGddAZK-rQ!Hyh{`KfQQ(GJOeuN_4wKgyf+_X_btyUW|ApopEzs;G&GyZbraeepzwYxe>z|cx1d>^p$modpdM2{bc=+(mhBgKArYf5zW(^3c7YZnIU zKyQcXpPg6q8+raK5a`Y41e&gjh5^;BIth?ENZ!s_4Jx`-vSCvA^u>8)zA&d&RvJT`AC%RiQRX_?oVtEacI*n T*m@q5^r^IdaylnEXzTw1pVcKm delta 3258 zcmb7G-ESOM6`$FU+3yc$*Pm<0>n0`HkR~RL3rU=&pd?LpX|L_J11tIG zbI&>V%)RIQ&Y5}mj{4)b)%2rODz5PF*9&LXj-9`oo=~%Q4;|&tw ztTImH{G3>2f+qNXyfRsyqA9k`D%C`#P%coRsMORalxngzT}@Tfr-C$7n^;*C%kkX! zIh_0PAKRW2A3U8GKOc*V(`qU_x92S)YJPHVLG}F)?`~`CQxx{Nh&^MqzL`%jGNsVL z+RREt{3DUe?AzN|i`H`0oOmu!2!CwPI+CxBR>zJ9s^g~u(mo;Whx+!(A^ViHAMV== zL-uKDFYVbgGaOy*N8c~4NBY*;$E-zIlOJ!!N^^h3B}v*TKI!!;Y?{XlEhD#&x2m>^RQj9KK#{%c3Dub6YZ*du|PE$wP z=q2bvI5BNPL>^2&l)Fa9dF{j)h6s`9m|OZTDd;t}FYkP5`rQE2;nb`d(>}`@Iw*c} z_;hay;u6E{py_9HmW1Qq3u^3SM}%)fs6&iOy(#^2iUj%0#du@9emH|m--x1xQx_s6BmMY@A)`EkYcsBd*3V zz5vs=7`#Y*&FD0FLe!x8BH$a)C;i##(}TVVeUst#6i!j})s3Rlt=GA&R=cg%_M9ww z>4ZOMlL{9?4uCH_`JDx05h(q zl#+g3FF-RW7S8Nt?N&qIuEN46+HR}! zF8U#Zml^CCdOc86yn4APhd^m;j|#jE??!qPAZa=1^+Eb&GGz@U86*Y%Gp)PTf4svz zZTF->KjS%>aye$XLlsUM&5HSQK7Nl^oBZA7d|v#0>C-!JlnZ#-{K(+n{x`Q8b()9fj9Z44yhiX!+8}|CfYw2*B)7 z{EG}SF}Uzq%rtIdnX3Lp>21~yO|6t`H=F#@;cpC!+5ZlE{9xz9rGl#MM~I^$vHWDu zORTmGXI*dEO{+sOHb_SEd*0@xI)2Z9eqI=FLdWmiG!$`Xvt_jEYi4_!GEgMQ?(*lx zF?_XAR_^J#76*|xLV>-|4ae>fy46c>nY2D~+w3-v`ChWw^5VD4g`UF>vXecHhemy? z!5z(-x3DkTHaOoFIWo0gSUyyKKFOCRVLTI>flzLFZJL3ETl{;P)6+JTGQ#Y diff --git a/routes/__pycache__/conversations.cpython-313.pyc b/routes/__pycache__/conversations.cpython-313.pyc index 732b9f599dcd85736ee1261c32dbaa2588ac48c4..719e5438d9fa81d175a73848027050a26990b865 100644 GIT binary patch delta 3926 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 diff --git a/routes/__pycache__/main.cpython-313.pyc b/routes/__pycache__/main.cpython-313.pyc index 45114d41fd2b6033bb9a1da738a2577255e4f800..eeb0d924a9fa403160e0ddccabc5550d942c2e2d 100644 GIT binary patch delta 5566 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 bYc7wz>I1jt>U$rfU)2@vxj5+dU<>~TNumWh delta 7463 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 aoL3spEA?{mGh=N3oSL}pnW=epyyE|!M|Qmc diff --git a/routes/__pycache__/room_members.cpython-313.pyc b/routes/__pycache__/room_members.cpython-313.pyc index 1a7cb40691f23bfea608f40d5c915599485f71e3..1f599c7c93b45a7799f24876a820175455cd9010 100644 GIT binary patch delta 2470 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 diff --git a/routes/__pycache__/rooms.cpython-313.pyc b/routes/__pycache__/rooms.cpython-313.pyc index c2ec82d4c78171177488d442f9863484cd829e3d..4dc761866eb3f8eea331ce335ace8814cf7a11ae 100644 GIT binary patch delta 4081 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 diff --git a/routes/__pycache__/trash.cpython-313.pyc b/routes/__pycache__/trash.cpython-313.pyc index 56e026cb033aff4d4f450af79046952025ce1247..dc05ee3b81e16b9f5d9e1aee5a7a170f720a5400 100644 GIT binary patch delta 2264 zcmZuxOKcle6n!)H*yFz)CvpBC=Tn@fP5Mb`)2OH_uozc`)-6S<+o@wu;wi~YxHG07 z7O7ZugM{>`#17a|Q~{D#th!_wGIK zzW38xzi7+ru~?YlGh`pHE_N~Y7kz9zfu^y3Ut;VoE3kr;XPN|(CWEZ`z^D1auPLBt z0SIV82x=;*1H_Z_AuSAHEdmko?aN2C7{s(V#I*z@v?L_86r{8ZEzlwe{(M?%g;rrJ z`8KT`+O-U1v<~RdI-yhRf-VsYxagU_~j*3Tm;p5GsU^`Jk`ZckTo~ ztE5wrE;iN79|q8Sio7GyLTo}R#E;3va3Qh9*koomnaM(Gli9Khb7+&9F0^hk+je0N zZ!pK(x6PT^g*h@G$aOrUtaB>AD-G}U5@1*r19aC|Tv{l(hNps2F~KOiUa$rW`kW1( zGPh82s{BW3PzvxT(!pU*Ib*x#Tu$;bV0>6Zy1rC$oa;7J^m4Ui%^Nv|C**O{OP9gG zS#^u}X1U~=w&f+~4Og#O7(u#Sv)nT{lUhNZYc3j|;sP#rD=;D67z$G&OJK~IH_A=L z#mk3C|8v!o`wO zwW3Zvj%L3)xe^&&@sF8ZG9!h&ObpE@4cOQd~VSGx{Ol6ipOAi*JmBM4r|bZqR4#gYrJlhcooQ(=sMt4>N& z{E0fGz##hgM5rx%8jTkGI}iBnA~j{Cr-y{f@y-|1h2~0$SOerA&sbghd!H`L5#mhWUKAOrrv>*1VUZfn}%je zBR5qvx^7nBZ47x)@o7|as=$+B&Y^}Lphw8+MVq+`0#q$Q6A(FNJGWDx0 zB!zcr&NiV_#aJ-#FfJLmcNRABmSE>m^a2taHJ&F+ly{V@_XsW`c+u-6M>j29WC-t* zzle})gWZ%{-t4%>v@zmeCeR745(t5|ic|P7d5@;01FZT8y zp%OjC0IR3#%tGaPfw>%ZB6F0CWp_idYCv(_rdx zpq&J&@E#KvNaHZ5GnjU5!8ie*VhDGKIX=YaK1q_+@=RZ4`Xi>VvBPUjTV>i)HnGOa zzp?TY7JABh|BSSLcJYhq4@VzHW^Sv0NTF4!`=QkRLiS60)_qLwc}~{zD3ilHo~`@o Gg#8cI+X}(} delta 1629 zcma)5&u<$=6rNe{u6Mn568{K}9a3-eqrq(;p}3KX+(;0$f;B3njz9vVts`%e6}H#B zU4v2y(mw$@MdB|&;=;vJZ=4Y7p(j*9t3@bOT$%$CQhGs^5c9^4Q=%ML$)Df6`R2{{ zzL~ja{djrzAd!d>{yltpar@o`A%EcD@KXlC!3HJdD^ev@S|Jt%YDpkj5r|ka$R_5c ziejmtT2Y9KaipSII_OpmVwM4h6^FQ$fP|QnDREz(eH z)B`7QTTZA1Q1`ccU&`}}97=A3eXoCV3C|={KJGfO6UMfj!1ll{EtOz|=Rrk?BSYVG zn}GxN#!jet&=3(6StquzmPgX_NQyR`ro&mGTJLn6b^{7HWg?tLm`0dEIKv^-UEkaE zpj8iG9+NVMq6RtkXKabiu~Wtjqw@J&s_FWHEwnogo9l3VxWuWk-gV7fx^5On%evPmckH!9>+^GlK$5qVYw2%_VvNkw=;DG~^n4jWMjD{M{wCgQL z6bp6myp}D-s5vwSYZ~Ybn#4X&uDpe&!dZlK2>D&C&$rn>*$;b9QYxh@y{GBN zl8U~;>umqI$=zZ9>)gZ3m={J~!APim0i!n%-sBL*Kdt+=+wPwcSeU=cp*Rlz!w4Pe zZzHTBTq9(ifaur6vDoie=SOB8c_8;SR&mWT!a4^Z?J@ZWW%eLDrPTQFdhhS7LDdx= z-pJp|jQ0w;I|`j(znprN=2)^Ym%#RmgL+rkYGF?8x5V0o!ZJ1%Dh_}L?i0Lah2u@) zdw~UnMdV7J53B5l!Ynn}h!tot9?O>fI>LlkRc*-&kH{HgjN_7!I& zR0-uMEmXRJ%jd<#9mQqhKpc#cIIr2_4cK-#xO5wxPyw&YA3r`#anGRk0fs2*TLS1e z2+3NR@7`@XEASc5aSMt6h{InIO6eXc-6tiEdt~)zvbsmgKaujUME{LUJu)(1y!Z9? SePjNn_B++t?b)7;MgIlmlvDBm diff --git a/routes/auth.py b/routes/auth.py index 940b815..44b6db4 100644 --- a/routes/auth.py +++ b/routes/auth.py @@ -1,9 +1,18 @@ -from flask import render_template, request, flash, redirect, url_for +from flask import render_template, request, flash, redirect, url_for, Blueprint, jsonify from flask_login import login_user, logout_user, login_required, current_user -from models import db, User +from models import db, User, Notif from functools import wraps from datetime import datetime -from utils import log_event, create_notification +from utils import log_event, create_notification, get_unread_count + +auth_bp = Blueprint('auth', __name__) + +@auth_bp.context_processor +def inject_unread_notifications(): + if current_user.is_authenticated: + unread_count = get_unread_count(current_user.id) + return {'unread_notifications': unread_count} + return {'unread_notifications': 0} def require_password_change(f): @wraps(f) diff --git a/routes/contacts.py b/routes/contacts.py index 1c8c7c1..bf2a3a5 100644 --- a/routes/contacts.py +++ b/routes/contacts.py @@ -1,11 +1,11 @@ -from flask import Blueprint, render_template, redirect, url_for, flash, request +from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify from flask_login import login_required, current_user -from models import db, User +from models import db, User, Notif from forms import UserForm from flask import abort from sqlalchemy import or_ from routes.auth import require_password_change -from utils import log_event, create_notification +from utils import log_event, create_notification, get_unread_count import json import os from werkzeug.utils import secure_filename @@ -17,6 +17,13 @@ UPLOAD_FOLDER = os.path.join(os.getcwd(), 'uploads', 'profile_pics') if not os.path.exists(UPLOAD_FOLDER): os.makedirs(UPLOAD_FOLDER) +@contacts_bp.context_processor +def inject_unread_notifications(): + if current_user.is_authenticated: + unread_count = get_unread_count(current_user.id) + return {'unread_notifications': unread_count} + return {'unread_notifications': 0} + def admin_required(): if not current_user.is_authenticated: return redirect(url_for('auth.login')) diff --git a/routes/conversations.py b/routes/conversations.py index e411d6c..12031f3 100644 --- a/routes/conversations.py +++ b/routes/conversations.py @@ -3,7 +3,7 @@ from flask_login import login_required, current_user from models import db, Conversation, User, Message, MessageAttachment from forms import ConversationForm from routes.auth import require_password_change -from utils import log_event, create_notification +from utils import log_event, create_notification, get_unread_count import os from werkzeug.utils import secure_filename from datetime import datetime @@ -54,7 +54,8 @@ def conversations(): if search: query = query.filter(Conversation.name.ilike(f'%{search}%')) conversations = query.order_by(Conversation.created_at.desc()).all() - return render_template('conversations/conversations.html', conversations=conversations, search=search) + unread_count = get_unread_count(current_user.id) + return render_template('conversations/conversations.html', conversations=conversations, search=search, unread_notifications=unread_count) @conversations_bp.route('/create', methods=['GET', 'POST']) @login_required diff --git a/routes/main.py b/routes/main.py index c945934..fbcc644 100644 --- a/routes/main.py +++ b/routes/main.py @@ -10,7 +10,7 @@ import logging import sys import time from forms import CompanySettingsForm -from utils import log_event, create_notification +from utils import log_event, create_notification, get_unread_count # Set up logging to show in console logging.basicConfig( @@ -28,6 +28,13 @@ def init_routes(main_bp): site_settings = SiteSettings.query.first() return dict(site_settings=site_settings) + @main_bp.context_processor + def inject_unread_notifications(): + if current_user.is_authenticated: + unread_count = get_unread_count(current_user.id) + return {'unread_notifications': unread_count} + return {'unread_notifications': 0} + @main_bp.route('/') @login_required @require_password_change diff --git a/routes/room_files.py b/routes/room_files.py index 72849b0..1a5b194 100644 --- a/routes/room_files.py +++ b/routes/room_files.py @@ -22,14 +22,14 @@ to maintain file metadata and content. from flask import Blueprint, jsonify, request, abort, send_from_directory, send_file from flask_login import login_required, current_user import os -from models import Room, RoomMemberPermission, RoomFile, TrashedFile, db +from models import Room, RoomMemberPermission, RoomFile, TrashedFile, db, User, Notif from werkzeug.utils import secure_filename, safe_join import time import shutil import io import zipfile from datetime import datetime -from utils import log_event +from utils import log_event, create_notification, get_unread_count # Blueprint for room file operations room_files_bp = Blueprint('room_files', __name__, url_prefix='/api/rooms') @@ -61,6 +61,13 @@ ALLOWED_EXTENSIONS = { 'eml', 'msg', 'vcf', 'ics' } +@room_files_bp.context_processor +def inject_unread_notifications(): + if current_user.is_authenticated: + unread_count = get_unread_count(current_user.id) + return {'unread_notifications': unread_count} + return {'unread_notifications': 0} + def get_room_dir(room_id): """ Get the absolute path to a room's directory. diff --git a/routes/room_members.py b/routes/room_members.py index bbeec56..5ac1d23 100644 --- a/routes/room_members.py +++ b/routes/room_members.py @@ -1,10 +1,18 @@ -from flask import Blueprint, jsonify, request, abort +from flask import Blueprint, jsonify, request, abort, render_template, redirect, url_for, flash from flask_login import login_required, current_user -from models import db, Room, User, RoomMemberPermission -from utils import user_has_permission, log_event, create_notification +from models import db, Room, User, RoomMemberPermission, Notif +from utils import user_has_permission, log_event, create_notification, get_unread_count +from routes.auth import require_password_change from datetime import datetime -room_members_bp = Blueprint('room_members', __name__) +room_members_bp = Blueprint('room_members', __name__, url_prefix='/api/rooms') + +@room_members_bp.context_processor +def inject_unread_notifications(): + if current_user.is_authenticated: + unread_count = get_unread_count(current_user.id) + return {'unread_notifications': unread_count} + return {'unread_notifications': 0} @room_members_bp.route('//members', methods=['GET']) @login_required diff --git a/routes/rooms.py b/routes/rooms.py index 42c799b..80a4a04 100644 --- a/routes/rooms.py +++ b/routes/rooms.py @@ -1,16 +1,23 @@ -from flask import Blueprint, render_template, redirect, url_for, flash, request +from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify from flask_login import login_required, current_user -from models import db, Room, User, RoomMemberPermission, RoomFile +from models import db, Room, User, RoomMemberPermission, RoomFile, Notif from forms import RoomForm from routes.room_files import user_has_permission from routes.auth import require_password_change -from utils import log_event, create_notification +from utils import log_event, create_notification, get_unread_count import os import shutil from datetime import datetime rooms_bp = Blueprint('rooms', __name__, url_prefix='/rooms') +@rooms_bp.context_processor +def inject_unread_notifications(): + if current_user.is_authenticated: + unread_count = get_unread_count(current_user.id) + return {'unread_notifications': unread_count} + return {'unread_notifications': 0} + @rooms_bp.route('/') @login_required @require_password_change diff --git a/routes/trash.py b/routes/trash.py index 9acaa0b..3b20d0a 100644 --- a/routes/trash.py +++ b/routes/trash.py @@ -1,11 +1,19 @@ -from flask import Blueprint, jsonify, request, abort +from flask import Blueprint, jsonify, request, abort, render_template, redirect, url_for, flash from flask_login import login_required, current_user -from models import db, Room, RoomFile, TrashedFile, UserStarredFile -from utils import user_has_permission, clean_path, log_event +from models import db, Room, RoomFile, TrashedFile, UserStarredFile, Notif +from routes.auth import require_password_change +from utils import user_has_permission, clean_path, log_event, create_notification, get_unread_count import os from datetime import datetime -trash_bp = Blueprint('trash', __name__) +trash_bp = Blueprint('trash', __name__, url_prefix='/trash') + +@trash_bp.context_processor +def inject_unread_notifications(): + if current_user.is_authenticated: + unread_count = get_unread_count(current_user.id) + return {'unread_notifications': unread_count} + return {'unread_notifications': 0} @trash_bp.route('//trash', methods=['GET']) @login_required diff --git a/static/js/notifications.js b/static/js/notifications.js index 933eeee..428f339 100644 --- a/static/js/notifications.js +++ b/static/js/notifications.js @@ -1,4 +1,10 @@ document.addEventListener('DOMContentLoaded', function() { + // Initialize tooltips + const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); + tooltipTriggerList.map(function (tooltipTriggerEl) { + return new bootstrap.Tooltip(tooltipTriggerEl); + }); + // Initialize variables let currentPage = 1; let totalPages = parseInt(document.getElementById('totalPages').textContent) || 1; @@ -62,6 +68,12 @@ document.addEventListener('DOMContentLoaded', function() { const data = await response.json(); updateNotificationsTable(data.notifications); updatePagination(data.total_pages, data.current_page); + + // Reinitialize tooltips after updating the table + const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); + tooltipTriggerList.map(function (tooltipTriggerEl) { + return new bootstrap.Tooltip(tooltipTriggerEl); + }); } catch (error) { console.error('Error fetching notifications:', error); notifsTableBody.innerHTML = 'Error loading notifications'; @@ -104,6 +116,11 @@ document.addEventListener('DOMContentLoaded', function() { }); } + // Function to update notification counter + function updateNotificationCounter() { + counter.textContent = document.querySelector('.nav-link[href*="notifications"] .badge'); + } + // Function to mark notification as read async function markAsRead(notifId) { try { @@ -138,6 +155,8 @@ document.addEventListener('DOMContentLoaded', function() { } } } + // Update the notification counter + updateNotificationCounter(); } } catch (error) { console.error('Error marking notification as read:', error); @@ -165,6 +184,10 @@ document.addEventListener('DOMContentLoaded', function() { // Remove the notification row from the table const notifRow = document.querySelector(`tr[data-notif-id="${notifId}"]`); if (notifRow) { + // Only update counter if the notification was unread + if (notifRow.classList.contains('table-warning')) { + updateNotificationCounter(); + } notifRow.remove(); } } @@ -206,6 +229,11 @@ document.addEventListener('DOMContentLoaded', function() { } } }); + // Remove the notification counter + const counter = document.querySelector('.nav-link[href*="notifications"] .badge'); + if (counter) { + counter.remove(); + } } } catch (error) { console.error('Error marking all notifications as read:', error); @@ -260,28 +288,30 @@ document.addEventListener('DOMContentLoaded', function() { }); } - // Add event listeners for filters with debounce - let filterTimeout; - function debouncedFetch() { - clearTimeout(filterTimeout); - filterTimeout = setTimeout(() => { - currentPage = 1; // Reset to first page when filters change - fetchNotifications(); - }, 300); - } + // Initialize event listeners + attachEventListeners(); - notifTypeFilter.addEventListener('change', debouncedFetch); - dateRangeFilter.addEventListener('change', debouncedFetch); + // Add event listeners for filters + notifTypeFilter.addEventListener('change', () => { + currentPage = 1; + fetchNotifications(); + updateURL(); + }); + + dateRangeFilter.addEventListener('change', () => { + currentPage = 1; + fetchNotifications(); + updateURL(); + }); - // Add event listener for clear filters clearFiltersBtn.addEventListener('click', () => { notifTypeFilter.value = ''; dateRangeFilter.value = '7d'; currentPage = 1; fetchNotifications(); + updateURL(); }); - // Add event listener for mark all as read markAllReadBtn.addEventListener('click', markAllAsRead); // Add event listeners for pagination @@ -289,6 +319,7 @@ document.addEventListener('DOMContentLoaded', function() { if (currentPage > 1) { currentPage--; fetchNotifications(); + updateURL(); } }); @@ -296,20 +327,7 @@ document.addEventListener('DOMContentLoaded', function() { if (currentPage < totalPages) { currentPage++; fetchNotifications(); + updateURL(); } }); - - // Initialize filters from URL parameters - const params = new URLSearchParams(window.location.search); - notifTypeFilter.value = params.get('notif_type') || ''; - dateRangeFilter.value = params.get('date_range') || '7d'; - currentPage = parseInt(params.get('page')) || 1; - - // Initial fetch if filters are set - if (notifTypeFilter.value || dateRangeFilter.value !== '7d') { - fetchNotifications(); - } - - // Attach initial event listeners - attachEventListeners(); }); \ No newline at end of file diff --git a/templates/common/base.html b/templates/common/base.html index 184d44a..49f27b6 100644 --- a/templates/common/base.html +++ b/templates/common/base.html @@ -32,8 +32,13 @@