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

Desktop, Mobile: Add Joplin Cloud account information to configuration screen #10553

Merged
merged 19 commits into from
Jun 12, 2024
Merged

Desktop, Mobile: Add Joplin Cloud account information to configuration screen #10553

merged 19 commits into from
Jun 12, 2024

Conversation

pedr
Copy link
Collaborator

@pedr pedr commented Jun 6, 2024

I'm adding Joplin account information to the configuration screen that we have for Joplin Cloud. I'm including the email and account type fields and a link to Joplin Cloud profile.

For Desktop I tried to follow the website design and for mobile I tried to reuse components that are already being used inside the Settings screen.

For the mobile I used the addition of the feature to move all the Joplin Cloud component to its own file.

Desktop

image

Mobile

image

packages/app-desktop/gui/JoplinCloudConfigScreen.tsx Outdated Show resolved Hide resolved
website={this.props.settings['sync.10.website']}
styles={this.styles()}
/>,
[emailToNoteDescription(), emailToNoteLabel(), accountEmailLabel(), accountTypeLabel(), accountInformationLabel()],
Copy link
Collaborator

Choose a reason for hiding this comment

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

This way of adding search text for custom components may need to be refactored at some point (though not in this pull request :) ).

One option could be to create a custom <SearchableText>...</SearchableText> component that sends search text to the settings screen with a custom React context and provider.

Copy link
Owner

Choose a reason for hiding this comment

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

For now let's put only emailToNoteLabel() and emailToNoteDescription() in there

@personalizedrefrigerator
Copy link
Collaborator

Creating as a draft because I haven't decided how to refactor accountTypeToString. Right now this function is sitting inside UserModel in the server package, specially because AccountType, enum required by it, is used in a lot of places.

It looks like @joplin/lib/utils/joplinCloud.ts already has similar functionality. However, its accountType seems to have minimum 1 and maximum 3... It could still make sense to put this enum and accountTypeToString in a similar location, though.

@pedr
Copy link
Collaborator Author

pedr commented Jun 7, 2024

I updated the PR, thanks for the review, Henry!

@pedr pedr marked this pull request as ready for review June 7, 2024 12:02
Copy link
Collaborator

@personalizedrefrigerator personalizedrefrigerator left a comment

Choose a reason for hiding this comment

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

I like that JoplinCloudConfig is now its own component — that should make the mobile settings screen easier to maintain.

I've left a few comments.

</View>
<View style={props.styles.styleSheet.settingContainerNoBottomBorder}>
<Text style={props.styles.styleSheet.settingText}>{accountTypeLabel()}</Text>
<Text style={props.styles.styleSheet.settingTextEmphasis}>{accountTypeToString(parseInt(props.accountType, 10))}</Text>
Copy link
Collaborator

Choose a reason for hiding this comment

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

🚧 Possible crash 🚧: Be careful here! Currently, accountTypeToString throws when the account type is unknown. If we eventually add a new account type, this might cause the app to crash if users with this account type open the JoplinCloud config screen on an old client.

Consider including a check that accountType actually is a valid account type, and displaying 'Unknown' if not.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I added a function that tries to get the value, if it fails it logs the error message and returns 'Unknown', I guess that works, right?

@pedr
Copy link
Collaborator Author

pedr commented Jun 10, 2024

  • Make sure the text is selectable

Comment on lines +24 to +26
const goToJoplinCloudProfile = async () => {
await bridge().openExternal(`${props.joplinCloudWebsite}/users/me`);
};
Copy link
Owner

Choose a reason for hiding this comment

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

We should probably default to using useCallback for event handlers, especially when it's dependent on a prop like here. Probably the prop won't change but if for some reason it's initially empty, then set to a value, the component will incorrectly use the empty value

Copy link
Collaborator

Choose a reason for hiding this comment

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

but if for some reason it's initially empty, then set to a value, the component will incorrectly use the empty value

