diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-11-27 14:35:54 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-11-27 14:35:54 +0200 |
commit | 6e8924cb5e95466cbdbe6c237365a8383c35b26b (patch) | |
tree | d5ced984200555684bc1f3843b4de45ae9dca725 /src | |
parent | 54f063e859f8e7eb5c3a825763ed4c85d30ef5b5 (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.c | 34 | ||||
-rw-r--r-- | src/feeds.h | 3 | ||||
-rw-r--r-- | src/ui/sidebarwidget.c | 7 | ||||
-rw-r--r-- | src/ui/window.c | 3 |
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) | |||
43 | iDefineTypeConstruction(FeedEntry) | 43 | iDefineTypeConstruction(FeedEntry) |
44 | 44 | ||
45 | void init_FeedEntry(iFeedEntry *d) { | 45 | void 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 | ||
476 | static int cmpTimeDescending_FeedEntryPtr_(const void *a, const void *b) { | 486 | static 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 | ||
481 | const iPtrArray *listEntries_Feeds(void) { | 491 | const 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) | |||
28 | iDeclareTypeConstruction(FeedEntry) | 28 | iDeclareTypeConstruction(FeedEntry) |
29 | 29 | ||
30 | struct Impl_FeedEntry { | 30 | struct 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 | ||
449 | void deinit_SidebarWidget(iSidebarWidget *d) { | 450 | void 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" }, |