diff options
Diffstat (limited to 'src/feeds.c')
-rw-r--r-- | src/feeds.c | 53 |
1 files changed, 44 insertions, 9 deletions
diff --git a/src/feeds.c b/src/feeds.c index 86f33367..3417b4e9 100644 --- a/src/feeds.c +++ b/src/feeds.c | |||
@@ -317,10 +317,10 @@ static iBool updateEntries_Feeds_(iFeeds *d, iBool isHeadings, uint32_t sourceId | |||
317 | /* Entries are removed from `incoming` if they are added to the Feeds entries array. | 317 | /* Entries are removed from `incoming` if they are added to the Feeds entries array. |
318 | Anything remaining in `incoming` will be deleted afterwards. */ | 318 | Anything remaining in `incoming` will be deleted afterwards. */ |
319 | iBool gotNew = iFalse; | 319 | iBool gotNew = iFalse; |
320 | lock_Mutex(d->mtx); | ||
321 | iTime now; | 320 | iTime now; |
322 | initCurrent_Time(&now); | 321 | initCurrent_Time(&now); |
323 | if (isHeadings) { | 322 | if (isHeadings) { |
323 | lock_Mutex(d->mtx); | ||
324 | // printf("Updating sourceID %d...\n", sourceId); | 324 | // printf("Updating sourceID %d...\n", sourceId); |
325 | iStringSet *known = listHeadingEntriesFrom_Feeds_(d, sourceId); | 325 | iStringSet *known = listHeadingEntriesFrom_Feeds_(d, sourceId); |
326 | // puts(" Known URLs:"); | 326 | // puts(" Known URLs:"); |
@@ -357,16 +357,22 @@ static iBool updateEntries_Feeds_(iFeeds *d, iBool isHeadings, uint32_t sourceId | |||
357 | // puts("Done."); | 357 | // puts("Done."); |
358 | iRelease(presentInSource); | 358 | iRelease(presentInSource); |
359 | iRelease(known); | 359 | iRelease(known); |
360 | unlock_Mutex(d->mtx); | ||
360 | } | 361 | } |
361 | else { | 362 | else { |
363 | /* All visited URLs still present in the source should be kept indefinitely so their | ||
364 | read status remains correct. The Kept flag will be cleared after the URL has been | ||
365 | discarded from the entry database and enough time has passed. */ { | ||
366 | // printf("updating entries from %d:\n", sourceId); | ||
367 | iForEach(PtrArray, i, incoming) { | ||
368 | const iFeedEntry *entry = i.ptr; | ||
369 | // printf("marking as kept: {%s}\n", cstr_String(&entry->url)); | ||
370 | setUrlKept_Visited(visited_App(), &entry->url, iTrue); | ||
371 | } | ||
372 | } | ||
373 | lock_Mutex(d->mtx); | ||
362 | iForEach(PtrArray, i, incoming) { | 374 | iForEach(PtrArray, i, incoming) { |
363 | iFeedEntry *entry = i.ptr; | 375 | iFeedEntry *entry = i.ptr; |
364 | /* Disregard old incoming entries. */ | ||
365 | if (secondsSince_Time(&now, &entry->posted) >= maxAge_Visited) { | ||
366 | /* We don't remember this far back, so the unread status of the entry would | ||
367 | be incorrect. */ | ||
368 | continue; | ||
369 | } | ||
370 | size_t pos; | 376 | size_t pos; |
371 | if (locate_SortedArray(&d->entries, &entry, &pos)) { | 377 | if (locate_SortedArray(&d->entries, &entry, &pos)) { |
372 | iFeedEntry *existing = *(iFeedEntry **) at_SortedArray(&d->entries, pos); | 378 | iFeedEntry *existing = *(iFeedEntry **) at_SortedArray(&d->entries, pos); |
@@ -384,7 +390,8 @@ static iBool updateEntries_Feeds_(iFeeds *d, iBool isHeadings, uint32_t sourceId | |||
384 | changed = iTrue; | 390 | changed = iTrue; |
385 | } | 391 | } |
386 | set_String(&existing->title, &entry->title); | 392 | set_String(&existing->title, &entry->title); |
387 | existing->posted = entry->posted; | 393 | existing->posted = entry->posted; |
394 | existing->discovered = entry->discovered; /* prevent discarding */ | ||
388 | delete_FeedEntry(entry); | 395 | delete_FeedEntry(entry); |
389 | if (changed) { | 396 | if (changed) { |
390 | /* TODO: better to use a new flag for read feed entries? */ | 397 | /* TODO: better to use a new flag for read feed entries? */ |
@@ -398,8 +405,8 @@ static iBool updateEntries_Feeds_(iFeeds *d, iBool isHeadings, uint32_t sourceId | |||
398 | } | 405 | } |
399 | remove_PtrArrayIterator(&i); | 406 | remove_PtrArrayIterator(&i); |
400 | } | 407 | } |
408 | unlock_Mutex(d->mtx); | ||
401 | } | 409 | } |
402 | unlock_Mutex(d->mtx); | ||
403 | return gotNew; | 410 | return gotNew; |
404 | } | 411 | } |
405 | 412 | ||
@@ -410,6 +417,8 @@ static iThreadResult fetch_Feeds_(iThread *thread) { | |||
410 | iZap(work); | 417 | iZap(work); |
411 | iBool gotNew = iFalse; | 418 | iBool gotNew = iFalse; |
412 | postCommand_App("feeds.update.started"); | 419 | postCommand_App("feeds.update.started"); |
420 | const int totalJobs = size_PtrArray(&d->jobs); | ||
421 | int numFinishedJobs = 0; | ||
413 | while (!d->stopWorker) { | 422 | while (!d->stopWorker) { |
414 | /* Start new jobs. */ | 423 | /* Start new jobs. */ |
415 | iForIndices(i, work) { | 424 | iForIndices(i, work) { |
@@ -420,6 +429,7 @@ static iThreadResult fetch_Feeds_(iThread *thread) { | |||
420 | sleep_Thread(0.5); /* TODO: wait on a Condition so we can exit quickly */ | 429 | sleep_Thread(0.5); /* TODO: wait on a Condition so we can exit quickly */ |
421 | if (d->stopWorker) break; | 430 | if (d->stopWorker) break; |
422 | size_t ongoing = 0; | 431 | size_t ongoing = 0; |
432 | iBool doNotify = iFalse; | ||
423 | iForIndices(i, work) { | 433 | iForIndices(i, work) { |
424 | if (work[i]) { | 434 | if (work[i]) { |
425 | if (isFinished_GmRequest(work[i]->request)) { | 435 | if (isFinished_GmRequest(work[i]->request)) { |
@@ -429,11 +439,15 @@ static iThreadResult fetch_Feeds_(iThread *thread) { | |||
429 | d, work[i]->checkHeadings, work[i]->bookmarkId, &work[i]->results); | 439 | d, work[i]->checkHeadings, work[i]->bookmarkId, &work[i]->results); |
430 | delete_FeedJob(work[i]); | 440 | delete_FeedJob(work[i]); |
431 | work[i] = NULL; | 441 | work[i] = NULL; |
442 | numFinishedJobs++; | ||
443 | doNotify = iTrue; | ||
432 | } | 444 | } |
433 | else if (isTimedOut_FeedJob_(work[i])) { | 445 | else if (isTimedOut_FeedJob_(work[i])) { |
434 | /* Maybe we'll get it next time! */ | 446 | /* Maybe we'll get it next time! */ |
435 | delete_FeedJob(work[i]); | 447 | delete_FeedJob(work[i]); |
436 | work[i] = NULL; | 448 | work[i] = NULL; |
449 | numFinishedJobs++; | ||
450 | doNotify = iTrue; | ||
437 | } | 451 | } |
438 | else { | 452 | else { |
439 | ongoing++; | 453 | ongoing++; |
@@ -441,6 +455,9 @@ static iThreadResult fetch_Feeds_(iThread *thread) { | |||
441 | /* TODO: abort job if it takes too long (> 15 seconds?) */ | 455 | /* TODO: abort job if it takes too long (> 15 seconds?) */ |
442 | } | 456 | } |
443 | } | 457 | } |
458 | if (doNotify) { | ||
459 | postCommandf_App("feeds.update.progress arg:%d total:%d", numFinishedJobs, totalJobs); | ||
460 | } | ||
444 | /* Stop if everything has finished. */ | 461 | /* Stop if everything has finished. */ |
445 | if (ongoing == 0 && isEmpty_PtrArray(&d->jobs)) { | 462 | if (ongoing == 0 && isEmpty_PtrArray(&d->jobs)) { |
446 | break; | 463 | break; |
@@ -448,6 +465,24 @@ static iThreadResult fetch_Feeds_(iThread *thread) { | |||
448 | } | 465 | } |
449 | initCurrent_Time(&d->lastRefreshedAt); | 466 | initCurrent_Time(&d->lastRefreshedAt); |
450 | save_Feeds_(d); | 467 | save_Feeds_(d); |
468 | /* Check if there are visited URLs marked as Kept that can be cleared because they are no | ||
469 | longer present in the database. */ { | ||
470 | iStringSet *knownEntryUrls = new_StringSet(); | ||
471 | lock_Mutex(d->mtx); | ||
472 | iConstForEach(Array, i, &d->entries.values) { | ||
473 | const iFeedEntry *entry = *(const iFeedEntry **) i.value; | ||
474 | insert_StringSet(knownEntryUrls, &entry->url); | ||
475 | } | ||
476 | unlock_Mutex(d->mtx); | ||
477 | iConstForEach(PtrArray, j, listKept_Visited(visited_App())) { | ||
478 | iVisitedUrl *visUrl = j.ptr; | ||
479 | if (!contains_StringSet(knownEntryUrls, &visUrl->url)) { | ||
480 | visUrl->flags &= ~kept_VisitedUrlFlag; | ||
481 | // printf("unkept: {%s}\n", cstr_String(&visUrl->url)); | ||
482 | } | ||
483 | } | ||
484 | iRelease(knownEntryUrls); | ||
485 | } | ||
451 | postCommandf_App("feeds.update.finished arg:%d unread:%zu", gotNew ? 1 : 0, | 486 | postCommandf_App("feeds.update.finished arg:%d unread:%zu", gotNew ? 1 : 0, |
452 | numUnread_Feeds()); | 487 | numUnread_Feeds()); |
453 | return 0; | 488 | return 0; |