From a92ff89221dd3c2e5be455f48da1bb00caa7f8e3 Mon Sep 17 00:00:00 2001 From: Kamil Date: Sun, 22 Dec 2024 06:39:03 +1100 Subject: [PATCH] Implement clickable plaintext links and dark mode fix (#488) * Plaintext links are now clickable and fixes dark mode. - Change `AppxPackageDir` path from `C:\Users\bkaan\Desktop\Packages\` to `$(USERPROFILE)\Desktop\Packages\`, fixes error when building release. - Plaintext links are now clickable, and match the same subtle style as Windows Mail. - Remove `!important` from inline styles to allow Dark Reader to properly recolor the element. * Implement setting for clickable plaintext link --- .../Interfaces/IPreferencesService.cs | 5 + .../Models/Reader/MailRenderingOptions.cs | 2 + .../Translations/en_US/resources.json | 1 + Wino.Core.UWP/Services/PreferencesService.cs | 13 +- Wino.Mail/JS/libs/linkify-element.min.js | 1 + Wino.Mail/JS/libs/linkify.min.js | 1 + Wino.Mail/JS/reader.html | 149 ++++++++++++------ Wino.Mail/Views/MailRenderingPage.xaml.cs | 3 +- .../Views/Settings/ReadComposePanePage.xaml | 9 +- Wino.Mail/Wino.Mail.csproj | 2 + Wino.Packaging/Wino.Packaging.wapproj | 2 +- 11 files changed, 138 insertions(+), 50 deletions(-) create mode 100644 Wino.Mail/JS/libs/linkify-element.min.js create mode 100644 Wino.Mail/JS/libs/linkify.min.js diff --git a/Wino.Core.Domain/Interfaces/IPreferencesService.cs b/Wino.Core.Domain/Interfaces/IPreferencesService.cs index 30567428..9ef30635 100644 --- a/Wino.Core.Domain/Interfaces/IPreferencesService.cs +++ b/Wino.Core.Domain/Interfaces/IPreferencesService.cs @@ -93,6 +93,11 @@ namespace Wino.Core.Domain.Interfaces /// bool RenderStyles { get; set; } + /// + /// Setting: Set whether plaintext links should be automatically converted to clickable links. + /// + bool RenderPlaintextLinks { get; set; } + /// /// Gets the preferred rendering options for HTML rendering. /// diff --git a/Wino.Core.Domain/Models/Reader/MailRenderingOptions.cs b/Wino.Core.Domain/Models/Reader/MailRenderingOptions.cs index 739592aa..1c3fb5e5 100644 --- a/Wino.Core.Domain/Models/Reader/MailRenderingOptions.cs +++ b/Wino.Core.Domain/Models/Reader/MailRenderingOptions.cs @@ -7,9 +7,11 @@ { private const bool DefaultLoadImageValue = true; private const bool DefaultLoadStylesValue = true; + private const bool DefaultRenderPlaintextLinksValue = true; public bool LoadImages { get; set; } = DefaultLoadImageValue; public bool LoadStyles { get; set; } = DefaultLoadStylesValue; + public bool RenderPlaintextLinks { get; set; } = DefaultRenderPlaintextLinksValue; // HtmlDocument.Load call is redundant if all the settings are in default values. // Therefore we will purify the HTML only if needed. diff --git a/Wino.Core.Domain/Translations/en_US/resources.json b/Wino.Core.Domain/Translations/en_US/resources.json index db5fe7ce..9892af41 100644 --- a/Wino.Core.Domain/Translations/en_US/resources.json +++ b/Wino.Core.Domain/Translations/en_US/resources.json @@ -484,6 +484,7 @@ "SettingsLinkedAccountsSave_Title": "Save Changes", "SettingsLoadImages_Title": "Load images automatically", "SettingsLoadStyles_Title": "Load styles automatically", + "SettingsLoadPlaintextLinks_Title": "Convert plaintext links to clickable links", "SettingsMailSpacing_Description": "Adjust the spacing for listing mails.", "SettingsMailSpacing_Title": "Mail Spacing", "SettingsFolderMenuStyle_Title": "Create Nested Folders", diff --git a/Wino.Core.UWP/Services/PreferencesService.cs b/Wino.Core.UWP/Services/PreferencesService.cs index 75d4149d..535e2f5e 100644 --- a/Wino.Core.UWP/Services/PreferencesService.cs +++ b/Wino.Core.UWP/Services/PreferencesService.cs @@ -42,7 +42,12 @@ namespace Wino.Core.UWP.Services } public MailRenderingOptions GetRenderingOptions() - => new MailRenderingOptions() { LoadImages = RenderImages, LoadStyles = RenderStyles }; + => new MailRenderingOptions() + { + LoadImages = RenderImages, + LoadStyles = RenderStyles, + RenderPlaintextLinks = RenderPlaintextLinks + }; public MailListDisplayMode MailItemDisplayMode { @@ -92,6 +97,12 @@ namespace Wino.Core.UWP.Services set => SetPropertyAndSave(nameof(RenderStyles), value); } + public bool RenderPlaintextLinks + { + get => _configurationService.Get(nameof(RenderPlaintextLinks), true); + set => SetPropertyAndSave(nameof(RenderPlaintextLinks), value); + } + public bool RenderImages { get => _configurationService.Get(nameof(RenderImages), true); diff --git a/Wino.Mail/JS/libs/linkify-element.min.js b/Wino.Mail/JS/libs/linkify-element.min.js new file mode 100644 index 00000000..9d639984 --- /dev/null +++ b/Wino.Mail/JS/libs/linkify-element.min.js @@ -0,0 +1 @@ +var linkifyElement=function(e){"use strict";const t=1,n=3;function r(e,t,n){let r=n[n.length-1];e.replaceChild(r,t);for(let t=n.length-2;t>=0;t--)e.insertBefore(n[t],r),r=n[t]}function i(e,t,n){const r=[];for(let i=0;i=0)return l;let s=l.firstChild;for(;s;){let d,u,f;switch(s.nodeType){case t:o(s,a,c);break;case n:if(d=s.nodeValue,u=e.tokenize(d),0===u.length||1===u.length&&"text"===u[0].t)break;f=i(u,a,c),r(l,s,f),s=f[f.length-1]}s=s.nextSibling}return l}function l(e){return t=>{let{tagName:n,attributes:r,content:i,eventListeners:o}=t;const l=e.createElement(n);for(const e in r)l.setAttribute(e,r[e]);if(o&&l.addEventListener)for(const e in o)l.addEventListener(e,o[e]);return l.appendChild(e.createTextNode(i)),l}}function a(t,n,r){void 0===n&&(n=null),void 0===r&&(r=null);try{r=r||document||window&&window.document||global&&global.document}catch(e){}if(!r)throw new Error("Cannot find document implementation. If you are in a non-browser environment like Node.js, pass the document implementation as the third argument to linkifyElement.");return o(t,new e.Options(n,l(r)),r)}return a.helper=o,a.getDefaultRender=l,a.normalize=(t,n)=>new e.Options(t,l(n)),a}(linkify); diff --git a/Wino.Mail/JS/libs/linkify.min.js b/Wino.Mail/JS/libs/linkify.min.js new file mode 100644 index 00000000..088e0362 --- /dev/null +++ b/Wino.Mail/JS/libs/linkify.min.js @@ -0,0 +1 @@ +var linkify=function(t){"use strict";const e="aaa1rp3bb0ott3vie4c1le2ogado5udhabi7c0ademy5centure6ountant0s9o1tor4d0s1ult4e0g1ro2tna4f0l1rica5g0akhan5ency5i0g1rbus3force5tel5kdn3l0ibaba4pay4lfinanz6state5y2sace3tom5m0azon4ericanexpress7family11x2fam3ica3sterdam8nalytics7droid5quan4z2o0l2partments8p0le4q0uarelle8r0ab1mco4chi3my2pa2t0e3s0da2ia2sociates9t0hleta5torney7u0ction5di0ble3o3spost5thor3o0s4vianca6w0s2x0a2z0ure5ba0by2idu3namex3narepublic11d1k2r0celona5laycard4s5efoot5gains6seball5ketball8uhaus5yern5b0c1t1va3cg1n2d1e0ats2uty4er2ntley5rlin4st0buy5t2f1g1h0arti5i0ble3d1ke2ng0o3o1z2j1lack0friday9ockbuster8g1omberg7ue3m0s1w2n0pparibas9o0ats3ehringer8fa2m1nd2o0k0ing5sch2tik2on4t1utique6x2r0adesco6idgestone9oadway5ker3ther5ussels7s1t1uild0ers6siness6y1zz3v1w1y1z0h3ca0b1fe2l0l1vinklein9m0era3p2non3petown5ital0one8r0avan4ds2e0er0s4s2sa1e1h1ino4t0ering5holic7ba1n1re3c1d1enter4o1rn3f0a1d2g1h0anel2nel4rity4se2t2eap3intai5ristmas6ome4urch5i0priani6rcle4sco3tadel4i0c2y3k1l0aims4eaning6ick2nic1que6othing5ud3ub0med6m1n1o0ach3des3ffee4llege4ogne5m0cast4mbank4unity6pany2re3uter5sec4ndos3struction8ulting7tact3ractors9oking4l1p2rsica5untry4pon0s4rses6pa2r0edit0card4union9icket5own3s1uise0s6u0isinella9v1w1x1y0mru3ou3z2dabur3d1nce3ta1e1ing3sun4y2clk3ds2e0al0er2s3gree4livery5l1oitte5ta3mocrat6ntal2ist5si0gn4v2hl2iamonds6et2gital5rect0ory7scount3ver5h2y2j1k1m1np2o0cs1tor4g1mains5t1wnload7rive4tv2ubai3nlop4pont4rban5vag2r2z2earth3t2c0o2deka3u0cation8e1g1mail3erck5nergy4gineer0ing9terprises10pson4quipment8r0icsson6ni3s0q1tate5t1u0rovision8s2vents5xchange6pert3osed4ress5traspace10fage2il1rwinds6th3mily4n0s2rm0ers5shion4t3edex3edback6rrari3ero6i0delity5o2lm2nal1nce1ial7re0stone6mdale6sh0ing5t0ness6j1k1lickr3ghts4r2orist4wers5y2m1o0o0d1tball6rd1ex2sale4um3undation8x2r0ee1senius7l1ogans4ntier7tr2ujitsu5n0d2rniture7tbol5yi3ga0l0lery3o1up4me0s3p1rden4y2b0iz3d0n2e0a1nt0ing5orge5f1g0ee3h1i0ft0s3ves2ing5l0ass3e1obal2o4m0ail3bh2o1x2n1odaddy5ld0point6f2o0dyear5g0le4p1t1v2p1q1r0ainger5phics5tis4een3ipe3ocery4up4s1t1u0ardian6cci3ge2ide2tars5ru3w1y2hair2mburg5ngout5us3bo2dfc0bank7ealth0care8lp1sinki6re1mes5iphop4samitsu7tachi5v2k0t2m1n1ockey4ldings5iday5medepot5goods5s0ense7nda3rse3spital5t0ing5t0els3mail5use3w2r1sbc3t1u0ghes5yatt3undai7ibm2cbc2e1u2d1e0ee3fm2kano4l1m0amat4db2mo0bilien9n0c1dustries8finiti5o2g1k1stitute6urance4e4t0ernational10uit4vestments10o1piranga7q1r0ish4s0maili5t0anbul7t0au2v3jaguar4va3cb2e0ep2tzt3welry6io2ll2m0p2nj2o0bs1urg4t1y2p0morgan6rs3uegos4niper7kaufen5ddi3e0rryhotels6logistics9properties14fh2g1h1i0a1ds2m1ndle4tchen5wi3m1n1oeln3matsu5sher5p0mg2n2r0d1ed3uokgroup8w1y0oto4z2la0caixa5mborghini8er3ncaster6d0rover6xess5salle5t0ino3robe5w0yer5b1c1ds2ease3clerc5frak4gal2o2xus4gbt3i0dl2fe0insurance9style7ghting6ke2lly3mited4o2ncoln4k2psy3ve1ing5k1lc1p2oan0s3cker3us3l1ndon4tte1o3ve3pl0financial11r1s1t0d0a3u0ndbeck6xe1ury5v1y2ma0drid4if1son4keup4n0agement7go3p1rket0ing3s4riott5shalls7ttel5ba2c0kinsey7d1e0d0ia3et2lbourne7me1orial6n0u2rckmsd7g1h1iami3crosoft7l1ni1t2t0subishi9k1l0b1s2m0a2n1o0bi0le4da2e1i1m1nash3ey2ster5rmon3tgage6scow4to0rcycles9v0ie4p1q1r1s0d2t0n1r2u0seum3ic4v1w1x1y1z2na0b1goya4me2tura4vy3ba2c1e0c1t0bank4flix4work5ustar5w0s2xt0direct7us4f0l2g0o2hk2i0co2ke1on3nja3ssan1y5l1o0kia3rton4w0ruz3tv4p1r0a1w2tt2u1yc2z2obi1server7ffice5kinawa6layan0group9dnavy5lo3m0ega4ne1g1l0ine5oo2pen3racle3nge4g0anic5igins6saka4tsuka4t2vh3pa0ge2nasonic7ris2s1tners4s1y3y2ccw3e0t2f0izer5g1h0armacy6d1ilips5one2to0graphy6s4ysio5ics1tet2ures6d1n0g1k2oneer5zza4k1l0ace2y0station9umbing5s3m1n0c2ohl2ker3litie5rn2st3r0america6xi3ess3ime3o0d0uctions8f1gressive8mo2perties3y5tection8u0dential9s1t1ub2w0c2y2qa1pon3uebec3st5racing4dio4e0ad1lestate6tor2y4cipes5d0stone5umbrella9hab3ise0n3t2liance6n0t0als5pair3ort3ublican8st0aurant8view0s5xroth6ich0ardli6oh3l1o1p2o0cks3deo3gers4om3s0vp3u0gby3hr2n2w0e2yukyu6sa0arland6fe0ty4kura4le1on3msclub4ung5ndvik0coromant12ofi4p1rl2s1ve2xo3b0i1s2c0a1b1haeffler7midt4olarships8ol3ule3warz5ience5ot3d1e0arch3t2cure1ity6ek2lect4ner3rvices6ven3w1x0y3fr2g1h0angrila6rp2w2ell3ia1ksha5oes2p0ping5uji3w3i0lk2na1gles5te3j1k0i0n2y0pe4l0ing4m0art3ile4n0cf3o0ccer3ial4ftbank4ware6hu2lar2utions7ng1y2y2pa0ce3ort2t3r0l2s1t0ada2ples4r1tebank4farm7c0group6ockholm6rage3e3ream4udio2y3yle4u0cks3pplies3y2ort5rf1gery5zuki5v1watch4iss4x1y0dney4stems6z2tab1ipei4lk2obao4rget4tamotors6r2too4x0i3c0i2d0k2eam2ch0nology8l1masek5nnis4va3f1g1h0d1eater2re6iaa2ckets5enda4ps2res2ol4j0maxx4x2k0maxx5l1m0all4n1o0day3kyo3ols3p1ray3shiba5tal3urs3wn2yota3s3r0ade1ing4ining5vel0ers0insurance16ust3v2t1ube2i1nes3shu4v0s2w1z2ua1bank3s2g1k1nicom3versity8o2ol2ps2s1y1z2va0cations7na1guard7c1e0gas3ntures6risign5mögensberater2ung14sicherung10t2g1i0ajes4deo3g1king4llas4n1p1rgin4sa1ion4va1o3laanderen9n1odka3lvo3te1ing3o2yage5u2wales2mart4ter4ng0gou5tch0es6eather0channel12bcam3er2site5d0ding5ibo2r3f1hoswho6ien2ki2lliamhill9n0dows4e1ners6me2olterskluwer11odside6rk0s2ld3w2s1tc1f3xbox3erox4finity6ihuan4n2xx2yz3yachts4hoo3maxun5ndex5e1odobashi7ga2kohama6u0tube6t1un3za0ppos4ra3ero3ip2m1one3uerich6w2",n="ελ1υ2бг1ел3дети4ею2католик6ом3мкд2он1сква6онлайн5рг3рус2ф2сайт3рб3укр3қаз3հայ3ישראל5קום3ابوظبي5رامكو5لاردن4بحرين5جزائر5سعودية6عليان5مغرب5مارات5یران5بارت2زار4يتك3ھارت5تونس4سودان3رية5شبكة4عراق2ب2مان4فلسطين6قطر3كاثوليك6وم3مصر2ليسيا5وريتانيا7قع4همراه5پاکستان7ڀارت4कॉम3नेट3भारत0म्3ोत5संगठन5বাংলা5ভারত2ৰত4ਭਾਰਤ4ભારત4ଭାରତ4இந்தியா6லங்கை6சிங்கப்பூர்11భారత్5ಭಾರತ4ഭാരതം5ලංකා4คอม3ไทย3ລາວ3გე2みんな3アマゾン4クラウド4グーグル4コム2ストア3セール3ファッション6ポイント4世界2中信1国1國1文网3亚马逊3企业2佛山2信息2健康2八卦2公司1益2台湾1灣2商城1店1标2嘉里0大酒店5在线2大拿2天主教3娱乐2家電2广东2微博2慈善2我爱你3手机2招聘2政务1府2新加坡2闻2时尚2書籍2机构2淡马锡3游戏2澳門2点看2移动2组织机构4网址1店1站1络2联通2谷歌2购物2通販2集团2電訊盈科4飞利浦3食品2餐厅2香格里拉3港2닷넷1컴2삼성2한국2",i=(t,e)=>{for(const n in e)t[n]=e[n];return t},r="numeric",s="ascii",o="alpha",a="asciinumeric",l="alphanumeric",u="domain",c="emoji",g="scheme",d="slashscheme",h="whitespace";function f(t,e){return t in e||(e[t]=[]),e[t]}function p(t,e,n){e[r]&&(e[a]=!0,e[l]=!0),e[s]&&(e[a]=!0,e[o]=!0),e[a]&&(e[l]=!0),e[o]&&(e[l]=!0),e[l]&&(e[u]=!0),e[c]&&(e[u]=!0);for(const i in e){const e=f(i,n);e.indexOf(t)<0&&e.push(t)}}function m(t){void 0===t&&(t=null),this.j={},this.jr=[],this.jd=null,this.t=t}m.groups={},m.prototype={accepts(){return!!this.t},go(t){const e=this,n=e.j[t];if(n)return n;for(let n=0;n=0&&(n[i]=!0);return n}(a.t,r),n);p(o,t,r)}else n&&p(o,n,r);a.t=o}return s.j[t]=a,a}};const y=(t,e,n,i,r)=>t.ta(e,n,i,r),k=(t,e,n,i,r)=>t.tr(e,n,i,r),E=(t,e,n,i,r)=>t.ts(e,n,i,r),b=(t,e,n,i,r)=>t.tt(e,n,i,r),T="WORD",v="UWORD",R="LOCALHOST",A="TLD",L="UTLD",O="SCHEME",C="SLASH_SCHEME",w="NUM",S="WS",j="NL",N="OPENBRACE",x="CLOSEBRACE",I="OPENBRACKET",H="CLOSEBRACKET",P="OPENPAREN",z="CLOSEPAREN",D="OPENANGLEBRACKET",U="CLOSEANGLEBRACKET",M="FULLWIDTHLEFTPAREN",B="FULLWIDTHRIGHTPAREN",K="LEFTCORNERBRACKET",_="RIGHTCORNERBRACKET",Q="LEFTWHITECORNERBRACKET",F="RIGHTWHITECORNERBRACKET",W="FULLWIDTHLESSTHAN",$="FULLWIDTHGREATERTHAN",G="AMPERSAND",q="APOSTROPHE",Y="ASTERISK",J="AT",X="BACKSLASH",V="BACKTICK",Z="CARET",tt="COLON",et="COMMA",nt="DOLLAR",it="DOT",rt="EQUALS",st="EXCLAMATION",ot="HYPHEN",at="PERCENT",lt="PIPE",ut="PLUS",ct="POUND",gt="QUERY",dt="QUOTE",ht="SEMI",ft="SLASH",pt="TILDE",mt="UNDERSCORE",yt="EMOJI",kt="SYM";var Et=Object.freeze({__proto__:null,WORD:T,UWORD:v,LOCALHOST:R,TLD:A,UTLD:L,SCHEME:O,SLASH_SCHEME:C,NUM:w,WS:S,NL:j,OPENBRACE:N,CLOSEBRACE:x,OPENBRACKET:I,CLOSEBRACKET:H,OPENPAREN:P,CLOSEPAREN:z,OPENANGLEBRACKET:D,CLOSEANGLEBRACKET:U,FULLWIDTHLEFTPAREN:M,FULLWIDTHRIGHTPAREN:B,LEFTCORNERBRACKET:K,RIGHTCORNERBRACKET:_,LEFTWHITECORNERBRACKET:Q,RIGHTWHITECORNERBRACKET:F,FULLWIDTHLESSTHAN:W,FULLWIDTHGREATERTHAN:$,AMPERSAND:G,APOSTROPHE:q,ASTERISK:Y,AT:J,BACKSLASH:X,BACKTICK:V,CARET:Z,COLON:tt,COMMA:et,DOLLAR:nt,DOT:it,EQUALS:rt,EXCLAMATION:st,HYPHEN:ot,PERCENT:at,PIPE:lt,PLUS:ut,POUND:ct,QUERY:gt,QUOTE:dt,SEMI:ht,SLASH:ft,TILDE:pt,UNDERSCORE:mt,EMOJI:yt,SYM:kt});const bt=/[a-z]/,Tt=/\p{L}/u,vt=/\p{Emoji}/u,Rt=/\d/,At=/\s/;var Lt=Object.freeze({__proto__:null,ASCII_LETTER:bt,LETTER:Tt,EMOJI:vt,EMOJI_VARIATION:/\ufe0f/,DIGIT:Rt,SPACE:At});const Ot="\n",Ct="️",wt="‍";let St=null,jt=null;function Nt(t){const e=[],n=t.length;let i=0;for(;i56319||i+1===n||(r=t.charCodeAt(i+1))<56320||r>57343?t[i]:t.slice(i,i+2);e.push(o),i+=o.length}return e}function xt(t,e,n,i,r){let s;const o=e.length;for(let n=0;n=0;)r++;if(r>0){e.push(n.join(""));for(let e=parseInt(t.substring(i,i+r),10);e>0;e--)n.pop();i+=r}else n.push(t[i]),i++}return e}const Ht={defaultProtocol:"http",events:null,format:zt,formatHref:zt,nl2br:!1,tagName:"a",target:null,rel:null,validate:!0,truncate:1/0,className:null,attributes:null,ignoreTags:[],render:null};function Pt(t,e){void 0===e&&(e=null);let n=i({},Ht);t&&(n=i(n,t instanceof Pt?t.o:t));const r=n.ignoreTags,s=[];for(let t=0;tt,check(t){return this.get("validate",t.toString(),t)},get(t,e,n){const i=null!=e;let r=this.o[t];return r?("object"==typeof r?(r=n.t in r?r[n.t]:Ht[t],"function"==typeof r&&i&&(r=r(e,n))):"function"==typeof r&&i&&(r=r(e,n.t,n)),r):r},getObj(t,e,n){let i=this.o[t];return"function"==typeof i&&null!=e&&(i=i(e,n.t,n)),i},render(t){const e=t.render(this);return(this.get("render",null,t)||this.defaultRender)(e,t.t,t)}};var Dt=Object.freeze({__proto__:null,defaults:Ht,Options:Pt,assign:i});function Ut(t,e){this.t="token",this.v=t,this.tk=e}function Mt(t,e){class n extends Ut{constructor(e,n){super(e,n),this.t=t}}for(const t in e)n.prototype[t]=e[t];return n.t=t,n}Ut.prototype={isLink:!1,toString(){return this.v},toHref(t){return this.toString()},toFormattedString(t){const e=this.toString(),n=t.get("truncate",e,this),i=t.get("format",e,this);return n&&i.length>n?i.substring(0,n)+"…":i},toFormattedHref(t){return t.get("formatHref",this.toHref(t.get("defaultProtocol")),this)},startIndex(){return this.tk[0].s},endIndex(){return this.tk[this.tk.length-1].e},toObject(t){return void 0===t&&(t=Ht.defaultProtocol),{type:this.t,value:this.toString(),isLink:this.isLink,href:this.toHref(t),start:this.startIndex(),end:this.endIndex()}},toFormattedObject(t){return{type:this.t,value:this.toFormattedString(t),isLink:this.isLink,href:this.toFormattedHref(t),start:this.startIndex(),end:this.endIndex()}},validate(t){return t.get("validate",this.toString(),this)},render(t){const e=this,n=this.toHref(t.get("defaultProtocol")),r=t.get("formatHref",n,this),s=t.get("tagName",n,e),o=this.toFormattedString(t),a={},l=t.get("className",n,e),u=t.get("target",n,e),c=t.get("rel",n,e),g=t.getObj("attributes",n,e),d=t.getObj("events",n,e);return a.href=r,l&&(a.class=l),u&&(a.target=u),c&&(a.rel=c),g&&i(a,g),{tagName:s,attributes:a,content:o,eventListeners:d}}};const Bt=Mt("email",{isLink:!0,toHref(){return"mailto:"+this.toString()}}),Kt=Mt("text"),_t=Mt("nl"),Qt=Mt("url",{isLink:!0,toHref(t){return void 0===t&&(t=Ht.defaultProtocol),this.hasProtocol()?this.v:`${t}://${this.v}`},hasProtocol(){const t=this.tk;return t.length>=2&&t[0].t!==R&&t[1].t===tt}});var Ft=Object.freeze({__proto__:null,MultiToken:Ut,Base:Ut,createTokenClass:Mt,Email:Bt,Text:Kt,Nl:_t,Url:Qt});const Wt=t=>new m(t);function $t(t,e,n){const i=n[0].s,r=n[n.length-1].e;return new t(e.slice(i,r),n)}const Gt="undefined"!=typeof console&&console&&console.warn||(()=>{}),qt="until manual call of linkify.init(). Register all schemes and plugins before invoking linkify the first time.",Yt={scanner:null,parser:null,tokenQueue:[],pluginQueue:[],customSchemes:[],initialized:!1};function Jt(){Yt.scanner=function(t){void 0===t&&(t=[]);const l={};m.groups=l;const f=new m;null==St&&(St=It(e)),null==jt&&(jt=It(n)),b(f,"'",q),b(f,"{",N),b(f,"}",x),b(f,"[",I),b(f,"]",H),b(f,"(",P),b(f,")",z),b(f,"<",D),b(f,">",U),b(f,"(",M),b(f,")",B),b(f,"「",K),b(f,"」",_),b(f,"『",Q),b(f,"』",F),b(f,"<",W),b(f,">",$),b(f,"&",G),b(f,"*",Y),b(f,"@",J),b(f,"`",V),b(f,"^",Z),b(f,":",tt),b(f,",",et),b(f,"$",nt),b(f,".",it),b(f,"=",rt),b(f,"!",st),b(f,"-",ot),b(f,"%",at),b(f,"|",lt),b(f,"+",ut),b(f,"#",ct),b(f,"?",gt),b(f,'"',dt),b(f,"/",ft),b(f,";",ht),b(f,"~",pt),b(f,"_",mt),b(f,"\\",X);const y=k(f,Rt,w,{[r]:!0});k(y,Rt,y);const Lt=k(f,bt,T,{[s]:!0});k(Lt,bt,Lt);const Nt=k(f,Tt,v,{[o]:!0});k(Nt,bt),k(Nt,Tt,Nt);const Ht=k(f,At,S,{[h]:!0});b(f,Ot,j,{[h]:!0}),b(Ht,Ot),k(Ht,At,Ht);const Pt=k(f,vt,yt,{[c]:!0});k(Pt,vt,Pt),b(Pt,Ct,Pt);const zt=b(Pt,wt);k(zt,vt,Pt);const Dt=[[bt,Lt]],Ut=[[bt,null],[Tt,Nt]];for(let t=0;tt[0]>e[0]?1:-1));for(let e=0;e=0?i[u]=!0:bt.test(n)?Rt.test(n)?i[a]=!0:i[s]=!0:i[r]=!0,E(f,n,n,i)}return E(f,"localhost",R,{ascii:!0}),f.jd=new m(kt),{start:f,tokens:i({groups:l},Et)}}(Yt.customSchemes);for(let t=0;t=0&&d++,r++,c++;if(d<0)r-=c,r0&&(s.push($t(Kt,e,o)),o=[]),r-=d,c-=d;const t=g.t,i=n.slice(r-c,r);s.push($t(t,e,i))}}return o.length>0&&s.push($t(Kt,e,o)),s}(Yt.parser.start,t,function(t,e){const n=Nt(e.replace(/[A-Z]/g,(t=>t.toLowerCase()))),i=n.length,r=[];let s=0,o=0;for(;o=0&&(g+=n[o].length,d++),u+=n[o].length,s+=n[o].length,o++;s-=g,o-=d,u-=g,r.push({t:c.t,v:e.slice(s-u,s),s:s-u,e:s})}return r}(Yt.scanner.start,t))}return t.MultiToken=Ut,t.Options=Pt,t.State=m,t.createTokenClass=Mt,t.find=function(t,e,n){if(void 0===e&&(e=null),void 0===n&&(n=null),e&&"object"==typeof e){if(n)throw Error(`linkifyjs: Invalid link type ${e}; must be a string`);n=e,e=null}const i=new Pt(n),r=Xt(t),s=[];for(let t=0;t - - - - - - - - + - function ChangeFontFamily(fontFamily) { - var containerDiv = document.getElementById("readerDiv"); - containerDiv.style.fontFamily = fontFamily; - } + + + + + -
- + // Called when rendering a new email for the first time + function RenderHTML(htmlString, shouldLinkifyText = true) { + window.scroll(0, 0); + + _htmlString = htmlString; + _shouldLinkifyText = shouldLinkifyText; + + internalRenderHTML(htmlString); + } + + // Called to render or refresh the email + function internalRenderHTML(htmlString) { + var containerDiv = document.getElementById("readerDiv"); + try { + containerDiv.innerHTML = htmlString; + + // Linkify plain text links if enabled + if (_shouldLinkifyText) { + linkifyElement( + containerDiv, + { className: "wino-plain-link" }, + document + ); + } + + // Remove !important from inline styles if dark mode is enabled + if ( + document.documentElement.getAttribute("data-theme") === + "dark" + ) { + removeImportantFromInlineStyles(); + } + } catch (e) { + containerDiv.innerHTML = htmlString; + } + } + + function ChangeFontFamily(fontFamily) { + var containerDiv = document.getElementById("readerDiv"); + containerDiv.style.fontFamily = fontFamily; + } + + function ChangeFontSize(size) { + var containerDiv = document.getElementById("readerDiv"); + containerDiv.style.fontSize = size; + } + + function SetLightEditor() { + document.documentElement.setAttribute("data-theme", "light"); + DarkReader.disable(); + + internalRenderHTML(_htmlString); + } + + function SetDarkEditor() { + document.documentElement.setAttribute("data-theme", "dark"); + DarkReader.enable(); + + internalRenderHTML(_htmlString); + } + + // Helper functions + function removeImportantFromInlineStyles() { + var allElements = document.querySelectorAll("*"); + allElements.forEach(function (element) { + var style = element.getAttribute("style"); + if (style) { + var newStyle = style.replace(/!important/g, ""); + element.setAttribute("style", newStyle); + } + }); + } + +
+ diff --git a/Wino.Mail/Views/MailRenderingPage.xaml.cs b/Wino.Mail/Views/MailRenderingPage.xaml.cs index 79991154..0208d875 100644 --- a/Wino.Mail/Views/MailRenderingPage.xaml.cs +++ b/Wino.Mail/Views/MailRenderingPage.xaml.cs @@ -96,7 +96,8 @@ namespace Wino.Views } else { - await ExecuteScriptFunctionAsync("RenderHTML", htmlBody); + var shouldLinkifyText = ViewModel.CurrentRenderModel?.MailRenderingOptions?.RenderPlaintextLinks ?? true; + await ExecuteScriptFunctionAsync("RenderHTML", htmlBody, shouldLinkifyText); } isRenderingInProgress = false; diff --git a/Wino.Mail/Views/Settings/ReadComposePanePage.xaml b/Wino.Mail/Views/Settings/ReadComposePanePage.xaml index 380945ce..89863050 100644 --- a/Wino.Mail/Views/Settings/ReadComposePanePage.xaml +++ b/Wino.Mail/Views/Settings/ReadComposePanePage.xaml @@ -69,6 +69,12 @@ + + + + + + @@ -116,5 +122,4 @@ - - + \ No newline at end of file diff --git a/Wino.Mail/Wino.Mail.csproj b/Wino.Mail/Wino.Mail.csproj index 814784f4..9cf6306f 100644 --- a/Wino.Mail/Wino.Mail.csproj +++ b/Wino.Mail/Wino.Mail.csproj @@ -567,6 +567,8 @@ + + diff --git a/Wino.Packaging/Wino.Packaging.wapproj b/Wino.Packaging/Wino.Packaging.wapproj index 008faf3b..c66a9cdc 100644 --- a/Wino.Packaging/Wino.Packaging.wapproj +++ b/Wino.Packaging/Wino.Packaging.wapproj @@ -44,7 +44,7 @@ False SHA256 False - C:\Users\bkaan\Desktop\Packages\ + $(USERPROFILE)\Desktop\Packages\ True x86|x64|arm64 True