diff --git a/v3/examples/notifications/frontend/Inter Font License.txt b/v3/examples/notifications/frontend/Inter Font License.txt new file mode 100644 index 000000000..b525cbf3a --- /dev/null +++ b/v3/examples/notifications/frontend/Inter Font License.txt @@ -0,0 +1,93 @@ +Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/notifications/index.js b/v3/examples/notifications/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/notifications/index.js similarity index 100% rename from v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/notifications/index.js rename to v3/examples/notifications/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/notifications/index.js diff --git a/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/notifications/models.js b/v3/examples/notifications/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/notifications/models.js similarity index 100% rename from v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/notifications/models.js rename to v3/examples/notifications/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/notifications/models.js diff --git a/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/notifications/service.js b/v3/examples/notifications/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/notifications/service.js similarity index 95% rename from v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/notifications/service.js rename to v3/examples/notifications/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/notifications/service.js index 5c91a1c77..8fb7eff96 100644 --- a/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/notifications/service.js +++ b/v3/examples/notifications/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/notifications/service.js @@ -30,6 +30,7 @@ export function RegisterNotificationCategory(category) { } /** + * RemoveAllDeliveredNotifications removes all delivered notifications * @returns {Promise & { cancel(): void }} */ export function RemoveAllDeliveredNotifications() { @@ -47,6 +48,7 @@ export function RemoveAllPendingNotifications() { } /** + * RemoveDeliveredNotification removes a specific delivered notification * @param {string} identifier * @returns {Promise & { cancel(): void }} */ diff --git a/v3/examples/notifications/frontend/dist/Inter-Medium.ttf b/v3/examples/notifications/frontend/dist/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/examples/notifications/frontend/dist/Inter-Medium.ttf differ diff --git a/v3/examples/notifications/frontend/dist/assets/index-CS0sRFez.js b/v3/examples/notifications/frontend/dist/assets/index-CS0sRFez.js new file mode 100644 index 000000000..297ce49b9 --- /dev/null +++ b/v3/examples/notifications/frontend/dist/assets/index-CS0sRFez.js @@ -0,0 +1,6 @@ +(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))o(r);new MutationObserver(r=>{for(const s of r)if(s.type==="childList")for(const l of s.addedNodes)l.tagName==="LINK"&&l.rel==="modulepreload"&&o(l)}).observe(document,{childList:!0,subtree:!0});function n(r){const s={};return r.integrity&&(s.integrity=r.integrity),r.referrerPolicy&&(s.referrerPolicy=r.referrerPolicy),r.crossOrigin==="use-credentials"?s.credentials="include":r.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function o(r){if(r.ep)return;r.ep=!0;const s=n(r);fetch(r.href,s)}})();let L="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict",O=(t=21)=>{let e="",n=t|0;for(;n--;)e+=L[Math.random()*64|0];return e};const _=window.location.origin+"/wails/runtime",h={Call:0,Events:3,ContextMenu:4,Dialog:5,Window:6,Browser:9,CancelCall:10};let k=O();function w(t,e){return function(n,o=null){return W(t,n,e,o)}}async function W(t,e,n,o){let r=new URL(_);t!=null&&r.searchParams.append("object",t),e!=null&&r.searchParams.append("method",e);let s={headers:{}};n&&(s.headers["x-wails-window-name"]=n),o&&r.searchParams.append("args",JSON.stringify(o)),s.headers["x-wails-client-id"]=k;let l=await fetch(r,s);if(!l.ok)throw new Error(await l.text());return l.headers.get("Content-Type")&&l.headers.get("Content-Type").indexOf("application/json")!==-1?l.json():l.text()}const P=(()=>{var t,e,n;try{if((t=window==null?void 0:window.chrome)!=null&&t.webview)return o=>window.chrome.webview.postMessage(o);if((n=(e=window==null?void 0:window.webkit)==null?void 0:e.messageHandlers)!=null&&n.external)return o=>window.webkit.messageHandlers.external.postMessage(o)}catch{console.warn(` +%c⚠️ Browser Environment Detected %c + +%cOnly UI previews are available in the browser. For full functionality, please run the application in desktop mode. +More information at: https://v3.wails.io/learn/build/#using-a-browser-for-development +`,"background: #ffffff; color: #000000; font-weight: bold; padding: 4px 8px; border-radius: 4px; border: 2px solid #000000;","background: transparent;","color: #ffffff; font-style: italic; font-weight: bold;")}return null})();function E(t){if(P)return P(t)}function H(){return window._wails.environment.OS==="windows"}function B(){return window._wails.environment.Debug===!0}window.addEventListener("contextmenu",Y);const U=w(h.ContextMenu,""),$=0;function Z(t,e,n,o){U($,{id:t,x:e,y:n,data:o})}function Y(t){let e=t.target,n=window.getComputedStyle(e).getPropertyValue("--custom-contextmenu");if(n=n?n.trim():"",n){t.preventDefault();let o=window.getComputedStyle(e).getPropertyValue("--custom-contextmenu-data");Z(n,t.clientX,t.clientY,o);return}j(t)}function j(t){if(B())return;const e=t.target;switch(window.getComputedStyle(e).getPropertyValue("--default-contextmenu").trim()){case"show":return;case"hide":t.preventDefault();return;default:if(e.isContentEditable)return;const r=window.getSelection(),s=r.toString().length>0;if(s)for(let l=0;lt.target.clientWidth||t.offsetY>t.target.clientHeight)return;p=!0}else p=!1}function V(){p=!1}function u(t){document.documentElement.style.cursor=t||G,S=t}function q(t){if(p&&(p=!1,(t.buttons!==void 0?t.buttons:t.which)>0)){E("wails:drag");return}if(!z||!H())return;let e=x("system.resizeHandleHeight")||5,n=x("system.resizeHandleWidth")||5,o=x("resizeCornerExtra")||10,r=window.outerWidth-t.clientXoe(type,{"call-id":e});let o=!1,r=!1,s=new Promise((l,a)=>{t["call-id"]=e,y.set(e,{resolve:l,reject:a}),ne(te,t).then(c=>{if(r=!0,o)return n()}).catch(c=>{a(c),y.delete(e)})});return s.cancel=()=>{if(r)return n();o=!0},s}function R(t,...e){return ae({methodID:t,args:e})}window._wails=window._wails||{};window._wails.dialogErrorCallback=we;window._wails.dialogResultCallback=he;const ce=2,ue=3,de=w(h.Dialog,""),f=new Map;function fe(){let t;do t=O();while(f.has(t));return t}function I(t,e={}){const n=fe();return e["dialog-id"]=n,new Promise((o,r)=>{f.set(n,{resolve:o,reject:r}),de(t,e).catch(s=>{r(s),f.delete(n)})})}function he(t,e,n){let o=f.get(t);o&&(f.delete(t),n?o.resolve(JSON.parse(e)):o.resolve(e))}function we(t,e){let n=f.get(t);n&&(f.delete(t),n.reject(new me(e)))}const me=t=>I(ce,t),ge=t=>I(ue,t);window._wails=window._wails||{};window._wails.dispatchWailsEvent=Ce;const pe=w(h.Events,""),Me=0,d=new Map;class be{constructor(e,n,o){this.eventName=e,this.maxCallbacks=o||-1,this.Callback=r=>(n(r),this.maxCallbacks===-1?!1:(this.maxCallbacks-=1,this.maxCallbacks===0))}}class ye{constructor(e,n=null){this.name=e,this.data=n}}function Ce(t){let e=d.get(t.name);if(e){let n=e.filter(o=>{if(o.Callback(t))return!0});n.length>0&&(e=e.filter(o=>!n.includes(o)),e.length===0?d.delete(t.name):d.set(t.name,e))}}function Ee(t,e,n){let o=d.get(t)||[];const r=new be(t,e,n);return o.push(r),d.set(t,o),()=>Se(r)}function N(t,e){return Ee(t,e,-1)}function Se(t){const e=t.eventName;let n=d.get(e).filter(o=>o!==t);n.length===0?d.delete(e):d.set(e,n)}function ve(t){return pe(Me,t)}const Re=0,xe=1,Oe=2,Te=3,Pe=4,ze=5,De=6,Ie=7,Ne=8,Ae=9,Fe=10,Le=11,_e=12,ke=13,We=14,He=15,Be=16,Ue=17,$e=18,Ze=19,Ye=20,je=21,Ge=22,Je=23,Xe=24,Ve=25,qe=26,Ke=27,Qe=28,et=29,tt=30,nt=31,ot=32,rt=33,it=34,st=35,lt=36,at=37,ct=38,ut=39,dt=40,ft=41,ht=42,wt=43,mt=44,gt=45,pt=46,Mt=47,i=Symbol();class v{constructor(e=""){this[i]=w(h.Window,e);for(const n of Object.getOwnPropertyNames(v.prototype))n!=="constructor"&&typeof this[n]=="function"&&(this[n]=this[n].bind(this))}Get(e){return new v(e)}Position(){return this[i](Re)}Center(){return this[i](xe)}Close(){return this[i](Oe)}DisableSizeConstraints(){return this[i](Te)}EnableSizeConstraints(){return this[i](Pe)}Focus(){return this[i](ze)}ForceReload(){return this[i](De)}Fullscreen(){return this[i](Ie)}GetScreen(){return this[i](Ne)}GetZoom(){return this[i](Ae)}Height(){return this[i](Fe)}Hide(){return this[i](Le)}IsFocused(){return this[i](_e)}IsFullscreen(){return this[i](ke)}IsMaximised(){return this[i](We)}IsMinimised(){return this[i](He)}Maximise(){return this[i](Be)}Minimise(){return this[i](Ue)}Name(){return this[i]($e)}OpenDevTools(){return this[i](Ze)}RelativePosition(){return this[i](Ye)}Reload(){return this[i](je)}Resizable(){return this[i](Ge)}Restore(){return this[i](Je)}SetPosition(e,n){return this[i](Xe,{x:e,y:n})}SetAlwaysOnTop(e){return this[i](Ve,{alwaysOnTop:e})}SetBackgroundColour(e,n,o,r){return this[i](qe,{r:e,g:n,b:o,a:r})}SetFrameless(e){return this[i](Ke,{frameless:e})}SetFullscreenButtonEnabled(e){return this[i](Qe,{enabled:e})}SetMaxSize(e,n){return this[i](et,{width:e,height:n})}SetMinSize(e,n){return this[i](tt,{width:e,height:n})}SetRelativePosition(e,n){return this[i](nt,{x:e,y:n})}SetResizable(e){return this[i](ot,{resizable:e})}SetSize(e,n){return this[i](rt,{width:e,height:n})}SetTitle(e){return this[i](it,{title:e})}SetZoom(e){return this[i](st,{zoom:e})}Show(){return this[i](lt)}Size(){return this[i](at)}ToggleFullscreen(){return this[i](ct)}ToggleMaximise(){return this[i](ut)}UnFullscreen(){return this[i](dt)}UnMaximise(){return this[i](ft)}UnMinimise(){return this[i](ht)}Width(){return this[i](wt)}Zoom(){return this[i](mt)}ZoomIn(){return this[i](gt)}ZoomOut(){return this[i](pt)}ZoomReset(){return this[i](Mt)}}const bt=new v("");function yt(){if(!EventTarget||!AbortSignal||!AbortController)return!1;let t=!0;const e=new EventTarget,n=new AbortController;return e.addEventListener("test",()=>{t=!1},{signal:n.signal}),n.abort(),e.dispatchEvent(new CustomEvent("test")),t}document.addEventListener("DOMContentLoaded",()=>!0);function Ct(t,e=null){ve(new ye(t,e))}function Et(t,e){const n=bt.Get(t),o=n[e];if(typeof o!="function"){console.error(`Window method '${e}' not found`);return}try{o.call(n)}catch(r){console.error(`Error calling window method '${e}': `,r)}}function St(t){const e=t.currentTarget;function n(r="Yes"){if(r!=="Yes")return;const s=e.getAttribute("data-wml-event"),l=e.getAttribute("data-wml-target-window")||"",a=e.getAttribute("data-wml-window"),c=e.getAttribute("data-wml-openURL");s!==null&&Ct(s),a!==null&&Et(l,a),c!==null&&ee(c)}const o=e.getAttribute("data-wml-confirm");o?ge({Title:"Confirm",Message:o,Detached:!1,Buttons:[{Label:"Yes"},{Label:"No",IsDefault:!0}]}).then(n):n()}const C=Symbol();class vt{constructor(){this[C]=new AbortController}set(e,n){return{signal:this[C].signal}}reset(){this[C].abort(),this[C]=new AbortController}}const b=Symbol(),g=Symbol();class Rt{constructor(){this[b]=new WeakMap,this[g]=0}set(e,n){return this[g]+=!this[b].has(e),this[b].set(e,n),{}}reset(){if(!(this[g]<=0)){for(const e of document.body.querySelectorAll("*")){if(this[g]<=0)break;const n=this[b].get(e);this[g]-=typeof n<"u";for(const o of n||[])e.removeEventListener(o,St)}this[b]=new WeakMap,this[g]=0}}}yt()?new vt:new Rt;window._wails=window._wails||{};let A=!1;function xt(){window._wails.invoke=E,E("wails:runtime:ready"),A=!0}window.addEventListener("load",()=>{A||xt()});function Ot(t){return R(2679064664,t)}function F(){return R(3412125712)}function Tt(t,e,n,o){return R(2246903123,t,e,n,o)}function Pt(t){return R(1615199806,t)}const zt=document.getElementById("time"),Dt=document.getElementById("notifications");window.sendNotification=async()=>{await F()&&await Tt("some-uuid-fronted","Frontend Notificaiton","","Notificaiton sent through JS!")};window.sendComplexNotification=async()=>{await F()&&(await Ot({id:"FRONTEND_NOTIF",actions:[{id:"VIEW_ACTION",title:"View"},{id:"MARK_READ_ACTION",title:"Mark as Read"},{id:"DELETE_ACTION",title:"Delete",destructive:!0}],hasReplyField:!0,replyButtonTitle:"Reply",replyPlaceholder:"Reply to frontend..."}),await Pt({id:"some-uuid-complex",title:"Complex Frontend Notification",subtitle:"From: Jane Doe",body:"Is it rainging today where you are?",categoryId:"FRONTEND_NOTIF",data:{messageId:"msg-456",senderId:"user-456",timestamp:Date.now()}}))};N("time",t=>{zt.innerText=t.data});N("notificationResponse",t=>{Dt.innerText+=JSON.stringify(t.data[0].data)}); diff --git a/v3/examples/notifications/frontend/dist/index.html b/v3/examples/notifications/frontend/dist/index.html new file mode 100644 index 000000000..39216bd8d --- /dev/null +++ b/v3/examples/notifications/frontend/dist/index.html @@ -0,0 +1,29 @@ + + + + + + + + Wails App + + + +
+ +

