@@ -174,17 +174,19 @@ browser.tabs.onRemoved.addListener(function(tabId){
174174} ) ;
175175
176176/* Listener for adding or removing feeds on the feedly website */
177- browser . webRequest . onCompleted . addListener ( function ( details ) {
177+ browser . webRequest . onCompleted . addListener ( async function ( details ) {
178178 if ( details . method === "POST" || details . method === "DELETE" ) {
179+ await ensureOptionsLoaded ( ) ;
179180 updateCounter ( ) ;
180181 updateFeeds ( ) ;
181182 appGlobal . getUserSubscriptionsPromise = null ;
182183 }
183184} , { urls : [ "*://*.feedly.com/v3/subscriptions*" , "*://*.feedly.com/v3/markers?*ct=feedly.desktop*" ] } ) ;
184185
185186/* Listener for adding or removing saved feeds */
186- browser . webRequest . onCompleted . addListener ( function ( details ) {
187+ browser . webRequest . onCompleted . addListener ( async function ( details ) {
187188 if ( details . method === "PUT" || details . method === "DELETE" ) {
189+ await ensureOptionsLoaded ( ) ;
188190 updateSavedFeeds ( ) ;
189191 }
190192} , { urls : [ "*://*.feedly.com/v3/tags*global.saved*" ] } ) ;
@@ -201,7 +203,7 @@ browser.action.onClicked.addListener(async function () {
201203} ) ;
202204
203205/* Initialization all parameters and run feeds check */
204- async function initialize ( ) {
206+ async function initialize ( immediate ) {
205207 if ( appGlobal . options . openSiteOnIconClick ) {
206208 await browser . action . setPopup ( { popup : "" } ) ;
207209 } else {
@@ -211,18 +213,29 @@ async function initialize() {
211213
212214 const platformInfo = await browser . runtime . getPlatformInfo ( ) ;
213215 appGlobal . environment . os = platformInfo . os ;
214- startSchedule ( appGlobal . options . updateInterval ) ;
216+ startSchedule ( appGlobal . options . updateInterval , immediate ) ;
215217}
216218
217- function startSchedule ( updateInterval ) {
218- stopSchedule ( ) ;
219- updateCounter ( ) ;
220- updateFeeds ( ) ;
221- if ( appGlobal . options . showCounter ) {
222- browser . alarms . create ( "updateCounter" , { periodInMinutes : updateInterval } ) ;
223- }
224- if ( appGlobal . options . showDesktopNotifications || appGlobal . options . playSound || ! appGlobal . options . openSiteOnIconClick ) {
225- browser . alarms . create ( "updateFeeds" , { periodInMinutes : updateInterval } ) ;
219+ async function ensureOptionsLoaded ( ) {
220+ await readOptions ( ) ;
221+ appGlobal . feedlyApiClient . accessToken = appGlobal . options . accessToken ;
222+ appGlobal . isLoggedIn = Boolean ( appGlobal . options . accessToken ) ;
223+ }
224+
225+ function startSchedule ( updateInterval , immediate ) {
226+ const shouldRecreate = ( immediate !== false ) ;
227+
228+ // When shouldRecreate is false (worker wake), keep existing alarms as-is and avoid immediate fetch.
229+ if ( shouldRecreate ) {
230+ stopSchedule ( ) ;
231+ if ( appGlobal . options . showCounter ) {
232+ browser . alarms . create ( "updateCounter" , { periodInMinutes : updateInterval } ) ;
233+ }
234+ if ( appGlobal . options . showDesktopNotifications || appGlobal . options . playSound || ! appGlobal . options . openSiteOnIconClick ) {
235+ browser . alarms . create ( "updateFeeds" , { periodInMinutes : updateInterval } ) ;
236+ }
237+ updateCounter ( ) ;
238+ updateFeeds ( ) ;
226239 }
227240}
228241
@@ -231,7 +244,10 @@ function stopSchedule() {
231244 browser . alarms . clear ( "updateFeeds" ) ;
232245}
233246
234- browser . alarms . onAlarm . addListener ( function ( alarm ) {
247+ browser . alarms . onAlarm . addListener ( async function ( alarm ) {
248+ // Ensure tokens/options are loaded when worker wakes for an alarm
249+ await ensureOptionsLoaded ( ) ;
250+
235251 if ( alarm && alarm . name === "updateCounter" ) {
236252 updateCounter ( ) ;
237253 } else if ( alarm && alarm . name === "updateFeeds" ) {
@@ -297,15 +313,16 @@ async function sendDesktopNotification(feeds) {
297313 let showBlogIcons = false ;
298314 let showThumbnails = false ;
299315
300- try {
301- const hasAllOrigins = await browser . permissions . contains ( { origins : [ "<all_urls>" ] } ) ;
302- if ( appGlobal . options . showBlogIconInNotifications && hasAllOrigins ) {
303- showBlogIcons = true ;
304- }
305- if ( appGlobal . options . showThumbnailInNotifications && hasAllOrigins ) {
306- showThumbnails = true ;
307- }
308- } catch ( _ ) { /* ignore */ }
316+ const canCheckPermissions = browser . permissions && typeof browser . permissions . contains === "function" ;
317+ const hasAllOrigins = canCheckPermissions
318+ ? await browser . permissions . contains ( { origins : [ "<all_urls>" ] } ) . catch ( function ( ) { return false ; } )
319+ : false ;
320+ if ( appGlobal . options . showBlogIconInNotifications && hasAllOrigins ) {
321+ showBlogIcons = true ;
322+ }
323+ if ( appGlobal . options . showThumbnailInNotifications && hasAllOrigins ) {
324+ showThumbnails = true ;
325+ }
309326
310327 createNotifications ( feeds , showBlogIcons , showThumbnails , isSoundEnabled ) ;
311328 }
@@ -388,13 +405,12 @@ function removeFeedFromCache(feedId) {
388405
389406/* Plays alert sound */
390407function playSound ( ) {
391- try {
392- var audio = new Audio ( appGlobal . options . sound ) ;
393- audio . volume = appGlobal . options . soundVolume ;
394- audio . play ( ) ;
395- } catch ( e ) {
396- // In MV3 service worker there is no Audio context; ignore.
408+ if ( typeof Audio !== "function" ) {
409+ return ;
397410 }
411+ var audio = new Audio ( appGlobal . options . sound ) ;
412+ audio . volume = appGlobal . options . soundVolume ;
413+ Promise . resolve ( audio . play ( ) ) . catch ( function ( ) { } ) ;
398414}
399415
400416/* Returns only new feeds and set date of last feed
@@ -439,6 +455,7 @@ async function updateSavedFeeds() {
439455 const response = await apiRequestWrapper ( "streams/" + encodeURIComponent ( appGlobal . savedGroup ) + "/contents" ) ;
440456 const feeds = await parseFeeds ( response ) ;
441457 appGlobal . cachedSavedFeeds = feeds ;
458+ browser . storage . local . set ( { cachedSavedFeeds : feeds } ) . catch ( function ( ) { } ) ;
442459}
443460
444461/* Sets badge counter if unread feeds more than zero */
@@ -534,7 +551,7 @@ async function makeMarkersRequest(parameters){
534551 * If silentUpdate is true, then notifications will not be shown
535552 * */
536553async function updateFeeds ( silentUpdate ) {
537- appGlobal . cachedFeeds = [ ] ;
554+ const previousCache = appGlobal . cachedFeeds . slice ( 0 ) ;
538555 appGlobal . options . filters = appGlobal . options . filters || [ ] ;
539556
540557 let streamIds = appGlobal . options . isFiltersEnabled && appGlobal . options . filters . length
@@ -558,12 +575,13 @@ async function updateFeeds(silentUpdate) {
558575 const responses = await Promise . all ( promises ) ;
559576 const parsedFeeds = await Promise . all ( responses . map ( response => parseFeeds ( response ) ) ) ;
560577
578+ let newCache = [ ] ;
561579 for ( let parsedFeed of parsedFeeds ) {
562- appGlobal . cachedFeeds = appGlobal . cachedFeeds . concat ( parsedFeed ) ;
580+ newCache = newCache . concat ( parsedFeed ) ;
563581 }
564582
565583 // Remove duplicates
566- appGlobal . cachedFeeds = appGlobal . cachedFeeds . filter ( function ( value , index , feeds ) {
584+ newCache = newCache . filter ( function ( value , index , feeds ) {
567585 for ( let i = ++ index ; i < feeds . length ; i ++ ) {
568586 if ( feeds [ i ] . id === value . id ) {
569587 return false ;
@@ -572,7 +590,7 @@ async function updateFeeds(silentUpdate) {
572590 return true ;
573591 } ) ;
574592
575- appGlobal . cachedFeeds = appGlobal . cachedFeeds . sort ( function ( a , b ) {
593+ newCache = newCache . sort ( function ( a , b ) {
576594 if ( appGlobal . options . sortBy === "newest" ) {
577595 if ( a . date > b . date ) {
578596 return - 1 ;
@@ -602,12 +620,16 @@ async function updateFeeds(silentUpdate) {
602620 }
603621 } ) ;
604622
605- appGlobal . cachedFeeds = appGlobal . cachedFeeds . splice ( 0 , appGlobal . options . maxNumberOfFeeds ) ;
623+ newCache = newCache . splice ( 0 , appGlobal . options . maxNumberOfFeeds ) ;
624+ appGlobal . cachedFeeds = newCache ;
625+ browser . storage . local . set ( { cachedFeeds : newCache } ) . catch ( function ( ) { } ) ;
606626 if ( ! silentUpdate && ( appGlobal . options . showDesktopNotifications ) ) {
607627 const newFeeds = await filterByNewFeeds ( appGlobal . cachedFeeds ) ;
608628 await sendDesktopNotification ( newFeeds ) ;
609629 }
610630 } catch ( e ) {
631+ // Preserve previous cache on failure
632+ appGlobal . cachedFeeds = previousCache ;
611633 console . info ( "Unable to update feeds." , e ) ;
612634 }
613635}
@@ -904,7 +926,9 @@ async function getAccessToken() {
904926 let codeParse = / c o d e = ( .+ ?) (?: & | $ ) / i;
905927 let matches = codeParse . exec ( information . url ) ;
906928 if ( matches ) {
907- try { browser . tabs . onUpdated . removeListener ( processCode ) ; } catch ( _ ) { }
929+ if ( browser . tabs && browser . tabs . onUpdated && typeof browser . tabs . onUpdated . removeListener === "function" ) {
930+ browser . tabs . onUpdated . removeListener ( processCode ) ;
931+ }
908932 resolve ( matches [ 1 ] ) ;
909933 }
910934 }
@@ -1000,6 +1024,14 @@ async function readOptions() {
10001024 }
10011025
10021026 appGlobal . options . currentUiLanguage = browser . i18n . getUILanguage ( ) ;
1027+
1028+ // Preload cached feeds from local storage to serve immediately on popup open
1029+ const cache = await browser . storage . local . get ( [ "cachedFeeds" , "cachedSavedFeeds" ] ) . catch ( function ( ) { return { } ; } ) ;
1030+ appGlobal . cachedFeeds = Array . isArray ( cache . cachedFeeds ) ? cache . cachedFeeds : [ ] ;
1031+ appGlobal . cachedSavedFeeds = Array . isArray ( cache . cachedSavedFeeds ) ? cache . cachedSavedFeeds : [ ] ;
1032+
1033+ // If we have a token, treat as logged in until a request proves otherwise
1034+ appGlobal . isLoggedIn = Boolean ( appGlobal . options . accessToken ) ;
10031035}
10041036
10051037async function apiRequestWrapper ( methodName , settings ) {
0 commit comments