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: Show notification on import/export using interop service #10520

Open
wants to merge 10 commits into
base: dev
Choose a base branch
from

Conversation

fabiogvdneto
Copy link
Contributor

Summary

When importing/exporting items in the desktop app, a giant model layer covers all our screen, and the users are forced to wait until the process is completed. This raised our attention and the need of implementing something that would let the users do their things, while still being able to know about when the process is happening and when it is finished.

One possible approach to this would be using the notyf library, which right now is only used by the TrashNotification component, and show a simple and minimal notification indicating the state of the process, instead of covering the entire screen with a modal layer. We truly believe that this would drastically increase the user experience.

In terms of implementation, we focused ourselves on creating a notification that stayed during the whole process, dismissing it when it's over, adding a successful notification if everything went well. For further use of other developers we created the notification based on operations, and different phases of the loading processes.

Before this feature was implemented:
Xnip2024-04-26_13-58-08

Notifications after the feature was implemented:
Screencast from 05-29-2024 10 43 41 PM.webm

Testing Plan

The manual testing steps are as shown in the attached video.

Resolves #9879

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.

Thank you for working on this!

To facilitate discussion, I'm attaching a screenshot of the new UI from the linked video:
screenshot: Blue notification near the bottom of the screen

Note

It's recommended to discuss large changes on the Joplin Forum before creating a pull request, particularly for UI-related changes. This helps gather community feedback (if any) and can help determine whether a feature/change could be accepted.

Edit: There are CI errors:

  Error: ➤ YN0000: │ root@workspace:. STDERR ➤ YN0000: [@joplin/app-desktop]: gui/MenuBar.tsx(295,7): error TS7034: Variable 'path' implicitly has type 'any' in some locations where its type cannot be determined.
  Error: ➤ YN0000: │ root@workspace:. STDERR ➤ YN0000: [@joplin/app-desktop]: gui/MenuBar.tsx(323,12): error TS7005: Variable 'path' implicitly has an 'any' type.

packages/app-desktop/gui/MenuBar.tsx Outdated Show resolved Hide resolved
packages/app-desktop/gui/MenuBar.tsx Outdated Show resolved Hide resolved
packages/app-desktop/app.reducer.ts Outdated Show resolved Hide resolved
@personalizedrefrigerator personalizedrefrigerator added enhancement Feature requests and code enhancements desktop All desktop platforms import Related to importing files such as ENEX, JEX, etc. labels May 30, 2024
fabiogvdneto and others added 2 commits June 3, 2024 22:04
When importing/exporting items in the desktop app, a giant
model layer covers all our screen, and the users are forced
to wait until the process is completed. This raised our
attention and the need of implementing something that would let
the users do their things, while still being able to know about
when the process is happening and when it is finished.

We though and decided that one possible solution would be using
the notyf library, which right now is only used by the
TrashNotification component, and show a simple and minimal
notification indicating the state of the process, instead of
covering the entire screen with a modal layer. We truly
believe that this would drastically increase the user
experience.

Co-authored-by: Henrique Silva <[email protected]>
@fabiogvdneto fabiogvdneto force-pushed the interop-service-notifications branch from 333d610 to 0380e09 Compare June 3, 2024 21:05
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've left a few comments.

Notes:

  • TaskProgressUIService could be used in the future to resolve Implement a plugin API to display a progress bar #10527 (this would be a separate PR or commit).
  • I'm not sure importOptions.onProgress provides enough information in status: any to show a progress indicator (the total number of notes is unknown).
    • See comment below.

Thank you for working on this!

Comment on lines 145 to 152
display: inline-block;
width: 15px;
height: 15px;
position: relative;

border: 3px solid var(--joplin-background-color, white);
border-top-color: var(--joplin-color, black);
border-radius: 10px;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
display: inline-block;
width: 15px;
height: 15px;
position: relative;
border: 3px solid var(--joplin-background-color, white);
border-top-color: var(--joplin-color, black);
border-radius: 10px;
display: inline-block;
width: 15px;
height: 15px;
position: relative;
border: 3px solid var(--joplin-background-color, white);
border-top-color: var(--joplin-color, black);
border-radius: 10px;


.loading-spinner.-progress {
width: 20px;
height: 20px;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
height: 20px;
height: 20px;

Comment on lines 177 to 184
.loading-spinner.-indeterminate {
animation: 4s linear infinite rotate;
}

@media (prefers-reduced-motion) {
.loading-spinner.-indeterminate { animation: none; }
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
.loading-spinner.-indeterminate {
animation: 4s linear infinite rotate;
}
@media (prefers-reduced-motion) {
.loading-spinner.-indeterminate { animation: none; }
}
.loading-spinner.-indeterminate {
animation: 4s linear infinite rotate;
}
@media (prefers-reduced-motion) {
.loading-spinner.-indeterminate { animation: none; }
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

Consider moving this file to packages/lib/services/ for consistency with existing code.

// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const errors: any[] = [];

const taskId = `interop-import-${path}}`;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
const taskId = `interop-import-${path}}`;
const taskId = `interop-import-${path}`;

Comment on lines 321 to 322
onProgress: (_status: any) => {
TaskProgressUIService.onTaskProgress(taskId, _status);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Looking at the Evernote importer, it seems that 1) status is an object with a created property 2) the total number of notes to import is unknown until import completes.

For now, it might be better to show indeterminate progress for import tasks. Supporting onProgress would still be useful for exporting (example) and #10527.

@fabiogvdneto fabiogvdneto force-pushed the interop-service-notifications branch from e459bc7 to c1de532 Compare June 21, 2024 19:58
@fabiogvdneto
Copy link
Contributor Author

fabiogvdneto commented Jun 21, 2024

Thank you for your help! We did what you requested above and now we also have a fully functional circular progress bar. I made a video so that you can see how it works rn. The video is just a demo of how the progress bar behaves when calling TaskUIService#onTaskProgress(). In the last commit, when importing/exporting something, the animation continues until the task is completed.

notifications.mp4

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.

Thank you for working on this!

I've reviewed the recent changes and left comments. Based on the code, this pull request seems almost ready to merge!

Comment on lines 919 to 920
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
dispatch={this.props.dispatch as any}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
dispatch={this.props.dispatch as any}
dispatch={this.props.dispatch}

Would it be possible to remove the as any cast? If not, consider updating the -- Old code before rule was applied to describe why the cast is necessary.

Comment on lines 24 to 32
if (type.type === 'success') {
// When type === 'success', type.icon is always of type object (INotyfIcon):
// https://github.com/caroso1222/notyf/blob/master/src/notyf.options.ts
(type.icon as INotyfIcon).color = theme.backgroundColor5;
} else if (type.type === 'error') {
// When type === 'error', type.icon is always of type object (INotyfIcon):
// https://github.com/caroso1222/notyf/blob/master/src/notyf.options.ts
(type.icon as INotyfIcon).color = theme.backgroundColor5;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if (type.type === 'success') {
// When type === 'success', type.icon is always of type object (INotyfIcon):
// https://github.com/caroso1222/notyf/blob/master/src/notyf.options.ts
(type.icon as INotyfIcon).color = theme.backgroundColor5;
} else if (type.type === 'error') {
// When type === 'error', type.icon is always of type object (INotyfIcon):
// https://github.com/caroso1222/notyf/blob/master/src/notyf.options.ts
(type.icon as INotyfIcon).color = theme.backgroundColor5;
}
if (type.type === 'success' || type.type === 'error') {
// When type is 'success' or 'error', type.icon is always of type object (INotyfIcon):
// https://github.com/caroso1222/notyf/blob/master/src/notyf.options.ts
(type.icon as INotyfIcon).color = theme.backgroundColor5;
}

It might be possible to combine these branches.

message: task.message,
duration: 0,
dismissible: true,
icon: `<i class="${className}" id="${task.id}" style="${cssStyle}"></i>`,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Consider escaping cssStyle and task.id. This should prevent future changes to cssStyle that include "s or task IDs with " from breaking the notification.

This escaping can, for example, be done with htmlentities from @joplin/utils/html:

Suggested change
icon: `<i class="${className}" id="${task.id}" style="${cssStyle}"></i>`,
icon: `<i class="${className}" id="${htmlentities(task.id)}" style="${htmlentities(cssStyle)}"></i>`,

Also see https://joplinapp.org/help/dev/coding_style#to-insert-into-an-html-string.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
desktop All desktop platforms enhancement Feature requests and code enhancements import Related to importing files such as ENEX, JEX, etc.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Closing and opening settings dismisses "exporting" and "importing" screens
3 participants