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
Only check for user preferences if signed in #951
Changes from all commits
b3bc0d4
8be131c
4ba7d6a
aab2313
c0b42f4
0b5b4b5
4949c7f
a1bb304
94ba47e
33abac3
306a554
1bba8a3
81caadb
6a4b147
eba672d
4582d43
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -158,7 +158,9 @@ window.QPixel = { | |||||
* @returns {Promise<Object>} a JSON object containing user details | ||||||
*/ | ||||||
user: async () => { | ||||||
if (QPixel._user) return QPixel._user; | ||||||
if (QPixel._user != null) { | ||||||
return QPixel._user; | ||||||
} | ||||||
const resp = await fetch('/users/me', { | ||||||
credentials: 'include', | ||||||
headers: { | ||||||
|
@@ -177,29 +179,28 @@ window.QPixel = { | |||||
* @returns {Promise<Object>} a JSON object containing user preferences | ||||||
*/ | ||||||
preferences: async () => { | ||||||
if (this._preferences == null && !!localStorage['qpixel.user_preferences']) { | ||||||
this._preferences = JSON.parse(localStorage['qpixel.user_preferences']); | ||||||
|
||||||
// If we don't have the global key, we're probably using an old preferences schema. | ||||||
if (!this._preferences.global) { | ||||||
delete localStorage['qpixel.user_preferences']; | ||||||
this._preferences = null; | ||||||
} | ||||||
// Do not attempt to access preferences if user is not signed in | ||||||
const user = await QPixel.user(); | ||||||
if ('error' in user) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. consider:
Suggested change
This is entirely opinion, but I find the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know the syntax of I still prefer
In this particular case it's unlikely Just checked out of curiosity and it is possible to be even more explicit with JavaScript, similarly to Python but a little more verbose:
I don't feel strongly enough about this particular case to argue against any of the mentioned forms though. |
||||||
return null; | ||||||
} | ||||||
else if (this._preferences == null) { | ||||||
// If they're still null (or undefined) after loading from localStorage, we're probably on a site we haven't | ||||||
// loaded them for yet. Load from Redis via AJAX. | ||||||
const resp = await fetch('/users/me/preferences', { | ||||||
credentials: 'include', | ||||||
headers: { | ||||||
'Accept': 'application/json' | ||||||
} | ||||||
}); | ||||||
const data = await resp.json(); | ||||||
localStorage['qpixel.user_preferences'] = JSON.stringify(data); | ||||||
this._preferences = data; | ||||||
// Early return for the most frequent case (local variable already contains the preferences) | ||||||
if (QPixel._preferences != null) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment shows me that you already knew what I just explained in my response to your previous comment. I apologise. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This value should never be |
||||||
return QPixel._preferences; | ||||||
} | ||||||
return this._preferences; | ||||||
// Early return the preferences from localStorage unless null or undefined | ||||||
const key = await QPixel._preferencesLocalStorageKey(); | ||||||
const localStoragePreferences = (key in localStorage) | ||||||
? JSON.parse(localStorage[key]) | ||||||
: null; | ||||||
if (localStoragePreferences != null) { | ||||||
QPixel._preferences = localStoragePreferences; | ||||||
return QPixel._preferences; | ||||||
} | ||||||
// Preferences are still null (or undefined) after loading from localStorage, so we're probably on a site we | ||||||
// haven't loaded them for yet. Load from Redis via AJAX. | ||||||
await QPixel._fetchPreferences(); | ||||||
return QPixel._preferences; | ||||||
}, | ||||||
|
||||||
/** | ||||||
|
@@ -212,26 +213,16 @@ window.QPixel = { | |||||
let prefs = await QPixel.preferences(); | ||||||
let value = community ? prefs.community[name] : prefs.global[name]; | ||||||
|
||||||
// Deliberate === here: null is a valid value for a preference, but undefined means we haven't fetched it. | ||||||
// If we haven't fetched a preference, that probably means it's new - run a full re-fetch. | ||||||
if (value === undefined) { | ||||||
const resp = await fetch('/users/me/preferences', { | ||||||
credentials: 'include', | ||||||
headers: { | ||||||
'Accept': 'application/json' | ||||||
} | ||||||
}); | ||||||
const data = await resp.json(); | ||||||
localStorage['qpixel.user_preferences'] = JSON.stringify(data); | ||||||
this._preferences = data; | ||||||
|
||||||
prefs = await QPixel.preferences(); | ||||||
value = community ? prefs.community[name] : prefs.global[name]; | ||||||
return value; | ||||||
} | ||||||
else { | ||||||
// Note that null is a valid value for a preference, but undefined means we haven't fetched it. | ||||||
if (typeof(value) !== 'undefined') { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In general I prefer to use Defining a variable as const value = undefined;
if (value !== undefined) {
console.log('defined');
} else {
console.log('undefined');
} Declaring a variable but not defining it also causes no problem. The following also outputs 'undefined' as expected: let value;
if (value !== undefined) {
console.log('defined');
} else {
console.log('undefined');
} Not declaring a variable causes a problem as it cannot be accessed in order to process the conditional expression of the if (value !== undefined) {
console.log('defined');
} else {
console.log('undefined');
} Using if (typeof(value) !== 'undefined') {
console.log('defined');
} else {
console.log('undefined');
} The line you commented on follows a declaration of |
||||||
return value; | ||||||
} | ||||||
// If we haven't fetched a preference, that probably means it's new - run a full re-fetch. | ||||||
await QPixel._fetchPreferences(); | ||||||
|
||||||
prefs = await QPixel.preferences(); | ||||||
value = community ? prefs.community[name] : prefs.global[name]; | ||||||
return value; | ||||||
}, | ||||||
|
||||||
/** | ||||||
|
@@ -258,11 +249,45 @@ window.QPixel = { | |||||
console.error(resp); | ||||||
} | ||||||
else { | ||||||
this._preferences = data.preferences; | ||||||
localStorage['qpixel.user_preferences'] = JSON.stringify(this._preferences); | ||||||
await QPixel._updatePreferencesLocally(data.preferences); | ||||||
} | ||||||
}, | ||||||
|
||||||
/** | ||||||
* Get the key to use for storing user preferences in localStorage, to avoid conflating users | ||||||
* @returns {Promise<string>} the localStorage key | ||||||
*/ | ||||||
_preferencesLocalStorageKey: async () => { | ||||||
const user = await QPixel.user(); | ||||||
return `qpixel.user_${user.id}_preferences`; | ||||||
}, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since you change the local storage key or a user to a different value with this change, I presume old values will be ignored and no longer used. What will the effect of that be? Will those values correctly be loaded from the servers, or do the settings set disappear? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I have understood correctly, the localStorage version of The In cases where two or more users share a browser, they may historically have overridden each other's localStorage preferences as this only existed once per browser. In these cases their preferences will now revert to their correct preferences from the server. If a user has become accustomed to incorrect preferences, this will appear to them as a change. Is this something we need to avoid / warn about? I don't know how many users (if any) share a browser with another user. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since this is my first code change to QPixel, if there's any doubt about the consequences it might be worth a second (third?) opinion. My understanding may be incorrect. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @trichoplax your understanding sounds correct to me. The only other effect that I can think this will have is to force a re-fetch of every user's preferences the first time they visit after this is deployed. That's not a major load. |
||||||
|
||||||
/** | ||||||
* Update local variable _preferences and localStorage with an AJAX call for the user preferences | ||||||
* @returns {Promise<void>} | ||||||
*/ | ||||||
_fetchPreferences: async () => { | ||||||
const resp = await fetch('/users/me/preferences', { | ||||||
credentials: 'include', | ||||||
headers: { | ||||||
'Accept': 'application/json' | ||||||
} | ||||||
}); | ||||||
const data = await resp.json(); | ||||||
await QPixel._updatePreferencesLocally(data); | ||||||
}, | ||||||
|
||||||
/** | ||||||
* Set local variable _preferences and localStorage to new preferences data | ||||||
* @param data an object, containing the new preferences data | ||||||
* @returns {Promise<void>} | ||||||
*/ | ||||||
_updatePreferencesLocally: async data => { | ||||||
QPixel._preferences = data; | ||||||
const key = await QPixel._preferencesLocalStorageKey(); | ||||||
localStorage[key] = JSON.stringify(QPixel._preferences); | ||||||
}, | ||||||
|
||||||
/** | ||||||
* Get the word in a string that the given position is in, and the position within that word. | ||||||
* @param splat an array, containing the string already split by however you define a "word" | ||||||
|
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.
Inquiry: The old code was doing something equivalent (perhaps even slightly better, since it also handles undefined?). Was there a reason for changing this part?
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.
My main reason for changing this was to comply with the code standards that forbid omitting braces from
if
statements:Adding in the
!= null
is not essential, I just prefer explicit comparisons for readability. The file currently has a mix of both styles and I'm happy to go with whichever is preferred.Even the addition of the braces is not essential. I was just making the code a little more code standards compliant while the file was already being changed.
Incidentally (not related to this change),
!= null
still handlesundefined
(but not undeclared variables). This is mentioned in the code standards:Note that
if (value == null)
will evaluate totrue
for bothnull
andundefined
. I find this counterintuitive, so if they were my code standards I would forbid this usage, and make all comparisons strict (!==
or===
).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.
Perfectly happy with this as is - just to mention that if you have something like this check that you want to make explicit, you can force it to a boolean:
if (QPixel._user)
becomesif (!!QPixel._user)
.