Skip to content
This repository was archived by the owner on May 21, 2021. It is now read-only.
Open
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
40 changes: 40 additions & 0 deletions src/lib/background/database/flattrs/save.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"use strict";

const {Date} = require("global/window");

const {db} = require("./index.js");

function* save({entity, tabId, title, type, url})
{
let {flattrs} = db;

if (url)
{
flattrs = flattrs.where("url").equals(url);
}
else
{
flattrs = flattrs.where("entity").equals(entity)
.and((entry) => entry.url == url);
}

let entry = yield flattrs.first();
if (!entry)
{
entry = {
entity, title, url,
timestamps: [Date.now()]
};

yield db.flattrs.add(entry);
}
else
{
entry.timestamps.push(Date.now());

yield db.flattrs.modify(entry);
}

return entry;
}
exports.save = save;
39 changes: 4 additions & 35 deletions src/lib/background/flattrManager.js
Original file line number Diff line number Diff line change
@@ -1,46 +1,15 @@
"use strict";

const {Date} = require("global/window");

const {emit} = require("../common/events");
const {db} = require("./database/flattrs");
const {sendFlattrs} = require("./state/actions/flattrs");
const {saveFlattrs} = require("./state/actions/flattrs");
const {store} = require("./state");

function submit({entity, tabId, title, type, url})
{
return db.transaction("rw", db.flattrs, function*()
{
let {flattrs} = db;
if (url)
{
flattrs = flattrs.where("url").equals(url);
}
else
{
flattrs = flattrs.where("entity").equals(entity)
.and((entry) => entry.url == url);
}
let entry = yield flattrs.first();
if (!entry)
{
entry = {
entity, title, url,
timestamps: []
};
}
entry.timestamps.push(Date.now());

yield db.flattrs.put(entry);

return entry;
})
.then((entry) =>
{
emit("flattr-added", {flattr: entry, tabId, type});
store.dispatch(sendFlattrs({flattrs: [entry]}));
return entry;
});
store.dispatch(saveFlattrs({
flattrs: [{entity, tabId, title, type, url}]
}));
}
exports.submit = submit;

Expand Down
6 changes: 3 additions & 3 deletions src/lib/background/history/task.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ const settingName = "history.lastProcessing";