I'm not sure that's true... I think useCallback prevents unnecessary re-renders of child components (rather than forcing the function to reload). More specifically, suppose that props.joplinCloudWebsite changes from "" to "https://joplincloud.com/":

  1. props.joplinCloudWebsite is initially "". This is the initial render.
    • goToJoplinCloudProfile is set to a function. props.joplinCloudWebsite is "" in its scope. As such, if goToJoplinCloudProfile is called, it will openExternal('/users/me').
    • The component renders. A Button is given goToJoplinCloudProfile as a prop. This is the first render of that Button.
  2. props.joplinCloudWebsite changes to "https://joplincloud.com/". This causes the component to re-render.
    • goToJoplinCloudProfile is set to a function. props.joplinCloudWebsite is "https://joplincloud.com/" in its scope. As such, if goToJoplinCloudProfile is called, it will openExternal('https://joplincloud.com//users/me').
    • The component renders. A Button is given goToJoplinCloudProfile as a prop. This prop is different from its previous value (because function(){} !== function(){}). As a result, the Button re-renders.
      • This re-render means that the button will use the new version of goToJoplinCloudProfile.

Observe that in step 2, because React considers different functions to be unequal, the Button uses the new version of the function that includes "https://joplincloud.com/" for props.joplinCloudWebsite in its scope.

Also see react.dev's "Should you add useCallback everywhere".

Copy link
Owner

Choose a reason for hiding this comment

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

Ah yes, that's true, it will re-render anyway.

But I actually think that it's reasonable to default to adding useCallback rather than wondering whether it needs to be optimised or not. In this case maybe it doesn't make a difference that it re-render even when the prop doesn't change, but maybe it does, who knows. That's the idea of mostly defaulting to using useCallback - then we don't need to wonder about this, and it feels like it's more future proof

Comment on lines 15 to 19
margin-bottom: calc(var(--joplin-font-size) * 3);

table {
margin-bottom: calc(var(--joplin-font-size) * 1.5);
border: none;
Copy link
Owner

Choose a reason for hiding this comment

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

Is it possible to avoid these CSS calculations? As I think that will make it difficult to maintain a consistent style throughout the application. We have the --joplin-margin - could that be enough? Or if something else is needed maybe introduce a new margin?

@pedr
Copy link
Collaborator Author

pedr commented Jun 11, 2024

I updated the PR and the screenshots, thanks for the review!

Comment on lines 1 to 6
enum AccountType {
Default = 0,
Basic = 1,
Pro = 2,
Team = 3,
}
Copy link
Owner

Choose a reason for hiding this comment

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

This AccountType could be used elsewhere, including in joplinCloud.ts so I think this file should be named more generically. Maybe just types.ts.

Even accountTypeToString is type-related since it converts from one type to another

@laurent22 laurent22 merged commit 889c395 into laurent22:dev Jun 12, 2024
10 checks passed
@pedr pedr deleted the add-joplin-cloud-info-to-config branch June 12, 2024 17:51
@laurent22
Copy link
Owner

laurent22 commented Jun 14, 2024

@pedr, I just looked at the Joplin Cloud screen on mobile and it looks different from all the other screen. Was there any reason why it wasn't implemented like, for example, the "Tools" or "Import and Export" screens? It feels like it wasn't necessary to create a bespoke screen for this unless I'm missing something. The reason we prefer all screens to look the same is that it makes it a lot easier to update the design, which we want to do some day.

@pedr
Copy link
Collaborator Author

pedr commented Jun 19, 2024

@pedr, I just looked at the Joplin Cloud screen on mobile and it looks different from all the other screen. Was there any reason why it wasn't implemented like, for example, the "Tools" or "Import and Export" screens? It feels like it wasn't necessary to create a bespoke screen for this unless I'm missing something. The reason we prefer all screens to look the same is that it makes it a lot easier to update the design, which we want to do some day.

I can see what you mean about having a screen that is implemented differently, and I could try to refactor the content out of the component to the ConfigScreen, but, for me, this screen was closer to the Plugins rather than Tools, where we wouldn't only show buttons and links, but also display information in a unique way (in this case the Account Information).

When implementing I tried to find in the config screen another place where we had a pair of key-value that was shown like the Account type: Default, since I couldn't find it I went with my idea on how to display it.

But even if I move the content of the screen out of the component to the ConfigScreenwe will not have anything similar to what we can do for Tools or Import and Export screen since for displaying the Account Information we need some customization.

@laurent22
Copy link
Owner

Maybe the addSettingText function can help with this? Or create a new type of control that just display a label? In some case it's hard to avoid creating a new screen, like indeed for plugins, but for relatively simple screens it would be nice to find a uniform way to do them.

@pedr
Copy link
Collaborator Author

pedr commented Jun 19, 2024

Can I create an issue to refactor this so we don't forget about it?

@laurent22
Copy link
Owner

Yes please

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