Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new lastread filter #1091

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion controllers/Items.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,17 @@ public function mark(Base $f3, array $params) {
$lastid = $_POST['ids'];
}

$skipped = array_key_exists('skipped', $_POST) && $_POST['skipped'] == 'true';

$itemDao = new \daos\Items();

// validate id or ids
if (!$itemDao->isValid('id', $lastid)) {
\F3::get('logger')->debug('invalid id: ' . var_export($lastid, true));
$this->view->error('invalid id');
}

$itemDao->mark($lastid);
$itemDao->mark($lastid, $skipped);

$return = [
'success' => true
Expand Down Expand Up @@ -238,8 +241,10 @@ public function sync() {
$sync['newItems'][] = [
'id' => $newItem['id'],
'datetime' => \helpers\ViewHelper::date_iso8601($newItem['datetime']),
'updatetime' => \helpers\ViewHelper::date_iso8601($newItem['updatetime']),
'unread' => $newItem['unread'],
'starred' => $newItem['starred'],
'skipped' => $newItem['skipped'],
'html' => $this->view->render('templates/item.phtml'),
'source' => $newItem['source'],
'tags' => array_keys($newItem['tags'])
Expand Down
6 changes: 6 additions & 0 deletions daos/mysql/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,12 @@ public function __construct() {
'INSERT INTO ' . \F3::get('db_prefix') . 'version (version) VALUES (12)'
]);
}
if (strnatcmp($version, '13') < 0) {
\F3::get('db')->exec([
'ALTER TABLE ' . \F3::get('db_prefix') . 'items ADD skipped BOOL NOT NULL DEFAULT 0;',
'INSERT INTO ' . \F3::get('db_prefix') . 'version (version) VALUES (13)'
]);
}
}

