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

Migrate react-scripts to vite #696

Open
wants to merge 32 commits into
base: master
Choose a base branch
from

Conversation

mfechner
Copy link

Fixes #695

To test this modification:

(cd ui && yarn)
(cd ui && yarn build)
go run .
(cd ui && yarn start)

Now access the address yarn start shows you.

@mfechner mfechner mentioned this pull request Oct 15, 2024
@mfechner
Copy link
Author

Ok, to sum it up.
The migration to vite is more or less done.
But it seems that state management is broken, after a login the message component is not loaded.

I tried to update the router, but that is even worse. They broke the complete API and do not even provide a migration script for it.

As I mainly develop in Angular I need to read about react before I continue, but it seems that this upgrade to vite is depending on updating all libs to a more current version.

If someone else here have a strategy how to update the ui, any feedback is welcome.
I understand now the comment from @jmattheis that this will not be an easy task ;)

@jmattheis
Copy link
Member

@mfechner I feel you (:, I think the only good way it to start from scratch and copy component by component into a new project, so that you don't have to fix 100 errors to get something working. It's on my todo list, sadly it's multi hour effort.

@mfechner
Copy link
Author

@jmattheis thanks a lot for your feedback. I currently study react to better understand react itself.
It's to early to really make a statement here, but why you do not use the standard state management of react. That would help to get rid of a third party lib and would also simplify upgrade in the future.

I'm on this task but it will take some time ;)

@jmattheis
Copy link
Member

The codebase is quite old, pre react hooks. In my experience it wasn't that ergonomic to use contexts for global state, so I've used a lib. Currently I like https://github.com/pmndrs/zustand for it's simplicity and small footprint.

@mfechner
Copy link
Author

I started now with a rewrite, but this will be a massiv change.
Steps I plan to do:

  • rewrite all class based components as functional components to be able to use all hook
  • upgrade react in first step to 18.3.1 and later to 19
  • upgrade material to v6
  • replace state management with @reduxjs/toolkit and react-redux
  • upgrade react-router to 7
  • use vite 6

I think it will be nearly impossible to get this changes sliced into small commit, as all steps link somehow together.
Maybe later try to get also rid of not needed third party libs like axios. The fetch API is normally more than enough nowadays.

I update you, if I have something to show, but it will for sure take some days maybe some weeks.

@mfechner mfechner force-pushed the react_scripts_to_vite branch from f296d85 to 1bf4902 Compare January 1, 2025 12:44
In DEV mode, you just do:
go run .
(cd ui && yarn start)
In dev mode there is a proxy in vite used to not have CORS problem.
You can access the UI on:
http://localhost:5173
The UI prefix every request to the backup with `/api` so the proxy can correctly handle it, the proxy strips the `/api` so the backend is equal to production mode.
changing (observed) observable values without using an action
Rewrite all components to functional component using hooks
Upgrade react-router to 7.1.0
Use reduxjs/toolkit and react-redux for state management
Upgrade material to 6.3.0
Updated most dependencies to current versions
@mfechner mfechner force-pushed the react_scripts_to_vite branch from 1bf4902 to 3d6fca9 Compare January 1, 2025 12:50
@mfechner
Copy link
Author

mfechner commented Jan 1, 2025

This is now a first version we can start.
The newly rewritten react app looks ok and tests I did are working fine.
There can be some finetuning, like loading indicator or use redux RTK query.

There are some todos left (infinite loading component for messages), but it is now a good starting point.

The only problem I have is that puppeteer sometimes is just not loading the page and therefore tests are not running.
Maybe someone has an idea why?

@jmattheis would be nice if you can have a look, it was really a lot of work

This caused a failure correctly forwarding the user to the login page
Using URL here provides a safer way handling exotic domains or pathes
…nto a arrow function with error handling to make the close handler easier to understand
If compression is enabled, go does not add a Content-Length Header to the request.
This causes problems with transfers in some browsers.
It is nicely visible while doing e2e tests with:
cd ui
yarn build
yarn run test user

This is maybe a bug in Chrome:
https://issues.chromium.org/issues/40406950

If you want compression use a proxy before gotify that handles the compression and headers correctly.

For reference:
golang/go#19420
https://go-review.googlesource.com/c/go/+/381956
@mfechner
Copy link
Author

mfechner commented Jan 2, 2025

All tests are now working fine for me.

@mfechner mfechner marked this pull request as ready for review January 2, 2025 08:25
@mfechner mfechner requested a review from a team as a code owner January 2, 2025 08:25
@jmattheis
Copy link
Member

I'll have a look at this in the coming days, but I'll take some time.

@jmattheis jmattheis self-requested a review January 2, 2025 17:11
@mfechner
Copy link
Author

mfechner commented Jan 2, 2025

I'll have a look at this in the coming days, but I'll take some time.

thank you very much. I found other small issues I corrected in the meantime.
The webinterface itself seems to work, but I have problem with the tests.
Sometimes they are failing (about 2 failures if doing 10 test runs).

The problems I found is that for most actions a submit or a click on a button was not blocking (running in background), which I reverted to the behaviour the released gotify version has.
But something with the test is maybe broken and as I never used pupeteer before, that is hard for me to analyse, so I need some help with the tests.

Later we can addnice indicators, if you e.g. click on Submit the button is replaced by an loading indicator.

I have here some very nice ideas, but I will provide this later in another PR, as that is not related to this topic.

Let us first get the update to react 19 merged, then we can continue further in smaller steps.

Maybe we can release these changes as a beta version to enable users testing it.

Replace react-codemirror2 which is unmaintained by @uiw/react-codemirror and applied required API changes to ReactMarkdown
…container and can be accessed for the windows host.

This is required to use/test plugins with gotify
Copy link
Member

@jmattheis jmattheis left a comment

Choose a reason for hiding this comment

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

Not finished with reviewing, this was only glancing through some files.

import Login from './user/Login.tsx';
import Users from './user/Users.tsx';

const router = createBrowserRouter([
Copy link
Member

Choose a reason for hiding this comment

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

Previously this used a hash router, urls now look like this https://app.gotify.net/messages/1 instead off https://app.gotify.net/#/messages/1, this produces not found when reloading the production build. It's likely only a change to the hash router.

404.mp4

@@ -0,0 +1,114 @@
import React, {useEffect} from 'react';
Copy link
Member

Choose a reason for hiding this comment

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

With browser width <=1500px, the navbar is broken:

image

@@ -0,0 +1,114 @@
import React, {useEffect} from 'react';
import {createBrowserRouter, RouterProvider} from 'react-router-dom';
import Applications from './application/Applications.tsx';
Copy link
Member

Choose a reason for hiding this comment

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

Previously the navbar had a different background color that the normal background to get some contrast:

image

@@ -0,0 +1,114 @@
import React, {useEffect} from 'react';
import {createBrowserRouter, RouterProvider} from 'react-router-dom';
Copy link
Member

Choose a reason for hiding this comment

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

Blank space above header

image

@@ -32,7 +31,7 @@ func Register(r *gin.Engine, version model.VersionInfo, register bool) {
return strings.Replace(content, "%CONFIG%", string(uiConfigBytes), 1)
}

ui := r.Group("/", gzip.Gzip(gzip.DefaultCompression))
Copy link
Member

Choose a reason for hiding this comment

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

I cannot reproduce the net:ERR_INCOMPLETE_CHUNKED_ENCODING error with your listed steps:

cd ui
yarn build
yarn run test user

with having the compression enabled here.

ws: true,
rewrite: (p) => p.replace(/^\/api/, ''),
rewriteWsOrigin: true,
}
Copy link
Member

Choose a reason for hiding this comment

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

Crash with markdown message

image

Unexpected `plugins` prop, use `remarkPlugins` instead (see <https://github.com/remarkjs/react-markdown/blob/main/changelog.md#change-plugins-to-remarkplugins> for more info)
Assertion: Unexpected `plugins` prop, use `remarkPlugins` instead (see <https://github.com/remarkjs/react-markdown/blob/main/changelog.md#change-plugins-to-remarkplugins> for more info)
    at assert (http://localhost:5173/node_modules/.vite/deps/chunk-KI4R7435.js?v=f1b9e081:62:56)
    at unreachable (http://localhost:5173/node_modules/.vite/deps/chunk-KI4R7435.js?v=f1b9e081:58:3)
    at Markdown (http://localhost:5173/node_modules/.vite/deps/react-markdown.js?v=f1b9e081:7013:7)
    at react-stack-bottom-frame (http://localhost:5173/node_modules/.vite/deps/react-dom_client.js?v=f1b9e081:16192:20)
    at renderWithHooks (http://localhost:5173/node_modules/.vite/deps/react-dom_client.js?v=f1b9e081:4306:24)
    at updateFunctionComponent (http://localhost:5173/node_modules/.vite/deps/react-dom_client.js?v=f1b9e081:5972:21)
    at beginWork (http://localhost:5173/node_modules/.vite/deps/react-dom_client.js?v=f1b9e081:7048:20)
    at runWithFiberInDEV (http://localhost:5173/node_modules/.vite/deps/react-dom_client.js?v=f1b9e081:726:18)
    at performUnitOfWork (http://localhost:5173/node_modules/.vite/deps/react-dom_client.js?v=f1b9e081:10831:98)
    at workLoopSync (http://localhost:5173/node_modules/.vite/deps/react-dom_client.js?v=f1b9e081:10692:43)

E.g.

{
  "title": "Title",
  "message": "**test**",
  "priority": 5,
  "extras": {
    "client::display": {
      "contentType": "text/markdown"
    }
  }
}


useEffect(() => {
dispatch(fetchMessages(appId));
Copy link
Member

Choose a reason for hiding this comment

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

The tests are likely failing due to this. The messages aren't cached, after switching to another application. The test currently switches the selected application and then deletes a messages (without waiting for anything like loading new messages etc). It's likely that the new messages aren't loaded yet, and an unexpected message is deleted.

<LoadingSpinner />
) : hasMessages ? (
<div style={{width: '100%'}} id="messages">
{/* TODO: maybe replace ReactInfitite with react-window, which is also documented here: https://mui.com/material-ui/react-list/#virtualized-list */}
Copy link
Member

Choose a reason for hiding this comment

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

I've recently tried a lot of window libraries for another project and landed on https://tanstack.com/virtual/latest/docs/introduction react-window doesn't play to well with having a window over the whole screen.

class SettingsDialog extends Component<IProps & Stores<'currentUser'>> {
@observable
private pass = '';
// TODO: rename to ChangePasswordDialog
Copy link
Member

Choose a reason for hiding this comment

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

Please make as few refactoring as possible in this PR, only what's necessary so it says small.

}

const initialUiState: UiState = {
themeKey: 'dark',
Copy link
Member

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 like this too, previously this was local component state, because it was only used in two places, and passed downwards to one component. Sure adding this to the global state can be done, but local component state is better as it's simpler and easier to navigate.

These refactorings making this review much more effort as it's not just library change / upgrades, but also logic changes which aren't 100% necessary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

Migrate react-scripts to vite
2 participants