1"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[5247],{8703:(e,i,t)=>{t.d(i,{W:()=>o});var r,n=t(32587);!function(e){e[e.Sunday=1]="Sunday",e[e.Monday=2]="Monday",e[e.Tuesday=3]="Tuesday",e[e.Wednesday=4]="Wednesday",e[e.Thursday=5]="Thursday",e[e.Friday=6]="Friday",e[e.Saturday=7]="Saturday"}(r||(r={}));let o=(0,n.F3)("LocalNotifications",{web:()=>t.e(2775).then(t.bind(t,22775)).then(e=>new e.LocalNotificationsWeb)})},75247:(e,i,t)=>{t.d(i,{notificationService:()=>u});var r=t(32587),n=t(59036),o=t(81494),a=t(8703);let s={enabled:!1,expirationAlerts:!0,lowStockAlerts:!0,weeklySummary:!0,quietHoursStart:"22:00",quietHoursEnd:"08:00"},c="vi-notifications";class l{async initialize(){if(this.initialized)return!0;try{return await this.loadPreferencesFromServer(),this.initialized=!0,!0}catch(e){return console.error("Failed to initialize notifications:",e instanceof Error?e.message:"Unknown error"),!1}}async initializeNative(){for(let e of this.listenerHandles)await e.remove().catch(()=>{});if(this.listenerHandles=[],"granted"!==(await o.h.requestPermissions()).receive)return!1;if(this.listenerHandles.push(await o.h.addListener("registration",e=>{this.deviceToken=e.value,this.sendTokenToServer(e.value)}),await o.h.addListener("registrationError",e=>{console.error("Push registration error:",e)}),await o.h.addListener("pushNotificationReceived",e=>{this.handleNotificationReceived(e)}),await o.h.addListener("pushNotificationActionPerformed",e=>{this.handleNotificationAction(e)})),await o.h.register(),await a.W.requestPermissions(),"android"===r.Ii.getPlatform())try{await a.W.createChannel({id:c,name:"Visual Inventory",description:"Expiration alerts, low stock warnings, and weekly summaries",importance:4,vibration:!0})}catch(e){}return!0}async initializeBrowser(){if(!("Notification"in window))return console.warn("This browser does not support notifications"),!1;if("default"===Notification.permission){if("granted"!==await Notification.requestPermission())return!1}else if("granted"!==Notification.permission)return!1;if(!("serviceWorker"in navigator)||!("PushManager"in window))return!1;let e="BJvZ0v4-1w8B1g4Fz9YwFOMwE_TvJN6kCbt6a8BEKj9uI7XjbajUWti_4BIswZ8ED1e9HGwEiH_hBw0St3nwUb4";if(!e)return console.warn("Push subscription failed: VAPID public key is not configured"),!1;try{let i=await navigator.serviceWorker.ready,t=await i.pushManager.getSubscription(),r=null!=t?t:await i.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:this.urlBase64ToUint8Array(e)});return await this.sendSubscriptionToServer(r),!0}catch(e){return console.warn("Push subscription failed:",e instanceof Error?e.message:"Unknown error"),!1}}async showNotification(e){this.preferences.enabled&&!this.isInQuietHours()&&(r.Ii.isNativePlatform()?await a.W.schedule({notifications:[{id:parseInt(e.id.replace(/\D/g,"").slice(0,9))||Date.now(),title:e.title,body:e.body,extra:e.data,channelId:c,schedule:e.schedule?{at:e.schedule}:void 0}]}):"Notification"in window&&"granted"===Notification.permission&&new Notification(e.title,{body:e.body,icon:"/icons/icon-192.png",badge:"/icons/icon-192.png",data:e.data,tag:e.id}))}async scheduleExpirationAlert(e,i,t){let r,n;this.preferences.expirationAlerts&&(t<=0?(r="\uD83D\uDEA8 Item Expired!",n="expired"):1===t?(r="â ï¸ Expires Tomorrow!",n="urgent"):t<=3?(r="â° Expiring Soon",n="soon"):(r="\uD83D\uDCC5 Upcoming Expiration",n="upcoming"),await this.showNotification({id:"exp-".concat(e,"-").concat(i.getTime()),title:r,body:"".concat(e," ").concat(t<=0?"has expired":"expires in ".concat(t," day").concat(t>1?"s":"")),data:{type:"expiration",itemName:e,urgency:n}}))}async showLowStockAlert(e,i,t){this.preferences.lowStockAlerts&&await this.showNotification({id:"low-stock-".concat(e,"-").concat(Date.now()),title:"\uD83D\uDCE6 Low Stock Alert",body:"Your ".concat(e," supplies cover ").concat(i," days (target: ").concat(t," days)"),data:{type:"low_stock",category:e}})}async showWeeklySummary(e){if(!this.preferences.weeklySummary)return;let i=e.readinessScore>=80?"\uD83D\uDFE2":e.readinessScore>=50?"\uD83D\uDFE1":"\uD83D\uDD34";await this.showNotification({id:"weekly-".concat(Date.now()),title:"\uD83D\uDCCA Weekly Pantry Intelligence Summary",body:"".concat(i," Score: ").concat(e.readinessScore,"% | ").concat(e.itemsExpiring," expiring | ").concat(e.itemsConsumed," consumed"),data:{type:"weekly_summary"}})}setPreferences(e){this.preferences={...this.preferences,...e}}getPreferences(){return{...this.preferences}}isSupported(){return!!r.Ii.isNativePlatform()||"Notification"in window&&"serviceWorker"
1in navigator&&"PushManager"in window}async checkPermission(){return r.Ii.isNativePlatform()?(await o.h.checkPermissions()).receive:"Notification"in window?Notification.permission:"denied"}async requestPermission(){return r.Ii.isNativePlatform()?"granted"===(await o.h.requestPermissions()).receive:"Notification"in window&&"granted"===await Notification.requestPermission()}async removeCurrentBrowserSubscription(){if(this.initialized=!1,r.Ii.isNativePlatform()||"undefined"==typeof navigator||!("serviceWorker"in navigator))return;let e=null;try{var i;let t=await navigator.serviceWorker.getRegistration();if(!(e=null!=(i=await (null==t?void 0:t.pushManager.getSubscription()))?i:null))return;try{let i=await (0,n.u9)("/api/notifications/subscribe",{method:"DELETE",headers:{"Content-Type":"application/json"},body:JSON.stringify({endpoint:e.endpoint})},5e3);i.ok||console.warn("Failed to remove push subscription from account:","HTTP ".concat(i.status))}catch(i){let e=i instanceof Error?i.message:"Unknown error";console.warn("Failed to remove push subscription from account:",e)}}catch(e){console.warn("Failed to read current push subscription:",e instanceof Error?e.message:"Unknown error")}finally{if(e)try{await e.unsubscribe()}catch(e){console.warn("Failed to unsubscribe browser push endpoint:",e instanceof Error?e.message:"Unknown error")}}}async enableOnThisDevice(){try{if(!(r.Ii.isNativePlatform()?await this.initializeNative():await this.initializeBrowser()))return!1;r.Ii.isNativePlatform()&&await this.enableServerConsent();let e=await this.checkPermission()==="granted";return e&&(this.initialized=!0,this.setPreferences({enabled:!0})),e}catch(e){return console.error("Failed to enable notifications:",e instanceof Error?e.message:"Unknown error"),!1}}async loadPreferencesFromServer(){this.preferences={...s};try{let e=await (0,n.u9)("/api/notifications/preferences",{method:"GET"},5e3);if(!e.ok)return;let i=await e.json();if(!i||"object"!=typeof i||Array.isArray(i))return;this.preferences={enabled:!0===i.enabled,expirationAlerts:!1!==i.expiration_alerts,lowStockAlerts:!1!==i.low_stock_alerts,weeklySummary:!1!==i.weekly_summary,quietHoursStart:"string"==typeof i.quiet_hours_start?i.quiet_hours_start:s.quietHoursStart,quietHoursEnd:"string"==typeof i.quiet_hours_end?i.quiet_hours_end:s.quietHoursEnd}}catch(e){}}async enableServerConsent(){let e=await (0,n.u9)("/api/notifications/preferences",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({enabled:!0})},5e3);if(!e.ok)throw Error("Could not save notification consent (HTTP ".concat(e.status,")"))}isInQuietHours(){if(!this.preferences.quietHoursStart||!this.preferences.quietHoursEnd)return!1;let e=new Date,i=60*e.getHours()+e.getMinutes(),[t,r]=this.preferences.quietHoursStart.split(":").map(Number),[n,o]=this.preferences.quietHoursEnd.split(":").map(Number),a=60*t+r,s=60*n+o;return a>s?i>=a||i<s:i>=a&&i<s}handleNotificationReceived(e){console.log("Notification received:",e)}handleNotificationAction(e){let i=e.notification.data,t=(null==i?void 0:i.type)==="expiration"?"inventory:expiring":(null==i?void 0:i.type)==="low_stock"?"shopping":null;t&&window.dispatchEvent(new CustomEvent("vi:navigate-tab",{detail:{tab:t}}))}async sendTokenToServer(e){try{await (0,n.u9)("/api/notifications/register",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:e,platform:r.Ii.getPlatform()})},15e3)}catch(e){console.error("Failed to register device token:",e instanceof Error?e.message:"Unknown error")}}async sendSubscriptionToServer(e){let i=await (0,n.u9)("/api/notifications/subscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e.toJSON())},15e3);if(!i.ok)throw Error("Could not register this device (HTTP ".concat(i.status,")"))}urlBase64ToUint8Array(e){let i="=".repeat((4-e.length%4)%4),t=(e+i).replace(/-/g,"+").replace(/_/g,"/"),r=window.atob(t),n=new Uint8Array(r.length);for(let e=0;e<r.length;++e)n[e]=r.charCodeAt(e);return n.buffer}constructor(){this.initialized=!1,this.deviceToken=null,this.preferences=s,this.listenerHandles=[]}}let u=new l},81494:(e,i,t)=>{t.d(i,{h:()=>r});let r=(0,t(32587).F3)("PushNotifications",{})}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.