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

feat(preferences): 2FA activation dialog with mocked state #605

Merged
merged 14 commits into from
Jul 27, 2021

Conversation

gorjan5sk
Copy link
Contributor

  • Implemented two factor authentication activation process in preferences
  • Uses hard-coded values for interface, can be completed successfully without providing secret key and auth code

@gorjan5sk gorjan5sk marked this pull request as ready for review July 22, 2021 15:34
@gorjan5sk gorjan5sk force-pushed the feature/2fa-dialog-flow branch from 4583127 to 0d3bfe5 Compare July 22, 2021 16:37
@gorjan5sk gorjan5sk force-pushed the feature/2fa-dialog-flow branch from 0d3bfe5 to 18fb0d1 Compare July 22, 2021 16:41
app/assets/javascripts/components/Button.tsx Outdated Show resolved Hide resolved
app/assets/javascripts/preferences/PreferencesView.tsx Outdated Show resolved Hide resolved
</Text>
</div>
<Switch
checked={auth.enabled !== false}
Copy link
Contributor

Choose a reason for hiding this comment

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

Can auth.enabled be anything different from a boolean?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. And yes, it doesn't look good :) I'm in the process of changing that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It should be better now. Commit 1f75df8.

@@ -0,0 +1,169 @@
import { action, makeAutoObservable, observable, untracked } from 'mobx';
Copy link
Contributor

Choose a reason for hiding this comment

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

These models are implemented using Mobx, any specific reason to keep them outside of the shared store?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, the single responsibility principle. I don't want to clutter the shared state with all possible states the Preferences and 2FA can be in.
Also, mobx observables don't need to be grouped in a single store. However, the reason it's not connected to it at all ATM is because the following PR is the one for integration with the actual activation.

Copy link
Contributor

Choose a reason for hiding this comment

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

You’d just add an extra domain store to the root store, so it would still be isolated enough that it wouldn’t be cluttered. The advantage of having all stores grouped in a root one is they can share references and interact with each other if they need to.

Copy link
Contributor Author

@gorjan5sk gorjan5sk Jul 26, 2021

Choose a reason for hiding this comment

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

I understand that advantage, but by exposing the whole state to any of the other components we are opening up to many bugs. If we share all the states it'd be pretty easy any component from anywhere to introduce an unexpected functionality. Additionally the outsider would have to handle all the possible states of the store that it uses, and it doesn't need to do that.
The simpler way, less bug-prone, would be to introduce a channel(method) towards the state that we want to access, and let the state handle the reception properly, in the exact place where we manage it! 😄

Copy link
Contributor

Choose a reason for hiding this comment

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

I’m not sure I agree tbh, I feel isolating each domain object from each other completely will make it difficult to share state between them in the future. So far I’ve found multiple domain stores grouped in a root store is a nice balance between having several stores each with their own responsibility but them being able to interact. I’d agree with this approach only if these models won’t need to have any interaction with the other stores, is this the case here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I’m not sure I agree tbh, I feel isolating each domain object from each other completely will make it difficult to share state between them in the future.

Not difficult at all. In fact, it's easier to reason about than handling all the combinations of a shared state in a variety of components. As I mentioned before:

  • If we want to trigger some event in the local state, we just implement a method which is called through an official channel instead of mutating the values directly.
  • If we want to make some properties readable by other components then we expose only those properties.

The alternative of a global shared state allows any component to change any state for any screen and that is a source of unpredictability. If multiple components alter and access the same property, there is no way of predicting what order the mutations will happen in, nor there is a way to guarantee that the changes will happen at the right moment.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It might even seem that it's more code, but it's not. Because we create an official channel to mutate the specific state, the outsider components need only call it, whereas if we mutate directly from those outsider components then they have to handle every intermediary state until the correct one is reached.
Not to mention that properly testing a several small states is much easier than testing one large shared state.

Copy link
Contributor

Choose a reason for hiding this comment

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

@gorjan5sk I'll give this approach a try, I think it'll make more sense to me when I start applying it 🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Cool! :) I suspect (but not sure) we'll see it in action in my next PR and we'll see how well it works in the current codebase.

navigator?.clipboard?.writeText(secretKey);
}}
/>
);
Copy link
Contributor

Choose a reason for hiding this comment

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

We could create a separate higher order component that returns receives icon and click handler and returns corresponding <IconButton /> component.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But, isn't that what IconButton already does?

Copy link
Contributor

Choose a reason for hiding this comment

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

I was referring to code duplication related to IconButton. For example, we have defined copy variable in 3 different places and only onClick handler is different there, so we could add abstraction over it. However, after looking into IconButton and its children, it seems introducing a new component will really not be so useful here.

@gorjan5sk gorjan5sk merged commit 84bb17b into develop Jul 27, 2021
@gorjan5sk gorjan5sk deleted the feature/2fa-dialog-flow branch July 27, 2021 09:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants