-
Notifications
You must be signed in to change notification settings - Fork 59
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
Add user preference persistence framework #1024
Add user preference persistence framework #1024
Conversation
Part of the currentUser resource. Uses JS proxies to enable some — hopefully comfortable — abstractions for working with preferences.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still haven't had a chance to write up my idea about reusing existing request logic, but I had a few unrelated comments that I thought I could share sooner.
I mentioned this before, but I really like how the currentUser.preferences
proxy works. 👏 Sending requests under the hood, using shallow reactivity, and returning a nice default even when the user doesn't have any preferences for a specific project. 👍
preferences = { | ||
site: {}, | ||
projects: {}, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than expecting the full preferences
object here, maybe it would be useful to allow tests to specify a partial object, then merge that partial object with the full object below. So here, it would be:
preferences = {},
And below, it would be:
// mergeDeepLeft() is from ramda
preferences: mergeDeepLeft(preferences, {
site: {},
projects: {}
}),
That would allow a test to easily specify a site preference without also specifying projects: {}
, e.g., something like:
// test/components/project/list.spec.js
it('uses the projectSortMode preference', () => {
// Specifying site without projects.
mockLogin({ preferences: { site: { projectSortMode: 'alphabetical' } } });
createProjects([
{ name: 'A' },
{ name: 'B', lastSubmission: ago({ days: 5 }).toISO() }
]);
const component = mountComponent();
const blocks = mountComponent().findAllComponents(ProjectHomeBlock);
// ['B', 'A'] is the default, as B has the latest data.
blocks.map(block => block.props().project.name).should.eql(['A', 'B']);
const select = component.getComponent(ProjectSort);
select.props().modelValue.should.equal('alphabetical');
});
Just an idea! Could also come later once tests are added.
669a062
to
4bb37a6
Compare
…d privatize some class members
f3909a5
to
0f8404b
Compare
When tests are run locally or in CircleCI, this warning is shown:
I took a look at what was going on. From the warning, it seems like While looking at the tests that logged the warning, I noticed that many rendered the I think the solution to this is just to make sure that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a few more small comments! I may add a few more tomorrow, but I thought I'd go ahead and leave these now.
I'm still seeing a slight issue with tests, in test/components/project/list.spec.js. I see things like this being logged:
That happens because tests in that file change the selection for the project sort. That didn't used to send a request, but now it does. Tests expect that a response is specified for every request that's sent. Interestingly, this isn't leading to tests failing, which I thought it would. It looks like test/run.sh fails if it sees For the tests that are logging errors, I updated them locally to handle the new requests. However, I had to do something special to account for |
@brontolosone and I have discussed merging this PR first, then following up with another PR with tests. Given that, I think this PR can be marked as ready for review? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, these are all my comments for now, mostly just small things. It's looking great overall. 👍
Co-authored-by: Matthew White <[email protected]>
…ferences-rb-02' into central_689-userpreferences-rb-02
Co-authored-by: Matthew White <[email protected]>
…ferences-rb-02' into central_689-userpreferences-rb-02
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good, LGTM! 🚀
I added one last comment, but it's not a blocker for merging.
Towards getodk/central#689
Supersedes #1021 which superseded #1018
Related: getodk/central-backend#1184 (backend)
This introduces persistent user preferences as per getodk/central#689.
They are exposed on the currentUser resource as
currentUser.preferences
, split up insite
andprojects
objects. These objects are proxied to enable some — hopefully comfortable — abstractions for working with preferences, egcurrentUser.preferences.projects[someProjectID].someproperty = "camembert"
will work even ifprojects[someProjectId]
doesn't actually exist yet (it will be autocreated).The
PUT
andDELETE
HTTP requests necessary for propagating values to the backend are implemented as side effects via these proxy objects, too.The semantics of the preferences values are completely up to the frontend, but the reactiveness enabled is shallow, thus complex preference values (eg objects, arrays) are discouraged — those are inconvenient anyway; more granular key-value pairs are preferred as those will result in less clobbering under concurrent activity from multiple sessions.
Implemented preferences:
Todo:
For later: