-
Notifications
You must be signed in to change notification settings - Fork 56
[Feature] Ratings prompt #22
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
Conversation
export var noOpTrackerTimeoutDuration = 20 * 1000; // 20 seconds | ||
export var fontSizeStep = 2; | ||
export var maxClipSuccessForRatingsPrompt = 12; // TODO waiting for consensus on this value | ||
export var maximumJSTimeValue = 8640000000000000; // http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.1 |
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.
can you make this multiplied out like the others?
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.
sorry, what would the multiplication you're suggesting here be? e.g., do you find 100,000,000 (days) * 86,400,000 (ms)
more readable (as described by the spec linked to in the comment)?
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.
Eh I guess it doesn't matter since its a max time value.
100000000 * 24 * 60 * 60 * 1000; // 100K days in milliseconds, http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.1
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.
hm, ok I can do that!
#Pending -> #Resolved
…ipper.tsx; increment as part of SaveToOneNote.startClip
@@ -52,10 +52,11 @@ test("helper function checkValueAndDescriptionOnObject should assert when given | |||
ok(checkValueAndDescriptionOnObject(valid)); | |||
}); | |||
|
|||
test("All entries in setting.json should have a non-empty Value and Description", () => { | |||
// TODO is a non-empty check necessary? | |||
/*test("All entries in setting.json should have a non-empty Value and Description", () => { |
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.
Commented out test?
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.
Yeah, wasn't sure if there was a technical reason why we disallow empty values... because now I'd like to start using empty values. I'll assume it's all good and delete this test.
#Pending -> #Resolved
|
||
// MOCK STORAGE | ||
|
||
let mockStorage: { [key: string]: string }; |
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.
We have 3-ish versions of mockStorage
out there. I see one in tooltipHelper_tests.ts
, another in sectionPicker_tests.ts
. Can you add a low pri PBI to unify these? Not necessary for this PR (may never be necessary 😜).
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.
#Resolved (opened #41)
* Public for testing | ||
*/ | ||
public static clipSuccessDelayIsOver(numClips: number): boolean { | ||
// TODO MVP+? when # of successful clips > nMax, collapse panel into a Rate Us hyperlink in the footer that is always available |
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.
similar to another comment. Create a task for this somewhere
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.
#Resolved (in VSO)
strictEqual(shouldShowRatingsPrompt, false); | ||
}); | ||
|
||
// test("", () => { }); |
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.
Tests for RatingsHelper looks very thorough !
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.
😃
}); | ||
} | ||
break; | ||
case RatingsPromptStage.Feedback: |
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.
Can you split these into functions for each state for readability? The logic looks good though.
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.
#WontFix after in-person discussion: any readability concern seems to apply only to the Init stage functions, and overall isn't too concerning
|
||
export function preCacheValues(storageKeys: string[]): void { | ||
for (let key of storageKeys) { | ||
Clipper.Storage.getValue(key, () => { }, true); |
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 know Matthew made a getValues
but you'd have to add in the caching in again that you already added to getValue
(if we want to switch to that function). Just a heads up depending on who merges first.
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.
#Pending -> #Resolved (merged with master)
let doNotPromptRatingsStr: string = Clipper.Storage.getCachedValue(Constants.StorageKeys.doNotPromptRatings); | ||
let lastBadRatingDateAsStr: string = Clipper.Storage.getCachedValue(Constants.StorageKeys.lastBadRatingDate); | ||
let lastBadRatingVersion: string = Clipper.Storage.getCachedValue(Constants.StorageKeys.lastBadRatingVersion); | ||
let lastSeenVersion: string = Clipper.Storage.getCachedValue(Constants.StorageKeys.lastSeenVersion); |
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.
This is gonna sound crappy but I am wary of this paradigm of knowing when a value is cached versus when it isn't. I think since you have tested it pretty thoroughly it will work here. The options are:
- Always get the fresh value from
localStorage
, which means dealing with callbacks, which is why we switched to this design in the first place - Risk not having a fresh value. If the caller needs a fresh value, force them to use the async version.
What happens when you attempt getCachedValue
but a corresponding getValue
was never called? It is hard to differentiate between not being in storage, as we have a lot of cases where it doesn't exist yet, versus it not being in the cache. From preCacheNeededValues
I know that these values are fine but we should discuss how we want to treat this in the future.
TL;DR: async
makes everything difficult -.-
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.
Time to take another hard look at being able to use async/await?
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 think we should too. I think there's lots of benefit in this.
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.
@VishaalK I definitely get your concern here - getCachedValue
is easy to accidentally misuse.
@VishaalK @leakymemory @mttwc I assume we're okay with using this paradigm for this PR, even if we need a better permanent solution... so how should we facilitate discussing the future?
Looks like async/await
didn't make it into ES7 (ES2016), and only "has a chance" of making ES8 (ES2017)... so do we want to polyfill it? I can open a task for investigation if we all agree.
And what happened with the first hard look at async/await
?
#Pending -> #Resolved (opened #43)
Noting a bug that @VishaalK informed me of: #Pending -> #Resolved |
… on the SignInPanel; fix what that broke on RatingsPanel
7bbbced
to
fa09552
Compare
fa09552
to
d0f287d
Compare
This feature enables the Web Clipper to receive positive and negative feedback from "active" users.
When a user's engagement with the Web Clipper qualifies them, we will show an additional sub-panel after a successful clip, which asks: "Enjoying the Web Clipper?" We will then log their response ("Yes, it's great!" vs "Not really...") depending on button clicked.
Additionally, if rate and feedback URLs are provided in Web Clipper settings, we will show the user additional prompts that will direct them to places where they can leave further feedback.
The "when to show" logic for this ratings sub-panel is where things get a little complicated. In broad strokes, the checks break down as follows:
Please see method descriptions in
RatingsHelper.ts
for further breakdown of the "when to show" logic.PENDING TO-DOs as of initial PR publish:
ratingsHelper_tests.tsx