function submitFlattrEntities(flattrEntities)
{
return flattrEntities.map((entity) =>
for (let entity of flattrEntities)
{
return submit({
submit({
entity,
tabId: null,
title: entity,
type: "visit",
url: `http://${entity}/`
});
});
}
}

function startProcessing(lastProcessing)
Expand Down
2 changes: 1 addition & 1 deletion src/lib/background/session/storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function addAttention(tabId, url, addedAttention, isManual)

if (addedAttention >= getRemainingAttention(entity, oldAttention))
{
yield flattrManager.submit({
flattrManager.submit({

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're no longer waiting for the submission to succeed before continuing with the next steps. Why wouldn't this be an issue?

At least I could imagine that this leads to inconsistencies in the UI (e.g. attention bar filling up but flattr count not increasing).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least I could imagine that this leads to inconsistencies in the UI (e.g. attention bar filling up but flattr count not increasing).

At the moment if there is an error then nothing in the UI changes, is that better? I don't think it is, I think it's worse because we're just hiding errors from the user.

Also we reduce the chances of an error here because we make sure that only one save is performed at a time, and so we don't depend on transactions (which can cause errors).

Finally we at least have the chance to correct the error here, with logic that can detect error states like the one that you pointed out above (Pretty sure I didn't do this here yet, but I think that would be a good follow up issue).

entity, tabId, url,
title: page.title,
type: "attention"
Expand Down
6 changes: 6 additions & 0 deletions src/lib/background/state/actions/flattrs.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
"use strict";

const {
SAVE_FLATTRS,
SUBMIT_FLATTRS
} = require("../types/flattrs");

exports.saveFlattrs = ({flattrs}) =>
{
return {type: SAVE_FLATTRS, flattrs};
};

exports.sendFlattrs = ({flattrs}) =>
{
return {type: SUBMIT_FLATTRS, flattrs};
Expand Down
54 changes: 44 additions & 10 deletions src/lib/background/state/reducers/flattrs.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
"use strict";

const {
SAVE_FLATTRS,
SAVE_FLATTRS_MERGE_PENDING,
SAVE_FLATTRS_SUCCESS,
SUBMIT_FLATTRS,
SUBMIT_FLATTRS_MERGE_PENDING,
SUBMIT_FLATTRS_SUCCESS,
Expand All @@ -10,36 +13,67 @@ const {
const {filterFlattrsForURLs} = require("../filters/flattrs");

const getFlattrsInitialState = () => ({
submitting: [], pending: []
save: {saving: [], pending: []},
submit: {submitting: [], pending: []}
});

let validFlattrs = (action) => (
Array.isArray(action.flattrs) && action.flattrs.length > 0);

const flattrsReducer = (state = getFlattrsInitialState(), action) =>
{
let {submitting, pending} = state;
let diff = {};
let newState = {};
newState.save = Object.assign({}, state.save);
newState.submit = Object.assign({}, state.submit);

switch (action.type)
{
case SUBMIT_FLATTRS_MERGE_PENDING:
diff = {pending: [], submitting: submitting.concat(pending)};
case SAVE_FLATTRS:
if (!validFlattrs(action))
{
break;
}
newState.save.pending = state.save.pending.concat(action.flattrs);
break;
case SAVE_FLATTRS_MERGE_PENDING:
newState.save.pending = [];
newState.save.saving = state.save.saving.concat(state.save.pending);
break;
case SAVE_FLATTRS_SUCCESS:
newState.save.saving = [];
break;
case SUBMIT_FLATTRS:
if (!Array.isArray(action.flattrs) || action.flattrs.length < 1)
if (!validFlattrs(action))
{
break;
}
diff.pending = pending.concat(filterFlattrsForURLs(action.flattrs));
newState.submit.pending =
state.submit.pending.concat(filterFlattrsForURLs(action.flattrs));
break;
case SUBMIT_FLATTRS_MERGE_PENDING:
newState.submit.pending = [];
newState.submit.submitting =
state.submit.submitting.concat(state.submit.pending);
break;
case SUBMIT_FLATTRS_FAILURE:
// we gave up trying to send flattrs to server, so stick the failed
// flattrs back in to the pending queue for the next event
diff.pending = pending.concat(submitting);
newState.submit.pending =
state.submit.pending.concat(state.submit.submitting);
// fall through
case SUBMIT_FLATTRS_SUCCESS:
diff.submitting = [];
newState.submit.submitting = [];
break;
}

return Object.assign({}, state, diff);
return newState;
};
exports.flattrs = flattrsReducer;

const getFlattrs = (state) => ((state || {}).flattrs || {});

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: I'd suggest writing this as (state = {}) => (state.flattrs || {}) to make this part a bit easier to read by avoiding the inner brackets.


const getFlattrsToSave = (state) => (getFlattrs(state).save || {});
exports.getFlattrsToSave = getFlattrsToSave;

const getFlattrsToSubmit = (state) => (getFlattrs(state).submit || {});
exports.getFlattrsToSubmit = getFlattrsToSubmit;
2 changes: 2 additions & 0 deletions src/lib/background/state/sagas/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

const {fork} = require("redux-saga/effects");
const {watchForFailuresAndLogError} = require("./failureLogger");
const {watchForSaveFlattrs} = require("./saveFlattrs");
const {watchForSubmitFlattrs} = require("./submitFlattrs");
const {watchForNewVisits} = require("./saveVisitTimestamps");

let sagas = [
watchForFailuresAndLogError.bind(null, {}),
watchForNewVisits,
watchForSaveFlattrs,
watchForSubmitFlattrs
];

Expand Down
58 changes: 58 additions & 0 deletions src/lib/background/state/sagas/saveFlattrs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"use strict";

const {buffers} = require("redux-saga");
const {
actionChannel, call, put, select, take
} = require("redux-saga/effects");

const {
SAVE_FLATTRS,
SAVE_FLATTRS_MERGE_PENDING,
SAVE_FLATTRS_SUCCESS,
SUBMIT_FLATTRS
} = require("../types/flattrs");
const {getFlattrsToSave} = require("../reducers/flattrs");
const {save} = require("../../database/flattrs/save");
const {emit} = require("../../../common/events");

function* saveFlattrs()
{
yield put({type: SAVE_FLATTRS_MERGE_PENDING});

let {saving} = yield select(getFlattrsToSave);

for (let {entity, tabId, title, type, url} of saving)
{
let entry = yield call(save, {entity, tabId, title, type, url});

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if save() fails? We do have SUBMIT_FLATTRS_FAILURE as well as SAVE_TIMESTAMP_FAILURE for the other cases for which we use Redux.


yield call(emit, "flattr-added", {flattr: entry, tabId, type});

// we don't want to wait for submission to complete here
yield call(put, {type: SUBMIT_FLATTRS, flattrs: [entry]});
}

yield put({type: SAVE_FLATTRS_SUCCESS});
}

function* watchForSaveFlattrs()
{
const buffer = yield call(buffers.dropping, 1);

const saveFlattrsChan = yield actionChannel(SAVE_FLATTRS, buffer);

while (true)
{
yield take(saveFlattrsChan);

const {pending, saving} = yield select(getFlattrsToSave);

// it's possible that a previous handler already submit the flattrs
if (pending.length < 1 && saving.length < 1)
{
continue;
}

yield call(saveFlattrs, {});

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Detail: saveFlattrs() doesn't expect any arguments.

}
}
exports.watchForSaveFlattrs = watchForSaveFlattrs;
9 changes: 3 additions & 6 deletions src/lib/background/state/sagas/submitFlattrs.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,12 @@ const {
SUBMIT_FLATTRS_SUCCESS,
SUBMIT_FLATTRS_FAILURE
} = require("../types/flattrs");

const {getFlattrsToSubmit} = require("../reducers/flattrs");
const {API_RETRY_DELAY_MS} = require("../../../common/constants");
const {sendFlattrs} = require("../../server/api");

const retryStatus = new Set([0, 408, 409, 500, 502, 503, 504, 525, 599]);

const getFlattrs = (state) => (state || {}).flattrs || {};
exports.getFlattrs = getFlattrs;

function* submitFlattrs({retryDelays = API_RETRY_DELAY_MS})
{
let retryCount = retryDelays.length;
Expand All @@ -30,7 +27,7 @@ function* submitFlattrs({retryDelays = API_RETRY_DELAY_MS})
// submission
yield put({type: SUBMIT_FLATTRS_MERGE_PENDING});

const {submitting: flattrs} = yield select(getFlattrs);
const {submitting: flattrs} = yield select(getFlattrsToSubmit);

if (flattrs.length < 1)
{
Expand Down Expand Up @@ -88,7 +85,7 @@ function* watchForSubmitFlattrs()
{
yield take(submitFlattrsChan);

const {pending, submitting} = yield select(getFlattrs);
const {pending, submitting} = yield select(getFlattrsToSubmit);

// it's possible that a previous handler already submit the flattrs
if (pending.length < 1 && submitting.length < 1)
Expand Down
3 changes: 3 additions & 0 deletions src/lib/background/state/types/flattrs.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
"use strict";

exports.SAVE_FLATTRS = "DB:SAVE_FLATTRS";
exports.SAVE_FLATTRS_MERGE_PENDING = "DB:SAVE_FLATTRS_MERGE_PENDING";
exports.SAVE_FLATTRS_SUCCESS = "DB:SAVE_FLATTRS_SUCCESS";
exports.SUBMIT_FLATTRS = "API:SUBMIT_FLATTRS";
exports.SUBMIT_FLATTRS_MERGE_PENDING = "API:SUBMIT_FLATTRS_MERGE_PENDING";
exports.SUBMIT_FLATTRS_SUCCESS = "API:SUBMIT_FLATTRS_SUCCESS";
Expand Down
Loading