// just initialize once
Expand Down
62 changes: 45 additions & 17 deletions daos/mysql/Items.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,31 @@ class Items extends Database {
*
* @return void
*/
public function mark($id) {
if ($this->isValid('id', $id) === false) {
public function mark($id, $skipped=false) {
if (is_array($id)) {
$in_ids = $this->stmt->intRowMatches('id', $id);
foreach($id as $i) {
\F3::get('logger')->debug('statusUpdate: ' . $i . ' marked as read');
}
} else {
$intId = (int) $id;
if ($intId > 0) {
$in_ids = 'id = ' . $intId;
\F3::get('logger')->debug('statusUpdate: ' . $intId . ' marked as read');
} else {
$in_ids = null;
}
}
if (is_null($in_ids)) {
return;
}

if (is_array($id)) {
$id = implode(',', $id);
$update = array($this->stmt->isFalse('unread'));
if ($skipped) {
$update[] = $this->stmt->isTrue('skipped');
}

// i used string concatenation after validating $id
\F3::get('db')->exec('UPDATE ' . \F3::get('db_prefix') . "items SET unread=? WHERE id IN ($id)", false);
\F3::get('db')->exec('UPDATE ' . \F3::get('db_prefix') . 'items SET ' . implode(',', $update) . ' WHERE ' . $in_ids . ';');
}

/**
Expand Down Expand Up @@ -215,6 +229,7 @@ public function get($options = []) {
$params = [];
$where = [$this->stmt->bool(true)];
$order = 'DESC';
$orderDatetime = 'items.datetime';

// only starred
if (isset($options['type']) && $options['type'] === 'starred') {
Expand All @@ -229,6 +244,14 @@ public function get($options = []) {
}
}

// recently read
elseif (isset($options['type']) && $options['type'] == 'lastread') {
$orderDatetime = 'items.updatetime';
$where[] = $this->stmt->isFalse('unread');
$where[] = $this->stmt->isFalse('skipped');
$order = 'DESC';
}

// search
if (isset($options['search']) && strlen($options['search']) > 0) {
$search = implode('%', \helpers\Search::splitTerms($options['search']));
Expand Down Expand Up @@ -282,8 +305,8 @@ public function get($options = []) {

// Because of sqlite lack of tuple comparison support, we use a
// more complicated condition.
$where[] = "(items.datetime $ltgt :offset_from_datetime OR
(items.datetime = :offset_from_datetime2 AND
$where[] = "($orderDatetime $ltgt :offset_from_datetime OR
($orderDatetime = :offset_from_datetime2 AND
items.id $ltgt :offset_from_id)
)";
}
Expand Down Expand Up @@ -325,7 +348,7 @@ public function get($options = []) {
items.id, datetime, items.title AS title, content, unread, starred, source, thumbnail, icon, uid, link, updatetime, author, sources.title as sourcetitle, sources.tags as tags
FROM ' . \F3::get('db_prefix') . 'items AS items, ' . \F3::get('db_prefix') . 'sources AS sources
WHERE items.source=sources.id AND';
$order_sql = 'ORDER BY items.datetime ' . $order . ', items.id ' . $order;
$order_sql = 'ORDER BY ' . $orderDatetime . ' ' . $order . ', items.id ' . $order;

if ($where_ids !== '') {
// This UNION is required for the extra explicitely requested items
Expand Down Expand Up @@ -377,7 +400,7 @@ public function hasMore() {
*/
public function sync($sinceId, DateTime $notBefore, DateTime $since, $howMany) {
$query = 'SELECT
items.id, datetime, items.title AS title, content, unread, starred, source, thumbnail, icon, uid, link, updatetime, author, sources.title as sourcetitle, sources.tags as tags
items.id, datetime, items.title AS title, content, unread, starred, skipped, source, thumbnail, icon, uid, link, updatetime, author, sources.title as sourcetitle, sources.tags as tags
FROM ' . \F3::get('db_prefix') . 'items AS items, ' . \F3::get('db_prefix') . 'sources AS sources
WHERE items.source=sources.id
AND (' . $this->stmt->isTrue('unread') .
Expand All @@ -399,6 +422,7 @@ public function sync($sinceId, DateTime $notBefore, DateTime $since, $howMany) {
'id' => \daos\PARAM_INT,
'unread' => \daos\PARAM_BOOL,
'starred' => \daos\PARAM_BOOL,
'skipped' => \daos\PARAM_BOOL,
'source' => \daos\PARAM_INT
]);
}
Expand Down Expand Up @@ -617,15 +641,17 @@ public function lastUpdate() {
*
* @return array of unread, starred, etc. status of specified items
*/
public function statuses(DateTime $since) {
$res = \F3::get('db')->exec('SELECT id, unread, starred
public function statuses(Datetime $since) {
$res = \F3::get('db')->exec('SELECT id, unread, starred, skipped,
updatetime
FROM ' . \F3::get('db_prefix') . 'items
WHERE ' . \F3::get('db_prefix') . 'items.updatetime > :since;',
[':since' => [$since->format(DateTime::ATOM), \PDO::PARAM_STR]]);
$res = $this->stmt->ensureRowTypes($res, [
'id' => \daos\PARAM_INT,
'unread' => \daos\PARAM_BOOL,
'starred' => \daos\PARAM_BOOL
'starred' => \daos\PARAM_BOOL,
'skipped' => \daos\PARAM_BOOL
]);

return $res;
Expand All @@ -647,7 +673,7 @@ public function bulkStatusUpdate(array $statuses) {
$statusUpdate = null;

// sanitize statuses
foreach (['unread', 'starred'] as $sk) {
foreach (['unread', 'starred', 'skipped'] as $sk) {
if (array_key_exists($sk, $status)) {
if ($status[$sk] == 'true') {
$statusUpdate = [
Expand Down Expand Up @@ -700,12 +726,14 @@ public function bulkStatusUpdate(array $statuses) {
foreach ($sql as $id => $q) {
$params = [
':id' => [$id, \PDO::PARAM_INT],
':statusUpdate' => [$q['datetime'], \PDO::PARAM_STR]
':statusUpdate' => [$q['datetime'], \PDO::PARAM_STR],
':statusUpdate2' => [$q['datetime'], \PDO::PARAM_STR]
];
$updated = \F3::get('db')->exec(
'UPDATE ' . \F3::get('db_prefix') . 'items
SET ' . implode(', ', array_values($q['updates'])) . '
WHERE id = :id AND updatetime < :statusUpdate', $params);
SET ' . implode(', ', array_values($q['updates'])) . ',
updatetime=:statusUpdate
WHERE id = :id AND updatetime < :statusUpdate2', $params);
if ($updated == 0) {
// entry status was updated in between so updatetime must
// be updated to ensure client side consistency of
Expand Down
15 changes: 15 additions & 0 deletions daos/pgsql/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,21 @@ public function __construct() {
'INSERT INTO version (version) VALUES (12)'
]);
}
if (strnatcmp($version, '13') < 0) {
\F3::get('db')->exec([
'ALTER TABLE items ADD skipped BOOLEAN NOT NULL DEFAULT FALSE;',
'CREATE TABLE item_resources (
id SERIAL PRIMARY KEY,
item_id INTEGER REFERENCES items ON DELETE CASCADE,
hash TEXT NOT NULL,
url TEXT NOT NULL
);',
'CREATE UNIQUE INDEX uid_idx ON items USING btree (source, uid)',
'CREATE INDEX datetime_idx ON items USING btree (datetime)',
'CREATE INDEX updatetime_idx ON items USING btree (updatetime)',
'INSERT INTO version (version) VALUES (13)'
]);
}
}

// just initialize once
Expand Down
6 changes: 6 additions & 0 deletions daos/sqlite/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,12 @@ public function __construct() {
'INSERT INTO version (version) VALUES (11)'
]);
}
if (strnatcmp($version, '13') < 0) {
\F3::get('db')->exec([
'ALTER TABLE items ADD skipped BOOL NOT NULL DEFAULT 0;',
'INSERT INTO version (version) VALUES (13)'
]);
}
}

// just initialize once
Expand Down
5 changes: 3 additions & 2 deletions public/js/selfoss-base.js
Original file line number Diff line number Diff line change
Expand Up @@ -535,15 +535,16 @@ var selfoss = {

if (selfoss.db.storage) {
selfoss.refreshUnread(unreadstats);
selfoss.dbOffline.entriesMark(ids, false).then(displayNextUnread);
selfoss.dbOffline.entriesMark(ids, false, true).then(displayNextUnread);
}

$.ajax({
url: $('base').attr('href') + 'mark',
type: 'POST',
dataType: 'json',
data: {
ids: ids
ids: ids,
skipped: true
},
success: function() {
selfoss.db.setOnline();
Expand Down
45 changes: 32 additions & 13 deletions public/js/selfoss-db.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ selfoss.dbOnline = {
var maxId = 0;
data.newItems.forEach(function(item) {
item.datetime = new Date(item.datetime);
item.updatetime = new Date(item.updatetime);
maxId = Math.max(item.id, maxId);
});

Expand Down Expand Up @@ -350,7 +351,7 @@ selfoss.dbOffline = {

selfoss.db.storage = new Dexie('selfoss');
selfoss.db.storage.version(1).stores({
entries: '&id,*datetime,[datetime+id]',
entries: '&id,*datetime,[datetime+id],*updatetime,[updatetime+id]',
statusq: '++id,*entryId',
stamps: '&name,datetime',
stats: '&name',
Expand Down Expand Up @@ -544,7 +545,13 @@ selfoss.dbOffline = {
var hasMore = false;

var ascOrder = selfoss.db.ascOrder();
var entries = selfoss.db.storage.entries.orderBy('[datetime+id]');
var orderDatetime = 'datetime';
if (selfoss.filter.type == 'lastread') {
orderDatetime = 'updatetime';
}
var entries = selfoss.db.storage.entries.orderBy(
'[' + orderDatetime + '+id]'
);
if (!ascOrder) {
entries = entries.reverse();
}
Expand All @@ -569,6 +576,8 @@ selfoss.dbOffline = {
return entry.starred;
} else if (selfoss.filter.type == 'unread') {
return entry.unread;
} else if (selfoss.filter.type == 'lastread') {
return !entry.unread && !entry.skipped;
}

return true;
Expand All @@ -582,13 +591,18 @@ selfoss.dbOffline = {
// seek pagination
isMore = !seek;
if (seek) {
var entryOrderDatetime = entry.datetime;
if (selfoss.filter.type == 'lastread') {
entryOrderDatetime = entry.updatetime;
}

if (ascOrder) {
isMore = entry.datetime > fromDatetime
|| (entry.datetime.getTime() == fromDatetime.getTime()
isMore = entryOrderDatetime > fromDatetime
|| (entryOrderDatetime.getTime() == fromDatetime.getTime()
&& entry.id > fromId);
} else {
isMore = entry.datetime < fromDatetime
|| (entry.datetime.getTime() == fromDatetime.getTime()
isMore = entryOrderDatetime < fromDatetime
|| (entryOrderDatetime.getTime() == fromDatetime.getTime()
&& entry.id < fromId);
}
}
Expand Down Expand Up @@ -741,7 +755,9 @@ selfoss.dbOffline = {

// update entries statuses
itemStatuses.forEach(function(itemStatus) {
var newStatus = {};
var newStatus = {
updatetime: new Date(itemStatus.updatetime)
};

selfoss.db.entryStatusNames.forEach(function(statusName) {
if (statusName in itemStatus) {
Expand Down Expand Up @@ -777,7 +793,8 @@ selfoss.dbOffline = {

if (updateStats) {
for (var statusName in statsDiff) {
if (statsDiff.hasOwnProperty(statusName)) {
if (statsDiff.hasOwnProperty(statusName)
&& statusName != 'skipped') {
selfoss.db.storage.stats.get(statusName, function(stat) {
selfoss.db.storage.stats.put({
name: statusName,
Expand All @@ -791,23 +808,25 @@ selfoss.dbOffline = {
},


entriesMark: function(itemIds, unread) {
entriesMark: function(itemIds, unread, skipped) {
selfoss.dbOnline.statsDirty = true;
var newStatuses = itemIds.map(function(itemId) {
return {id: itemId, unread: unread};
return {id: itemId, unread: unread, skipped: skipped,
updatetime: new Date()};
});
return selfoss.dbOffline.storeEntryStatuses(newStatuses);
},


entryMark: function(itemId, unread) {
return selfoss.dbOffline.entriesMark([itemId], unread);
return selfoss.dbOffline.entriesMark([itemId], unread, false);
},


entryStar: function(itemId, starred) {
return selfoss.dbOffline.storeEntryStatuses([{
id: itemId,
updatetime: new Date(),
starred: starred
}]);
}
Expand All @@ -822,7 +841,7 @@ selfoss.db = {
storage: null,
online: true,
enableOffline: window.localStorage.getItem('enableOffline') === 'true',
entryStatusNames: ['unread', 'starred'],
entryStatusNames: ['unread', 'starred', 'skipped'],
userWaiting: true,


Expand Down Expand Up @@ -935,7 +954,7 @@ selfoss.db = {
selfoss.filter.extraIds.push(selfoss.events.entryId);
}

if (!append || selfoss.filter.type != 'newest') {
if (!append || selfoss.filter.type == 'starred' || selfoss.filter.type == 'unread') {
selfoss.dbOffline.olderEntriesOnline = false;
}

Expand Down
2 changes: 2 additions & 0 deletions public/js/selfoss-events-navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ selfoss.events.navigation = function() {
selfoss.filter.type = 'unread';
} else if ($(this).hasClass('nav-filter-starred')) {
selfoss.filter.type = 'starred';
} else if ($(this).hasClass('nav-filter-lastread')) {
selfoss.filter.type = 'lastread';
}

selfoss.events.reloadSamePath = true;
Expand Down
2 changes: 1 addition & 1 deletion public/js/selfoss-events.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ selfoss.events = {

// load items
if ($.inArray(selfoss.events.section,
['newest', 'unread', 'starred']) > -1) {
['newest', 'unread', 'starred', 'lastread']) > -1) {
selfoss.filter.type = selfoss.events.section;
selfoss.filter.tag = '';
selfoss.filter.source = '';
Expand Down
Loading