Wails + Javascript

+ +
+ + diff --git a/v3/examples/notifications/frontend/dist/javascript.svg b/v3/examples/notifications/frontend/dist/javascript.svg new file mode 100644 index 000000000..f9abb2b72 --- /dev/null +++ b/v3/examples/notifications/frontend/dist/javascript.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/examples/notifications/frontend/dist/style.css b/v3/examples/notifications/frontend/dist/style.css new file mode 100644 index 000000000..127799eda --- /dev/null +++ b/v3/examples/notifications/frontend/dist/style.css @@ -0,0 +1,160 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +* { + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + /* width: 60px; */ + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.vanilla:hover { + filter: drop-shadow(0 0 2em #f7df1eaa); +} + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} \ No newline at end of file diff --git a/v3/examples/notifications/frontend/dist/wails.png b/v3/examples/notifications/frontend/dist/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/examples/notifications/frontend/dist/wails.png differ diff --git a/v3/examples/notifications/frontend/index.html b/v3/examples/notifications/frontend/index.html new file mode 100644 index 000000000..bde7d3124 --- /dev/null +++ b/v3/examples/notifications/frontend/index.html @@ -0,0 +1,29 @@ + + + + + + + + Wails App + + +
+ +

Wails + Javascript

+ +
+ + + diff --git a/v3/examples/notifications/frontend/main.js b/v3/examples/notifications/frontend/main.js new file mode 100644 index 000000000..54c03e560 --- /dev/null +++ b/v3/examples/notifications/frontend/main.js @@ -0,0 +1,50 @@ +import * as Notifications from "./bindings/github.com/wailsapp/wails/v3/pkg/services/notifications/service"; +import { Events } from "@wailsio/runtime"; + +const timeElement = document.getElementById('time'); +const notificationsElement = document.getElementById('notifications'); + +window.sendNotification = async () => { + const granted = await Notifications.RequestUserNotificationAuthorization(); + if (granted) { + await Notifications.SendNotification("some-uuid-fronted", "Frontend Notificaiton", "", "Notificaiton sent through JS!"); + } +} + +window.sendComplexNotification = async () => { + const granted = await Notifications.RequestUserNotificationAuthorization(); + if (granted) { + await Notifications.RegisterNotificationCategory({ + id: "FRONTEND_NOTIF", + actions: [ + { id: "VIEW_ACTION", title: "View" }, + { id: "MARK_READ_ACTION", title: "Mark as Read" }, + { id: "DELETE_ACTION", title: "Delete", destructive: true }, + ], + hasReplyField: true, + replyButtonTitle: "Reply", + replyPlaceholder: "Reply to frontend...", + }); + + await Notifications.SendNotificationWithActions({ + id: "some-uuid-complex", + title: "Complex Frontend Notification", + subtitle: "From: Jane Doe", + body: "Is it rainging today where you are?", + categoryId: "FRONTEND_NOTIF", + data: { + messageId: "msg-456", + senderId: "user-456", + timestamp: Date.now(), + } + }); + } +} + +Events.On('time', (time) => { + timeElement.innerText = time.data; +}); + +Events.On("notificationResponse", (response) => { + notificationsElement.innerText += JSON.stringify(response.data[0].data); +}); \ No newline at end of file diff --git a/v3/examples/notifications/frontend/package.json b/v3/examples/notifications/frontend/package.json new file mode 100644 index 000000000..9ae87549e --- /dev/null +++ b/v3/examples/notifications/frontend/package.json @@ -0,0 +1,16 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build:dev": "vite build --minify false --mode development", + "build": "vite build --mode production", + "preview": "vite preview" + }, + "devDependencies": { + "vite": "^5.0.0", + "@wailsio/runtime": "latest" + } +} \ No newline at end of file diff --git a/v3/examples/notifications/frontend/public/Inter-Medium.ttf b/v3/examples/notifications/frontend/public/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/examples/notifications/frontend/public/Inter-Medium.ttf differ diff --git a/v3/examples/notifications/frontend/public/javascript.svg b/v3/examples/notifications/frontend/public/javascript.svg new file mode 100644 index 000000000..f9abb2b72 --- /dev/null +++ b/v3/examples/notifications/frontend/public/javascript.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/examples/notifications/frontend/public/style.css b/v3/examples/notifications/frontend/public/style.css new file mode 100644 index 000000000..127799eda --- /dev/null +++ b/v3/examples/notifications/frontend/public/style.css @@ -0,0 +1,160 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +* { + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + /* width: 60px; */ + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.vanilla:hover { + filter: drop-shadow(0 0 2em #f7df1eaa); +} + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} \ No newline at end of file diff --git a/v3/examples/notifications/frontend/public/wails.png b/v3/examples/notifications/frontend/public/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/examples/notifications/frontend/public/wails.png differ diff --git a/v3/examples/notifications/main.go b/v3/examples/notifications/main.go new file mode 100644 index 000000000..8c2e941df --- /dev/null +++ b/v3/examples/notifications/main.go @@ -0,0 +1,135 @@ +package main + +import ( + "embed" + _ "embed" + "encoding/json" + "fmt" + "log" + "time" + + "github.com/wailsapp/wails/v3/pkg/application" + "github.com/wailsapp/wails/v3/pkg/events" + "github.com/wailsapp/wails/v3/pkg/services/notifications" +) + +//go:embed all:frontend/dist +var assets embed.FS + +func main() { + NotificationService := notifications.New() + + app := application.New(application.Options{ + Name: "notifications", + Description: "A demo of using raw HTML & CSS", + Services: []application.Service{ + application.NewService(NotificationService), + }, + Assets: application.AssetOptions{ + Handler: application.AssetFileServerFS(assets), + }, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + }) + + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ + Title: "Window 1", + Mac: application.MacWindow{ + InvisibleTitleBarHeight: 50, + Backdrop: application.MacBackdropTranslucent, + TitleBar: application.MacTitleBarHiddenInset, + }, + BackgroundColour: application.NewRGB(27, 38, 54), + URL: "/", + }) + + app.OnApplicationEvent(events.Mac.ApplicationDidFinishLaunching, func(event *application.ApplicationEvent) { + // Request pemission to send notifications + granted, err := NotificationService.RequestUserNotificationAuthorization() + if err != nil { + log.Default().Printf("WARNING: %s\n", err) + } + + if granted { + // Send notification with no actions + err = NotificationService.SendNotification("some-uuid", "Title", "", "body!") + if err != nil { + log.Default().Printf("WARNING: %s\n", err) + } + + err = NotificationService.RegisterNotificationCategory(notifications.NotificationCategory{ + ID: "MESSAGE_CATEGORY", + Actions: []notifications.NotificationAction{ + {ID: "VIEW_ACTION", Title: "View"}, + {ID: "MARK_READ_ACTION", Title: "Mark as Read"}, + {ID: "DELETE_ACTION", Title: "Delete", Destructive: true}, + }, + HasReplyField: true, + ReplyPlaceholder: "Type your reply...", + ReplyButtonTitle: "Reply", + }) + if err != nil { + log.Default().Printf("WARNING: %s\n", err) + } + + err = NotificationService.SendNotificationWithActions(notifications.NotificationOptions{ + ID: "some-other-uuid", + Title: "New Message", + Subtitle: "From: Jane Doe", + Body: "Is it raining today where you are?", + CategoryID: "MESSAGE_CATEGORY", + Data: map[string]interface{}{ + "messageId": "msg-123", + "senderId": "user-123", + "timestamp": time.Now().Unix(), + }, + }) + if err != nil { + log.Default().Printf("WARNING: %s\n", err) + } + } + }) + + app.OnEvent("notificationResponse", func(event *application.CustomEvent) { + data, _ := json.Marshal(event.Data) + fmt.Printf("%s\n", string(data)) + }) + + go func() { + time.Sleep(time.Second * 5) + // Sometime later check if you are still authorized + granted, err := NotificationService.CheckNotificationAuthorization() + if err != nil { + log.Default().Printf("WARNING: %s\n", err) + } + println(granted) + + // Sometime later remove delivered notification by identifier + err = NotificationService.RemoveDeliveredNotification("some-uuid") + if err != nil { + log.Default().Printf("WARNING: %s\n", err) + } + }() + + go func() { + time.Sleep(time.Second * 10) + // Sometime later remove all pending and delivered notifications + err := NotificationService.RemoveAllPendingNotifications() + if err != nil { + log.Default().Printf("WARNING: %s\n", err) + } + err = NotificationService.RemoveAllDeliveredNotifications() + if err != nil { + log.Default().Printf("WARNING: %s\n", err) + } + }() + + // Run the application. This blocks until the application has been exited. + err := app.Run() + + // If an error occurred while running the application, log it and exit. + if err != nil { + log.Fatal(err) + } +} diff --git a/v3/examples/services/assets/index.html b/v3/examples/services/assets/index.html index 24a372593..67746fdec 100644 --- a/v3/examples/services/assets/index.html +++ b/v3/examples/services/assets/index.html @@ -10,13 +10,6 @@ import * as kvstore from './bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/keyvaluestore.js'; import {Debug, Info, Warning, Error} from './bindings/github.com/wailsapp/wails/v3/pkg/services/log/loggerservice.js'; import * as hash from './bindings/github.com/wailsapp/wails/v3/examples/services/hashes/service.js'; - import * as notifications from "./bindings/github.com/wailsapp/wails/v3/pkg/services/notifications/service.js" - - notifications.RequestUserNotificationAuthorization().then((granted) => { - if (granted) { - notifications.SendNotification("some-uuid-fronted", "Frontend Notificaiton", "", "Notificaiton sent through JS!"); - } - }); function runHash() { let hashstring = document.getElementById("hashstring").value; diff --git a/v3/examples/services/main.go b/v3/examples/services/main.go index 6bc020c8a..b85650784 100644 --- a/v3/examples/services/main.go +++ b/v3/examples/services/main.go @@ -12,7 +12,6 @@ import ( "github.com/wailsapp/wails/v3/pkg/services/fileserver" "github.com/wailsapp/wails/v3/pkg/services/kvstore" "github.com/wailsapp/wails/v3/pkg/services/log" - "github.com/wailsapp/wails/v3/pkg/services/notifications" "github.com/wailsapp/wails/v3/pkg/services/sqlite" ) @@ -50,7 +49,6 @@ func main() { }), application.ServiceOptions{ Route: "/files", }), - application.NewService(notifications.New()), }, Assets: application.AssetOptions{ Handler: application.BundledAssetFileServer(assets),