summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-11-27 14:35:54 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-11-27 14:35:54 +0200
commit6e8924cb5e95466cbdbe6c237365a8383c35b26b (patch)
treed5ced984200555684bc1f3843b4de45ae9dca725 /src
parent54f063e859f8e7eb5c3a825763ed4c85d30ef5b5 (diff)
Feeds: Separate timestamps for posting and discovery
The discovery time is needed for knowing how long the entry can be retained in the history.
Diffstat (limited to 'src')
-rw-r--r--src/feeds.c34
-rw-r--r--src/feeds.h3
-rw-r--r--src/ui/sidebarwidget.c7
-rw-r--r--src/ui/window.c3
4 files changed, 30 insertions, 17 deletions
diff --git a/src/feeds.c b/src/feeds.c
index 26d8c04f..3340d201 100644
--- a/src/feeds.c
+++ b/src/feeds.c
@@ -43,7 +43,8 @@ iDeclareType(FeedJob)
43iDefineTypeConstruction(FeedEntry) 43iDefineTypeConstruction(FeedEntry)
44 44
45void init_FeedEntry(iFeedEntry *d) { 45void init_FeedEntry(iFeedEntry *d) {
46 iZap(d->timestamp); 46 iZap(d->posted);
47 iZap(d->discovered);
47 init_String(&d->url); 48 init_String(&d->url);
48 init_String(&d->title); 49 init_String(&d->title);
49 d->bookmarkId = 0; 50 d->bookmarkId = 0;
@@ -135,6 +136,8 @@ static void parseResult_FeedJob_(iFeedJob *d) {
135 /* TODO: Should tell the user if the request failed. */ 136 /* TODO: Should tell the user if the request failed. */
136 if (isSuccess_GmStatusCode(status_GmRequest(d->request))) { 137 if (isSuccess_GmStatusCode(status_GmRequest(d->request))) {
137 iBeginCollect(); 138 iBeginCollect();
139 iTime now;
140 initCurrent_Time(&now);
138 iRegExp *linkPattern = 141 iRegExp *linkPattern =
139 new_RegExp("^=>\\s*([^\\s]+)\\s+" 142 new_RegExp("^=>\\s*([^\\s]+)\\s+"
140 "([0-9][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9])" 143 "([0-9][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9])"
@@ -153,6 +156,7 @@ static void parseResult_FeedJob_(iFeedJob *d) {
153 const iRangecc date = capturedRange_RegExpMatch(&m, 2); 156 const iRangecc date = capturedRange_RegExpMatch(&m, 2);
154 const iRangecc title = capturedRange_RegExpMatch(&m, 3); 157 const iRangecc title = capturedRange_RegExpMatch(&m, 3);
155 iFeedEntry * entry = new_FeedEntry(); 158 iFeedEntry * entry = new_FeedEntry();
159 entry->discovered = now;
156 entry->bookmarkId = d->bookmarkId; 160 entry->bookmarkId = d->bookmarkId;
157 setRange_String(&entry->url, url); 161 setRange_String(&entry->url, url);
158 set_String(&entry->url, absoluteUrl_String(url_GmRequest(d->request), &entry->url)); 162 set_String(&entry->url, absoluteUrl_String(url_GmRequest(d->request), &entry->url));
@@ -161,7 +165,7 @@ static void parseResult_FeedJob_(iFeedJob *d) {
161 int year, month, day; 165 int year, month, day;
162 sscanf(date.start, "%04d-%02d-%02d", &year, &month, &day); 166 sscanf(date.start, "%04d-%02d-%02d", &year, &month, &day);
163 init_Time( 167 init_Time(
164 &entry->timestamp, 168 &entry->posted,
165 &(iDate){ 169 &(iDate){
166 .year = year, .month = month, .day = day, .hour = 12 /* noon UTC */ }); 170 .year = year, .month = month, .day = day, .hour = 12 /* noon UTC */ });
167 pushBack_PtrArray(&d->results, entry); 171 pushBack_PtrArray(&d->results, entry);
@@ -185,15 +189,15 @@ static iBool updateEntries_Feeds_(iFeeds *d, iPtrArray *incoming) {
185 iBool changed = iFalse; 189 iBool changed = iFalse;
186 iDate newDate; 190 iDate newDate;
187 iDate oldDate; 191 iDate oldDate;
188 init_Date(&newDate, &entry->timestamp); 192 init_Date(&newDate, &entry->posted);
189 init_Date(&oldDate, &existing->timestamp); 193 init_Date(&oldDate, &existing->posted);
190 if (!equalCase_String(&existing->title, &entry->title) || 194 if (!equalCase_String(&existing->title, &entry->title) ||
191 (newDate.year != oldDate.year || newDate.month != oldDate.month || 195 (newDate.year != oldDate.year || newDate.month != oldDate.month ||
192 newDate.day != oldDate.day)) { 196 newDate.day != oldDate.day)) {
193 changed = iTrue; 197 changed = iTrue;
194 } 198 }
195 set_String(&existing->title, &entry->title); 199 set_String(&existing->title, &entry->title);
196 existing->timestamp = entry->timestamp; 200 existing->posted = entry->posted;
197 delete_FeedEntry(entry); 201 delete_FeedEntry(entry);
198 if (changed) { 202 if (changed) {
199 /* TODO: better to use a new flag for read feed entries? */ 203 /* TODO: better to use a new flag for read feed entries? */
@@ -321,9 +325,10 @@ static void save_Feeds_(iFeeds *d) {
321 writeData_File(f, "# Entries\n", 10); 325 writeData_File(f, "# Entries\n", 10);
322 iConstForEach(Array, i, &d->entries.values) { 326 iConstForEach(Array, i, &d->entries.values) {
323 const iFeedEntry *entry = *(const iFeedEntry **) i.value; 327 const iFeedEntry *entry = *(const iFeedEntry **) i.value;
324 format_String(str, "%x\n%llu\n%s\n%s\n", 328 format_String(str, "%x\n%llu\n%llu\n%s\n%s\n",
325 entry->bookmarkId, 329 entry->bookmarkId,
326 integralSeconds_Time(&entry->timestamp), 330 integralSeconds_Time(&entry->posted),
331 integralSeconds_Time(&entry->discovered),
327 cstr_String(&entry->url), 332 cstr_String(&entry->url),
328 cstr_String(&entry->title)); 333 cstr_String(&entry->title));
329 write_File(f, utf8_String(str)); 334 write_File(f, utf8_String(str));
@@ -385,7 +390,11 @@ static void load_Feeds_(iFeeds *d) {
385 case 2: { 390 case 2: {
386 const uint32_t feedId = strtoul(line.start, NULL, 16); 391 const uint32_t feedId = strtoul(line.start, NULL, 16);
387 if (!nextSplit_Rangecc(range_Block(src), "\n", &line)) break; 392 if (!nextSplit_Rangecc(range_Block(src), "\n", &line)) break;
388 const unsigned long long ts = strtoull(line.start, NULL, 10); 393 const unsigned long long posted = strtoull(line.start, NULL, 10);
394 if (posted == 0) break;
395 if (!nextSplit_Rangecc(range_Block(src), "\n", &line)) break;
396 const unsigned long long discovered = strtoull(line.start, NULL, 10);
397 if (discovered == 0) break;
389 if (!nextSplit_Rangecc(range_Block(src), "\n", &line)) break; 398 if (!nextSplit_Rangecc(range_Block(src), "\n", &line)) break;
390 const iRangecc urlRange = line; 399 const iRangecc urlRange = line;
391 if (!nextSplit_Rangecc(range_Block(src), "\n", &line)) break; 400 if (!nextSplit_Rangecc(range_Block(src), "\n", &line)) break;
@@ -396,8 +405,9 @@ static void load_Feeds_(iFeeds *d) {
396 const iFeedHashNode *node = (iFeedHashNode *) value_Hash(feeds, feedId); 405 const iFeedHashNode *node = (iFeedHashNode *) value_Hash(feeds, feedId);
397 if (node) { 406 if (node) {
398 iFeedEntry *entry = new_FeedEntry(); 407 iFeedEntry *entry = new_FeedEntry();
399 entry->bookmarkId = node->bookmarkId; 408 entry->bookmarkId = node->bookmarkId;
400 entry->timestamp.ts.tv_sec = ts; 409 entry->posted.ts.tv_sec = posted;
410 entry->discovered.ts.tv_sec = discovered;
401 set_String(&entry->url, url); 411 set_String(&entry->url, url);
402 set_String(&entry->title, title); 412 set_String(&entry->title, title);
403 insert_SortedArray(&d->entries, &entry); 413 insert_SortedArray(&d->entries, &entry);
@@ -475,7 +485,7 @@ void removeEntries_Feeds(uint32_t feedBookmarkId) {
475 485
476static int cmpTimeDescending_FeedEntryPtr_(const void *a, const void *b) { 486static int cmpTimeDescending_FeedEntryPtr_(const void *a, const void *b) {
477 const iFeedEntry * const *e1 = a, * const *e2 = b; 487 const iFeedEntry * const *e1 = a, * const *e2 = b;
478 return -cmp_Time(&(*e1)->timestamp, &(*e2)->timestamp); 488 return -cmp_Time(&(*e1)->posted, &(*e2)->posted);
479} 489}
480 490
481const iPtrArray *listEntries_Feeds(void) { 491const iPtrArray *listEntries_Feeds(void) {
@@ -519,7 +529,7 @@ const iString *entryListPage_Feeds(void) {
519 iConstForEach(PtrArray, i, listEntries_Feeds()) { 529 iConstForEach(PtrArray, i, listEntries_Feeds()) {
520 const iFeedEntry *entry = i.ptr; 530 const iFeedEntry *entry = i.ptr;
521 iDate entryDate; 531 iDate entryDate;
522 init_Date(&entryDate, &entry->timestamp); 532 init_Date(&entryDate, &entry->posted);
523 if (on.year != entryDate.year || on.month != entryDate.month || on.day != entryDate.day) { 533 if (on.year != entryDate.year || on.month != entryDate.month || on.day != entryDate.day) {
524 appendFormat_String( 534 appendFormat_String(
525 src, "## %s\n", cstrCollect_String(format_Date(&entryDate, "%Y-%m-%d"))); 535 src, "## %s\n", cstrCollect_String(format_Date(&entryDate, "%Y-%m-%d")));
diff --git a/src/feeds.h b/src/feeds.h
index 5d1a47e0..caa2a6ef 100644
--- a/src/feeds.h
+++ b/src/feeds.h
@@ -28,7 +28,8 @@ iDeclareType(FeedEntry)
28iDeclareTypeConstruction(FeedEntry) 28iDeclareTypeConstruction(FeedEntry)
29 29
30struct Impl_FeedEntry { 30struct Impl_FeedEntry {
31 iTime timestamp; 31 iTime posted;
32 iTime discovered;
32 iString url; 33 iString url;
33 iString title; 34 iString title;
34 uint32_t bookmarkId; /* note: runtime only, not a persistent ID */ 35 uint32_t bookmarkId; /* note: runtime only, not a persistent ID */
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c
index 35c19f7c..8bbbbcd1 100644
--- a/src/ui/sidebarwidget.c
+++ b/src/ui/sidebarwidget.c
@@ -124,12 +124,12 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) {
124 iConstForEach(PtrArray, i, listEntries_Feeds()) { 124 iConstForEach(PtrArray, i, listEntries_Feeds()) {
125 const iFeedEntry *entry = i.ptr; 125 const iFeedEntry *entry = i.ptr;
126 /* Exclude entries that are too old for Visited to keep track of. */ 126 /* Exclude entries that are too old for Visited to keep track of. */
127 if (secondsSince_Time(&now, &entry->timestamp) > maxAge_Visited) { 127 if (secondsSince_Time(&now, &entry->discovered) > maxAge_Visited) {
128 break; /* the rest are even older */ 128 break; /* the rest are even older */
129 } 129 }
130 /* Insert date separators. */ { 130 /* Insert date separators. */ {
131 iDate entryDate; 131 iDate entryDate;
132 init_Date(&entryDate, &entry->timestamp); 132 init_Date(&entryDate, &entry->posted);
133 if (on.year != entryDate.year || on.month != entryDate.month || 133 if (on.year != entryDate.year || on.month != entryDate.month ||
134 on.day != entryDate.day) { 134 on.day != entryDate.day) {
135 on = entryDate; 135 on = entryDate;
@@ -444,6 +444,7 @@ void init_SidebarWidget(iSidebarWidget *d) {
444 setBackgroundColor_Widget(d->resizer, none_ColorId); 444 setBackgroundColor_Widget(d->resizer, none_ColorId);
445 d->resizeCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE); 445 d->resizeCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);
446 d->menu = NULL; 446 d->menu = NULL;
447 addAction_Widget(w, SDLK_r, KMOD_PRIMARY | KMOD_SHIFT, "feeds.refresh");
447} 448}
448 449
449void deinit_SidebarWidget(iSidebarWidget *d) { 450void deinit_SidebarWidget(iSidebarWidget *d) {
@@ -925,7 +926,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev)
925 d->contextItem = hoverItem_ListWidget(d->list); 926 d->contextItem = hoverItem_ListWidget(d->list);
926 /* Update menu items. */ 927 /* Update menu items. */
927 /* TODO: Some callback-based mechanism would be nice for updating menus right 928 /* TODO: Some callback-based mechanism would be nice for updating menus right
928 before they open? */ 929 before they open? */
929 if (d->mode == bookmarks_SidebarMode && d->contextItem) { 930 if (d->mode == bookmarks_SidebarMode && d->contextItem) {
930 const iBookmark *bm = get_Bookmarks(bookmarks_App(), d->contextItem->id); 931 const iBookmark *bm = get_Bookmarks(bookmarks_App(), d->contextItem->id);
931 if (bm) { 932 if (bm) {
diff --git a/src/ui/window.c b/src/ui/window.c
index 6e91d644..cdb122be 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -104,7 +104,8 @@ static const iMenuItem navMenuItems_[] = {
104 { "Save to Downloads", SDLK_s, KMOD_PRIMARY, "document.save" }, 104 { "Save to Downloads", SDLK_s, KMOD_PRIMARY, "document.save" },
105 { "---", 0, 0, NULL }, 105 { "---", 0, 0, NULL },
106 { "Copy Source Text", SDLK_c, KMOD_PRIMARY, "copy" }, 106 { "Copy Source Text", SDLK_c, KMOD_PRIMARY, "copy" },
107 { "Bookmark This Page", SDLK_d, KMOD_PRIMARY, "bookmark.add" }, 107 { "Bookmark Page", SDLK_d, KMOD_PRIMARY, "bookmark.add" },
108 { "Subscribe to Page", 0, 0, "bookmark.addtag tag:subscribed" },
108 { "---", 0, 0, NULL }, 109 { "---", 0, 0, NULL },
109 { "Toggle Sidebar", SDLK_l, KMOD_PRIMARY | KMOD_SHIFT, "sidebar.toggle" }, 110 { "Toggle Sidebar", SDLK_l, KMOD_PRIMARY | KMOD_SHIFT, "sidebar.toggle" },
110 { "Zoom In", SDLK_EQUALS, KMOD_PRIMARY, "zoom.delta arg:10" }, 111 { "Zoom In", SDLK_EQUALS, KMOD_PRIMARY, "zoom.delta arg:10" },