-
Notifications
You must be signed in to change notification settings - Fork 50
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
Embed editor state in URL; remove session storage #299
Embed editor state in URL; remove session storage #299
Conversation
client/src/Try/Container.purs
Outdated
@@ -141,12 +139,7 @@ component = H.mkComponent | |||
handleAction $ Compile Nothing | |||
|
|||
Cache text -> H.liftEffect do |
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.
Not sure if Cache
still makes sense as the action name, maybe Persist
? EncodeInURL
?
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.
EncodeInUrl
sounds good to me.
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.
Renamed here 6eb73be
withSession :: Aff { sourceFile :: Maybe SourceFile, code :: String } | ||
withSession = do |
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.
Also maybe the withSession
name no longer makes sense, now that this doesn't take a sessionId
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.
What are your thoughts on what this could be renamed to then?
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.
Couldn't think of anything better 😅 so I'll just leave it
aca3f09
to
2d5c9bd
Compare
client/src/Try/Container.purs
Outdated
H.modify_ (_ + 1) | ||
H.liftAff $ delay (1_500.0 # Milliseconds) | ||
H.modify_ (_ - 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.
Maybe this state would be more clear if it were a showConfirmation :: Boolean
instead of Int
? I used Int
so that repeated clicks would keep the confirmation message showing, but that's probably not 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.
Doesn't this block the thread because you're not wrapping the delay
in an H.fork
? And shouldn't some debouncer be used here:
- user clicks the button, which copies the content to the clipboard and starts a debouncer. If the copy doesn't fail, a "Copied to Clipboard" message is displayed. If it does fail, a "Failed to Copy" is displayed.
- if user clicks again before debouncer ends, debounder restarts
- if debouncer times out, then the text changes back to "Share URL"
Then, the state would be just showConfirmation :: Boolean
or perhaps Maybe String
where Nothing = "Share URL"
and Just s = s
where s
is either "Failed to Copy" or "Copied to Clipboard". If you initialized this component with a Ref
, you could use that to store the reference to the debouncer's ref.
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.
Wouldn't debouncing mean something like: we delay writing to clipboard until N milliseconds have passed without another click event? I think I'd still want to write to clipboard immediately, but just keep the notification message showing for some time after. I'm new to Halogen, so there might be a way to leverage debouncing to accomplish that that I'm unaware of; maybe I'd need to setup an event emitter and then debounce some "hide notification message" event?
I've got a prototyped solution using H.fork
and storing the ForkId
in state here: https://try.purescript.org/?gist=5605231047155a3bf4b834daa03b510e
I'm not sure if there's a simpler way, but that will at least allow showing either a success or failure message as appropriate. I'll plan on going with that unless I find a simpler solution.
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.
Using H.fork
(and cancelling previous handleAction
) here 6eb73be
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.
Wouldn't debouncing mean something like: we delay writing to clipboard until N milliseconds have passed without another click event?
No, we'd write to the clipboard immediately. We would delay when we change from "Copied to Clipboard" back to the original text of "Share Link". If multiple clicks happen, we keep displaying "Copied to Clipboard". Once the debouncer times out because the user hasn't clicked that button for a while, we change back to "Share Link".
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 the recent changes implement that behavior, though I'm not explicitly using a debounce
function.
client/src/Try/Container.purs
Outdated
H.liftAff $ makeAff \f -> do | ||
runEffectFn3 copyToClipboard url (f (Right unit)) (f (Left $ Aff.error "Failed to copy to clipboard")) |
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 copying the pattern I saw with how setupIFrame
is called, but maybe showing a message to the user is more appropriate than throwing an Error
here.
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.
Perhaps a simple window >>= alert
would be a cheap-and-easy way to notify the user? I know it's not the best UX, but do we anticipate this failing that often?
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.
Actually, since the "Share URL" changes to 'Copied to Clipboard" when that is clicked, could it also be changed to "Failed to Copy" when a failure occurs?
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.
Actually, since the "Share URL" changes to 'Copied to Clipboard" when that is clicked, could it also be changed to "Failed to Copy" when a failure occurs?
Yeah, I'll plan on 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.
Showing the error message in UI here 6eb73be
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.
do we anticipate this failing that often?
Browser support is pretty much universal https://caniuse.com/mdn-api_clipboard_writetext. I was thinking there was a possibility that some users might have disabled writing to the clipboard, but I haven't figured out how to do that in Chrome/Firefox. In Chrome there is a setting for disabling reading from clipboard, but haven't seen a way of limiting write access.
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.
Hmm, I’d probably want to also wrap the FFI call in try
/catch
in the 5% chance that navigator.clipboard.writeText
doesn’t exist (in non-supported browsers)
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.
Updated here: 973b0ff
Show copy failure message to user; rename action
client/src/Try/Container.purs
Outdated
H.modify_ _ { showCopySucceeded = Just copySucceeded } | ||
forkId <- H.fork do | ||
H.liftAff $ delay (1_500.0 # Milliseconds) | ||
H.modify_ (_ - 1) | ||
render :: Int -> H.ComponentHTML Unit () Aff | ||
render n = | ||
H.modify_ _ { showCopySucceeded = Nothing } | ||
H.modify_ _ { forkId = Just forkId } |
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 modifying the state twice...
copySucceed <- ...
H.modify_ _ { showCopySucceeded = Just copySucceeded }
...
H.modify_ _ { forkId = Just forkId }
Why not do a single state update at the end?
copySucceed <- ...
forkId <- H.fork do ...
H.modify_ $ const { forkId: Just forkId, showCopySucceeded: Just copySucceeded }
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.
Updated here 1f4b17a
I'll have to try this out locally, but I this looks good to me. |
I've checked this out locally and played with it for a bit. The code works, but there are a few UX things to figure out now. I think these can be solved in a separate PR. First, if I load a gist, the URL will include the gist in the query param in addition to the Second, now that we can share URLs, some may expect the URL to automatically update to whatever options the user chooses. For example, if the user chooses "View > Output", we'll see only the output, but the URL doesn't update to reflect that. If User A sends the URL to User B with the intent of discussing just the output, User B will see the side-by-side view, not the output view. This will likely be a bit confusing to both. What are your thoughts? |
Oh, one other thing. The readme needs to be updated to drop the part about session storage. |
e805b7b
to
409795b
Compare
What was the reasoning behind using |
It was an arbitrary choice, I'd be open to change it to whatever. Rust Playground uses |
Yeah, using `code` sounds good.
|
I have a separate commit for syncing the URL with the local state for |
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.
LGTM. The other stuff can be resolved in a separate PR.
🏓 @garyb Can I get an approval on 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.
Nice 👍
lz-string
is new to me, but looks super useful.
I've just redeployed TPS with this change. Thanks @ptrfrncsmrph! |
Description of the change
Relates to #118
This removes the session storage in
localStorage
and persists editor state in the URL usinglz-string
. 2d5c9bd adds a Share URL button (not actually a button, just a clickable<li>
) that copies the URL to clipboard, and gives feedback that the URL has successfully been copied to the clipboard.Example URL
For the default editor state of
the URL looks like http://localhost:8080/?purs=LYewJgrgNgpgBAWQIYEsB2cDuALGAnGAKEJWAAcQ8AXOABQKgjCJPMpoBEkqkA6AMRBQwSAEaw4ACgBmQsAEpWFanACi06TADGNSes07FpZTQAqeAJ60IBAMpa8KMruwBGADRxsAJk9lPVDAAHlSeUCgAzqFw6Mxo0eFoANaeBGjMeJ5a4DCKhMCoGABcRWoa2jQAqmgoVPmFcAC8hHBwaRlwACRwssItrXAA2l6uUoEhcABE5hZ0NjD2js4AhJOKA3B%2BY8E005ZwIBA0VLhwwUjksBFwojBQIJielHAOMNzwFod4B5hoq%2BsDTw%2BbYTSaqIIXMhXNb9VphSK6ApkHp4EDAcGQiTnS4wCIAuFebwg3a2bBIAhwACaXzgAGEcjCNltJONdgBBOZ2BxOGjSFASLRIDC3OD3JDMMAotFwADitQAEhBRFLgHAkHAAOYIg7fdUECgRWqUCy8OCmEBwCJkinZZhwCCGtAatWahGeQ2XWboLSMO0neBaqJwACSHBiGH9cEqACUADJq669e6YCJFRmA8NxXTIyS2%2BAsnZTVpUSy8MjzCLc5y8SgagD8gaojQAPI2YmAAHxreT4zZwHO9MCwjZDOCsqamU5tq2HYQvEDxBrWLlLGigSASNAXGCSybDpnzu0F0HIdDpkd98eTcMuvmb7e7-cZvPEqantBlmwRc8jgKF6-ZIu6DoM6nw2JyCxVjQea8HuF6tAAur2zJXualrWvAL7SKiqp6jABpGpYnjer68CRmQ3DYGOFqRhEXxaPAd7wEgiZCMmqY-sR6QwPEUg5i%2Bx67MWpblgQlarjWeD1lqJxKo0AD0zYPGg%2BAdop%2BogGpzaiHgQpaFRzw8BqWlMR2n54N%2BPbIf2UiDk%2BrTDKhpxMehs6SiK6rLpBq5wOu0DwFuwA7lM9l9gJV7vj%2BB5XvOQE1E6cBgd8XmLDyh4wLBoVIcOCH9Dg%2BBEK02FohiOJwAA3mOtSwO69HwAAvk0w6JEkUiTNgVBUGQqbyfJMnYEqvDZMA8mibiUHySWFhjeJPLyQUUT4PJPooDxVDydiUK4vJ17Nh2lp1fIr5UNVuTEK0m1XE1GzDJVJ1ULApSTGyUAajAOmoFocBcDwZrTbicG-gdNgMU9bIcKYETmd%2BT71cOnh3adT2xiAIDdYDB50SDMDI6j3XQxjAxwweiMPTjUwAPJkPg3CUDDF61djT1UzTVB0wTsPwxVVVk090baJQYD00DWN4KDUz89keBCxzF7ExmpOPRL2hfigC6EwSovi5MkuqwussjvLBKK%2BTkwcBaAByIA8Cd6tPozYum%2BbVs22rH6icLGxG32JtPaY-10lALERAD9vA47fv-bSQcRCHUMexrcDewjPNK5MMo8fgKBffQIAarpwDACBicO9rGcqY4WgG17XO%2B1MACKEDZ0ktK4FoSQl%2BH2uN83rfaEk1dEzlhBAA
Checklist: