Skip to content
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

WiFi dialog: add dialog to the UI #1813

Merged
merged 56 commits into from
Jul 16, 2024
Merged

Conversation

jotaen4tinypilot
Copy link
Contributor

@jotaen4tinypilot jotaen4tinypilot commented Jul 1, 2024

Related #131, part (d). Stacked on #1812.

This PR adds a new dialog “Wi-Fi” to the “System→Network” menu, which makes the Wi-Fi feature available to end-users.

Summary of the discussion below:

  • We made the wording so that it’s clear that we only store the WiFi credentials, but we don’t verify whether they are actually accepted by the WiFi router, or whether the WiFi is reachable at all. For further clarification, we also added a success state to the dialog.
  • In a subsequent PR, we’ll turn the “country code” free text field into a dropdown, to make it easier for users to enter their correct and applicable value.

Review on CodeApprove

@jotaen4tinypilot
Copy link
Contributor Author

jotaen4tinypilot commented Jul 4, 2024

@cghague Thanks for your feedback!

  • Wording of Intro Text: Changed
  • SSID Field Label: for space efficiency, I went with Network Name for the input field label, and used Wi-Fi SSID as placeholder.
  • Country Code Field: that sounds sensible – I’ll make it a dropdown via a subsequent PR (this one is already pretty large). I’ve added it as subtask (g) to the main issue.
  • “No Ethernet” Warning Message: Changed
  • Wi-Fi vs WiFi: I’ve changed it to Wi-Fi in the UI, however I suggest to leave the internal code occurrences as they are, to avoid code churn. I left a code comment about this decision, similar to the note in the mass storage / virtual media dialog.
Screenshot 2024-07-04 at 11 51 16

Copy link
Contributor

@jdeanwallace jdeanwallace left a comment

Choose a reason for hiding this comment

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

Automated comment from CodeApprove ➜

In: Discussion
Edge case scenario:

  1. Device is connected via wifi only
  2. User reads warning message about only being connected via wifi
  3. User connects ethernet cable to device
  4. User opens wifi dialog and disables wifi
  5. User reopens wifi dialog
  6. Web UI hangs and struggles to connect to device

Video demo:
Screen Recording 2024-07-04 at 14.41.19.mov

I think the main issue with this scenario is that even though we're able to detect network status of the ethernet and wifi connection, we don't know for sure which connection we're currently using.


In: app/templates/custom-elements/wifi-dialog.html:

> Line 66
      device using Wi-Fi. If you change your settings, you might get

Should we stress that they are only connected using wifi?


In: app/templates/custom-elements/wifi-dialog.html:

> Line 132
  </div>

While testing on device, I was expecting a success page, but after apply the new wifi settings the dialog seemingly just closed.

In reality, the "Applying Changes" screen just flashed really quickly then closed.

Can we add a success/done screen like we have after an update has completed?


In: app/templates/custom-elements/wifi-dialog.html:

> Line 231
          const networkStatus = await getNetworkStatus();

Can we catch fatal network errors in this method?

I experienced an infinite loading screen after changing the wifi settings then reopening the wifi dialog:

(failed)net::ERR_TIMED_OUT

Screen Shot 2024-07-04 at 13.44.15.png


In: app/templates/custom-elements/wifi-dialog.html:

> Line 253
          });

We can also use Array.some for this, similar to what we did in video-settings-dialog:

// Save Button: only enable if the user actually changed some value.
const hasChangedValues = [
[this._getStreamingMode(), this._initialSettings.streamingMode],
[this._getFrameRate(), this._initialSettings.frameRate],
[this._getMjpegQuality(), this._initialSettings.mjpegQuality],
[this._getH264Bitrate(), this._initialSettings.h264Bitrate],
[h264StunServer, this._initialSettings.h264StunServer],
[h264StunPort, this._initialSettings.h264StunPort],
].some(([actualValue, initialValue]) => actualValue !== initialValue);
this._elements.saveButton.disabled = !hasChangedValues;


👀 @jotaen4tinypilot it's your turn please take a look

Copy link
Contributor Author

@jotaen4tinypilot jotaen4tinypilot left a comment

Choose a reason for hiding this comment

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

Automated comment from CodeApprove ➜

In: Discussion
Yeah, I’m not sure we can really do something about this, but I’m also wondering whether we have to. My thinking is that – as a user – if I make changes to the network setup, then it’s somewhat expected that the browser connection may glitch or be interrupted for a while, and I’d probably naively try to reload the page until the connection is stable again.

I think this issue should also be alleviated by us guarding the dialog initialization in a try/catch (your other review comment), because then at least the dialog would error out once the timeout kicks in.


In: app/templates/custom-elements/wifi-dialog.html:
Yes, I’ve wrapped both network calls into a try/catch.


In: app/templates/custom-elements/wifi-dialog.html:
Ah right, that’s nicer – done.


In: app/templates/custom-elements/wifi-dialog.html:

> Line 66
      device using Wi-Fi. If you change your settings, you might get

To me, the current phrasing would be clear, but I see your point that we might want to consider over-emphasizing this, to make sure the user really understands the situation. @cghague could you weigh in here?


In: app/templates/custom-elements/wifi-dialog.html:

> Line 132
  </div>

I was also debating this, but I’m actually not sure here. As far as I know, the update dialog is the only dialog where we provide a dedicated success state, but we don’t have such a state in any other transactional dialog that I can think of:

  • The video settings dialog just closes after the operation went through
  • The hostname dialog and the static IP dialog implicitly close, due to the redirect kicking in.
    • The static IP dialog at least displays a short success note for a brief moment before the redirect, but it’s actually a combination of a success message and an (auto-)confirm prompt.

To me, it would feel slightly more consistent right now to just close the WiFi dialog. Another point to consider would be that if we display a success state, then we need to make sure to communicate that we only successfully stored the WiFi settings, but that doesn’t mean that the WiFi connection is active now.

I’m still open to a success state, I just wanted to raise the above points for consideration.

Regarding the flickering, since we know that the enable/disable operations are rather short (due to the “fire and forget” mode), we could also consider to optimistically omit the loading state altogether.

Just for the records: related issue are #1639, #714 (comment).


👀 @jdeanwallace it's your turn please take a look

Copy link
Contributor

@jdeanwallace jdeanwallace left a comment

Choose a reason for hiding this comment

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

Automated comment from CodeApprove ➜

In: Discussion
Yeah this reminds me of the risk of setting a static IP (i.e., the user may lose connection to their device). For setting a static IP, we went through a lot of effort to make sure settings were reverted if they were incorrect. Are we sure we don't want to do the same here?

If the user doesn't have physical access to their device, then the risk feels similar to that of setting a static IP. However, if the user does have physical access then simply connecting an ethernet cable will resolve the issue.


In: app/templates/custom-elements/wifi-dialog.html:

> Line 132
  </div>

...we need to make sure to communicate that we only successfully stored the WiFi settings, but that doesn’t mean that the WiFi connection is active now.

Yeah I think this is an important point. I felt that setting wifi credentials without knowing if the settings were correct (either by visually confirming that the password I typed was correct or the system giving me feedback that the credentials were correct) was slightly strange. No feedback makes me think the credential were correct, so I think perhaps we should make it clear that we don't actually verify their credentials, in our help text.

Regarding the flickering, since we know that the enable/disable operations are rather short (due to the “fire and forget” mode), we could also consider to optimistically omit the loading state altogether.

Yeah I think this can work. Without a results screen, a loading state seems pointless.


In: app/templates/custom-elements/wifi-dialog.html:

> Line 225
            networkStatus = await getNetworkStatus();

(nit) Can we do this in parallel using Promise.all?


👀 @jotaen4tinypilot it's your turn please take a look

Copy link
Contributor Author

@jotaen4tinypilot jotaen4tinypilot left a comment

Choose a reason for hiding this comment

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

Automated comment from CodeApprove ➜

In: Discussion

Are we sure we don't want to do the same here?

We briefly discussed this question, and I think the situation with WiFi is slightly different than with static IP. While the user may lock themselves out with both features, the implications are slightly different:

  • For fixing WiFi credentials, the user can plug in an Ethernet cable to regain access. This should be an obvious fix, because the user already needed an Ethernet cable to configure WiFi in the first place. So Ethernet is kind of the base case for connecting to the device, and the WiFi connection is kind of “optional”/additional.
  • For fixing Static IP (as far as I know) you’d have to mount the SD card somewhere, and then manually revert the DHCP config via the file system. This is probably not obvious to most users, and it’s rather difficult to accomplish. It also may not be obvious that you may lock yourself out to begin with (i.e., without any fallback) when setting a Static IP. In that sense, the Static IP setting is not optional, but it’s more like “either it works, or you are in trouble” (at least seemingly).

The other aspect is that it’s legitimate for users to enter WiFi credentials ahead of time, e.g. without the respective WiFi network being reachable. In this case, it would be undesired for them if we just reverted the WiFi settings. I think this use-case will also become more apparent, if we’d decide to allow storing multiple WiFi connections. In this case, it may be even more likely that the user might want to manage their WiFi settings without being in reach of the respective networks. So if we’d issue a check of the credentials, we probably have to make it either optional or subtle enough that it also satisfies this use-case. We created #1814 to track this issue separately.


In: app/templates/custom-elements/wifi-dialog.html:
Sure!


In: app/templates/custom-elements/wifi-dialog.html:

> Line 132
  </div>

No feedback makes me think the credential were correct, so I think perhaps we should make it clear that we don't actually verify their credentials, in our help text.

I agree that it’s not optimal to not provide any feedback at all at the moment. We are currently discussing the “feedback” topic and exploring UI options in #1814.

The tricky thing with WiFi is that the user may decide to preconfigure their WiFi credentials ahead of time (while not being in reach of the WiFi yet), or that the WiFi network might be unavailable at config-time for other reasons. So I think we cannot trivially yield an error saying “your WiFi credentials are incorrect”, because we still can store the WiFi settings and use them for trying to connect.

So the process of enabling WiFi is actually two-fold: (a) storing and enabling the WiFi settings and (b) successfully connecting to the WiFi network. So maybe we can consider to clarify this distinction a bit better, to make the workings of the WiFi feature in that regard more transparent. If we’d wanted to do that, I’d currently see two options (UI mock-ups only for demo purposes, not meant as literal suggestion):

This also may be a question for @cghague or @shalver-tp to weigh in.


👀 @jdeanwallace it's your turn please take a look

Copy link
Contributor

@cghague cghague left a comment

Choose a reason for hiding this comment

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

Automated comment from CodeApprove ➜

In: app/templates/custom-elements/wifi-dialog.html:

> Line 66
      device using Wi-Fi. If you change your settings, you might get

The only situation when a user would be connected using "only" Wi-Fi is if they're using the access point mode, which could make this new wording misleading. I can also foresee the new wording confusing users using services like Tailscale. I recommend leaving this as it is.


In: app/templates/custom-elements/wifi-dialog.html:

> Line 132
  </div>

Great discussion! I've read your comments, looked at the mockups, and reviewed the associated issues. I think the confusion comes from treating this as a tool to connect to a network when it's more accurately a tool for storing network credentials.

I, therefore, suggest changing the button labels to "Remove" and "Save" and updating the header to "Wi-Fi Credentials". This change will make what the feature does more obvious while keeping it recognizable and understandable. Most users will enter their credentials and connect immediately, but it will still be clear what's happening for those preconfiguring a device.

If we couple the above with @jotaen4tinypilot's "success state" mockup, there would be no ambiguity, and we'll have covered both use cases. I appreciate that we don't have a "success state" anywhere else, but similar scenarios are likely as we add more features, so this may simply be the first!


👀 @jdeanwallace, @jotaen4tinypilot it's your turn please take a look

Copy link
Contributor Author

@jotaen4tinypilot jotaen4tinypilot left a comment

Choose a reason for hiding this comment

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

Automated comment from CodeApprove ➜

In: app/templates/custom-elements/wifi-dialog.html:

> Line 134
  </div>

I’ve updated the wording and added in a success state, the flow now looks like this:

Screen Recording 2024-07-05 at 07.24.03.mov

@cghague could you do a final double-check of the wording of all texts, particularly the new success state ones? Shall we also make an adjustment to the initial intro text, to emphasize that we only store the credentials but don’t check whether they are sound?


👀 @jdeanwallace it's your turn please take a look

jotaen4tinypilot added a commit that referenced this pull request Jul 5, 2024
Related #131, part (c).
Stacked on #1811.

This PR adds the backend logic and endpoints that we need for the WiFi
dialog.

- There are endpoints for reading, writing, and deleting the WiFi
configuration. There is also one endpoint for checking the status of the
network connectivity, which allows us to display warnings or status
indicators in the frontend.
- As [mentioned in the code
comment](https://github.com/tiny-pilot/tinypilot/pull/1812/files#diff-43d8494a595e7d1f838bc01d5f2c6b18eebf61ca63bc3bbf2837dbcb27a8e109R62-R63),
we cannot read the `wpa_supplicant.conf` file directly due to file
ownership. Therefore, we use the [new `print-marker-sections` privileged
script](#1811) from the
previous PR.
- The scripts for enabling and disabling are executed in a “fire and
forget” manner, otherwise it may happen that the frontend requests hang
due to e.g. a change/interruption in the network (which would result in
an eternal loading spinner). As we only write a config file and trigger
service/daemon restarts, we wouldn’t get direct feedback about errors
anyway, but we’d have to poll for the resulting status, which can take
quite long, though.
- Note that we also can’t really verify the WiFi credentials, because
the desired WiFi might not be reachable at the time where the user wants
to enter the settings (e.g., because they want to enter the config ahead
of time).
- The validation of the incoming request data is relatively important,
because we will show the error messages in the frontend for input
validation. So we need to make sure that we detect all relevant
potential issues. The validation errors would be user-facing.

This PR can probably be tested best via the [subsequent frontend
PR](#1813) – that one isn’t
100% code-complete yet, but it’s pretty close in terms of functionality.
<a data-ca-tag
href="https://codeapprove.com/pr/tiny-pilot/tinypilot/1812"><img
src="https://codeapprove.com/external/github-tag-allbg.png" alt="Review
on CodeApprove" /></a>

---------

Co-authored-by: Jan Heuermann <[email protected]>
Base automatically changed from wifi-dialog/3-backend to master July 5, 2024 05:52
Copy link
Contributor

@jdeanwallace jdeanwallace left a comment

Choose a reason for hiding this comment

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

Automated comment from CodeApprove ➜

⏳ Approval Pending (2 unresolved comments)
Approval will be granted automatically when all comments are resolved

Nice work!


In: Discussion
Thanks for explanation and links. I'm glad the risks have been brought up previously and accepted. I'm happy to proceed as is.


In: app/templates/custom-elements/wifi-dialog.html:

> Line 134
  </div>

The updated flow looks great to me, thanks!

You can resolve this discussion when ready.


👀 @jotaen4tinypilot it's your turn please take a look

cghague
cghague previously requested changes Jul 5, 2024
Copy link
Contributor

@cghague cghague left a comment

Choose a reason for hiding this comment

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

Automated comment from CodeApprove ➜

⏳ Approval Pending (4 unresolved comments)
Approval will be granted automatically when all comments are resolved

LGTM


In: app/templates/custom-elements/wifi-dialog.html:

> Line 134
  </div>

Thanks, @jotaen4tinypilot; the new flow looks excellent! I've added two review comments with suggested wording. I recommend leaving the initial introduction text as it is, as too much detail on the process here might confuse users. I have some ideas for improving debug logs to track failed connection attempts, which should remove some ambiguity without overwhelming the interface. I'll create issues for the new ideas soon.


In: app/templates/custom-elements/wifi-dialog.html:

> Line 139
      Your Wi-Fi credentials have been saved. Your TinyPilot device will now

Suggest:

Your Wi-Fi credentials have been saved. When your TinyPilot device is in range of the wireless network, it will automatically try to connect to it.

In: app/templates/custom-elements/wifi-dialog.html:

> Line 148
    <p>Your Wi-Fi credentials have been removed.</p>

Suggest:

Your Wi-Fi credentials have been removed. Your TinyPilot device will no longer try to connect to the wireless network.

👀 @jdeanwallace, @jotaen4tinypilot it's your turn please take a look

Copy link
Contributor Author

@jotaen4tinypilot jotaen4tinypilot left a comment

Choose a reason for hiding this comment

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

Automated comment from CodeApprove ➜

In: app/templates/custom-elements/wifi-dialog.html:
Resolved


In: app/templates/custom-elements/wifi-dialog.html:
Resolved


In: app/templates/custom-elements/wifi-dialog.html:

> Line 70
      device using Wi-Fi. If you change your settings, you might get

Resolved


In: app/templates/custom-elements/wifi-dialog.html:

> Line 134
  </div>

Alright, awesome, I’ve taken over the new wording!

Copy link
Contributor

@jdeanwallace jdeanwallace left a comment

Choose a reason for hiding this comment

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

Automated comment from CodeApprove ➜

Approved: I have approved this change on CodeApprove and all of my comments have been resolved.

Copy link
Contributor

@cghague cghague left a comment

Choose a reason for hiding this comment

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

Automated comment from CodeApprove ➜

Approved: I have approved this change on CodeApprove and all of my comments have been resolved.

@jdeanwallace jdeanwallace dismissed their stale review July 16, 2024 13:05

Review approved on CodeApprove

@cghague cghague dismissed their stale review July 16, 2024 13:05

Review approved on CodeApprove

@jotaen4tinypilot jotaen4tinypilot merged commit efe7ac6 into master Jul 16, 2024
13 checks passed
@jotaen4tinypilot jotaen4tinypilot deleted the wifi-dialog/4-frontend branch July 16, 2024 13:22
jotaen4tinypilot added a commit that referenced this pull request Jul 16, 2024
Related #131, part (h).
Stacked on #1813.

We didn’t have a `<select>` field in the style guide yet, and we also
were missing the general CSS for it. We’ll need this for [turning the
“Country Code” input field into a
select-field](#1820), as
[discussed in the previous
PR](#1813 (comment)).

Once we merged this into Pro, we can also refactor [the inlined CSS of
the `<select>` field in the static IP
dialog](https://github.com/tiny-pilot/tinypilot-pro/blob/f806639d5124e7edad8b9a44a727ecf49f73ed54/app/templates/custom-elements/static-ip-dialog.html#L38-L46).

<img width="840" alt="Screenshot 2024-07-05 at 08 34 39"
src="https://github.com/tiny-pilot/tinypilot/assets/83721279/c49b6a94-d114-495b-9dcb-ba32eb30029d">

<a data-ca-tag
href="https://codeapprove.com/pr/tiny-pilot/tinypilot/1819"><img
src="https://codeapprove.com/external/github-tag-allbg.png" alt="Review
on CodeApprove" /></a>

---------

Co-authored-by: Jan Heuermann <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants