diff --git a/v3/examples/notifications/frontend/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 index 2525e0f38..958e7d79c 100644 --- a/v3/examples/notifications/frontend/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 @@ -9,8 +9,8 @@ import {Create as $Create} from "@wailsio/runtime"; /** * NotificationAction represents an action button for a notification * @typedef {Object} NotificationAction - * @property {string} id - * @property {string} title + * @property {string} [id] + * @property {string} [title] * @property {boolean} [destructive] * @property {boolean} [authenticationRequired] */ @@ -18,8 +18,8 @@ import {Create as $Create} from "@wailsio/runtime"; /** * NotificationCategory groups actions for notifications * @typedef {Object} NotificationCategory - * @property {string} id - * @property {NotificationAction[]} actions + * @property {string} [id] + * @property {NotificationAction[]} [actions] * @property {boolean} [hasReplyField] * @property {string} [replyPlaceholder] * @property {string} [replyButtonTitle] @@ -28,10 +28,10 @@ import {Create as $Create} from "@wailsio/runtime"; /** * NotificationOptions contains configuration for a notification * @typedef {Object} NotificationOptions - * @property {string} id - * @property {string} title + * @property {string} [id] + * @property {string} [title] * @property {string} [subtitle] - * @property {string} body + * @property {string} [body] * @property {string} [categoryId] * @property {{ [_: string]: any }} [data] */ diff --git a/v3/examples/notifications/frontend/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 index 8fb7eff96..85da85975 100644 --- a/v3/examples/notifications/frontend/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 @@ -11,7 +11,8 @@ import {Call as $Call, Create as $Create} from "@wailsio/runtime"; import * as $models from "./models.js"; /** - * CheckNotificationAuthorization checks current permission status + * CheckNotificationAuthorization is a Windows stub that always returns true. + * (user authorization is macOS-specific) * @returns {Promise & { cancel(): void }} */ export function CheckNotificationAuthorization() { @@ -20,7 +21,7 @@ export function CheckNotificationAuthorization() { } /** - * RegisterNotificationCategory registers a category with actions and optional reply field + * RegisterNotificationCategory registers a new NotificationCategory to be used with SendNotificationWithActions. * @param {$models.NotificationCategory} category * @returns {Promise & { cancel(): void }} */ @@ -30,7 +31,8 @@ export function RegisterNotificationCategory(category) { } /** - * RemoveAllDeliveredNotifications removes all delivered notifications + * RemoveAllDeliveredNotifications is a Windows stub that always returns nil. + * (macOS-specific) * @returns {Promise & { cancel(): void }} */ export function RemoveAllDeliveredNotifications() { @@ -39,7 +41,8 @@ export function RemoveAllDeliveredNotifications() { } /** - * RemoveAllPendingNotifications removes all pending notifications + * RemoveAllPendingNotifications is a Windows stub that always returns nil. + * (macOS-specific) * @returns {Promise & { cancel(): void }} */ export function RemoveAllPendingNotifications() { @@ -48,27 +51,40 @@ export function RemoveAllPendingNotifications() { } /** - * RemoveDeliveredNotification removes a specific delivered notification - * @param {string} identifier + * RemoveDeliveredNotification is a Windows stub that always returns nil. + * (macOS-specific) + * @param {string} $0 * @returns {Promise & { cancel(): void }} */ -export function RemoveDeliveredNotification(identifier) { - let $resultPromise = /** @type {any} */($Call.ByID(149440045, identifier)); +export function RemoveDeliveredNotification($0) { + let $resultPromise = /** @type {any} */($Call.ByID(149440045, $0)); return $resultPromise; } /** - * RemovePendingNotification removes a specific pending notification - * @param {string} identifier + * RemoveNotificationCategory removes a previously registered NotificationCategory. + * @param {string} categoryId * @returns {Promise & { cancel(): void }} */ -export function RemovePendingNotification(identifier) { - let $resultPromise = /** @type {any} */($Call.ByID(3872412470, identifier)); +export function RemoveNotificationCategory(categoryId) { + let $resultPromise = /** @type {any} */($Call.ByID(229511469, categoryId)); return $resultPromise; } /** - * RequestUserNotificationAuthorization requests permission for notifications. + * RemovePendingNotification is a Windows stub that always returns nil. + * (macOS-specific) + * @param {string} $0 + * @returns {Promise & { cancel(): void }} + */ +export function RemovePendingNotification($0) { + let $resultPromise = /** @type {any} */($Call.ByID(3872412470, $0)); + return $resultPromise; +} + +/** + * RequestUserNotificationAuthorization is a Windows stub that always returns true, nil. + * (user authorization is macOS-specific) * @returns {Promise & { cancel(): void }} */ export function RequestUserNotificationAuthorization() { @@ -77,20 +93,21 @@ export function RequestUserNotificationAuthorization() { } /** - * SendNotification sends a notification with the given identifier, title, subtitle, and body. - * @param {string} identifier - * @param {string} title - * @param {string} subtitle - * @param {string} body + * SendNotification sends a basic notification with a name, title, and body. All other options are ignored on Windows. + * (subtitle, category id, and data are only available on macOS) + * @param {$models.NotificationOptions} options * @returns {Promise & { cancel(): void }} */ -export function SendNotification(identifier, title, subtitle, body) { - let $resultPromise = /** @type {any} */($Call.ByID(2246903123, identifier, title, subtitle, body)); +export function SendNotification(options) { + let $resultPromise = /** @type {any} */($Call.ByID(2246903123, options)); return $resultPromise; } /** - * SendNotificationWithActions sends a notification with the specified actions + * SendNotificationWithActions sends a notification with additional actions and inputs. + * A NotificationCategory must be registered with RegisterNotificationCategory first. The `CategoryID` must match the registered category. + * If a NotificationCategory is not registered a basic notification will be sent. + * (subtitle, category id, and data are only available on macOS) * @param {$models.NotificationOptions} options * @returns {Promise & { cancel(): void }} */ diff --git a/v3/examples/notifications/frontend/dist/Inter-Medium.ttf b/v3/examples/notifications/frontend/dist/Inter-Medium.ttf deleted file mode 100644 index a01f3777a..000000000 Binary files a/v3/examples/notifications/frontend/dist/Inter-Medium.ttf and /dev/null differ diff --git a/v3/examples/notifications/frontend/dist/assets/index-CS0sRFez.js b/v3/examples/notifications/frontend/dist/assets/index-CS0sRFez.js deleted file mode 100644 index 297ce49b9..000000000 --- a/v3/examples/notifications/frontend/dist/assets/index-CS0sRFez.js +++ /dev/null @@ -1,6 +0,0 @@ -(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 deleted file mode 100644 index 39216bd8d..000000000 --- a/v3/examples/notifications/frontend/dist/index.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - Wails App - - - -
- -

Wails + Javascript

- -
- - diff --git a/v3/examples/notifications/frontend/dist/javascript.svg b/v3/examples/notifications/frontend/dist/javascript.svg deleted file mode 100644 index f9abb2b72..000000000 --- a/v3/examples/notifications/frontend/dist/javascript.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/v3/examples/notifications/frontend/dist/style.css b/v3/examples/notifications/frontend/dist/style.css deleted file mode 100644 index 127799eda..000000000 --- a/v3/examples/notifications/frontend/dist/style.css +++ /dev/null @@ -1,160 +0,0 @@ -: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 deleted file mode 100644 index 8bdf42483..000000000 Binary files a/v3/examples/notifications/frontend/dist/wails.png and /dev/null differ diff --git a/v3/examples/notifications/frontend/main.js b/v3/examples/notifications/frontend/main.js index f032ac16b..9f1ac1d69 100644 --- a/v3/examples/notifications/frontend/main.js +++ b/v3/examples/notifications/frontend/main.js @@ -1,5 +1,5 @@ import * as Notifications from "./bindings/github.com/wailsapp/wails/v3/pkg/services/notifications/service"; -import { Events } from "@wailsio/runtime"; +import { Events, System } from "@wailsio/runtime"; const timeElement = document.getElementById('time'); const notificationsElement = document.getElementById('notifications'); @@ -7,8 +7,17 @@ const notificationsElement = document.getElementById('notifications'); window.sendNotification = async () => { const granted = await Notifications.RequestUserNotificationAuthorization(); if (granted) { - const uuid = crypto.randomUUID(); - await Notifications.SendNotification(uuid, "Frontend Notification", "", "Notification sent through JS!"); + const id = System.IsWindows() ? "Wails Notification Demo" : crypto.randomUUID() + await Notifications.SendNotification({ + id, + title: "Title", + body: "Body!", + data: { + messageId: "msg-123", + senderId: "user-123", + timestamp: Date.now(), + } + }); } } @@ -20,19 +29,18 @@ window.sendComplexNotification = async () => { 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...", }); - const uuid = crypto.randomUUID(); + const id = System.IsWindows() ? "Wails Notification Demo" : crypto.randomUUID() await Notifications.SendNotificationWithActions({ - id: uuid, + id, title: "Complex Frontend Notification", subtitle: "From: Jane Doe", - body: "Is it raining today where you are?", + body: "Is it rainging today where you are?", categoryId: "FRONTEND_NOTIF", data: { messageId: "msg-456", @@ -48,5 +56,6 @@ Events.On('time', (time) => { }); Events.On("notificationResponse", (response) => { + console.log(response) notificationsElement.innerText += JSON.stringify(response.data[0].data); }); \ No newline at end of file diff --git a/v3/examples/notifications/main.go b/v3/examples/notifications/main.go index 8c2e941df..d69bf3f93 100644 --- a/v3/examples/notifications/main.go +++ b/v3/examples/notifications/main.go @@ -9,18 +9,29 @@ import ( "time" "github.com/wailsapp/wails/v3/pkg/application" - "github.com/wailsapp/wails/v3/pkg/events" "github.com/wailsapp/wails/v3/pkg/services/notifications" ) +// Wails uses Go's `embed` package to embed the frontend files into the binary. +// Any files in the frontend/dist folder will be embedded into the binary and +// made available to the frontend. +// See https://pkg.go.dev/embed for more information. + //go:embed all:frontend/dist var assets embed.FS +// main function serves as the application's entry point. It initializes the application, creates a window, +// and starts a goroutine that emits a time-based event every second. It subsequently runs the application and +// logs any error that might occur. func main() { NotificationService := notifications.New() - + // Create a new Wails application by providing the necessary options. + // Variables 'Name' and 'Description' are for application metadata. + // 'Assets' configures the asset server with the 'FS' variable pointing to the frontend files. + // 'Bind' is a list of Go struct instances. The frontend has access to the methods of these instances. + // 'Mac' options tailor the application when running an macOS. app := application.New(application.Options{ - Name: "notifications", + Name: "notifications_demo", Description: "A demo of using raw HTML & CSS", Services: []application.Service{ application.NewService(NotificationService), @@ -33,6 +44,11 @@ func main() { }, }) + // Create a new window with the necessary options. + // 'Title' is the title of the window. + // 'Mac' options tailor the window when running on macOS. + // 'BackgroundColour' is the background colour of the window. + // 'URL' is the URL that will be loaded into the webview. app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "Window 1", Mac: application.MacWindow{ @@ -44,85 +60,52 @@ func main() { 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) + app.OnEvent("notificationResponse", func(event *application.CustomEvent) { + data, _ := json.Marshal(event.Data) + fmt.Printf("%s\n", string(data)) + }) - // Sometime later remove delivered notification by identifier - err = NotificationService.RemoveDeliveredNotification("some-uuid") - if err != nil { - log.Default().Printf("WARNING: %s\n", err) - } - }() + NotificationService.RequestUserNotificationAuthorization() + NotificationService.CheckNotificationAuthorization() - 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) - } + time.Sleep(time.Second * 2) + NotificationService.SendNotification(notifications.NotificationOptions{ + ID: "Wails Notification Demo", + Title: "Title!", + Body: "Body!", + Data: map[string]interface{}{ + "messageId": "msg-123", + "senderId": "user-123", + "timestamp": time.Now().String(), + }, + }) + + time.Sleep(time.Second * 2) + NotificationService.RegisterNotificationCategory(notifications.NotificationCategory{ + ID: "BACKEND_NOTIF", + 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, + ReplyButtonTitle: "Reply", + ReplyPlaceholder: "Reply to backend...", + }) + + NotificationService.SendNotificationWithActions(notifications.NotificationOptions{ + ID: "Wails Notification Demo", + Title: "Complex Backend Notification", + Subtitle: "Should not show on Windows", + Body: "Is it raining today where you are?", + CategoryID: "BACKEND_NOTIF", + Data: map[string]interface{}{ + "messageId": "msg-456", + "senderId": "user-456", + "timestamp": time.Now().String(), + }, + }) }() // Run the application. This blocks until the application has been exited.