Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7a5b699
fix: upgrade to vite & vitest
jmattheis Aug 3, 2025
ca2d087
fix: upgrade typescript
jmattheis Aug 3, 2025
7cfa314
fix: remove proptypes
jmattheis Aug 3, 2025
54f6de1
fix: upgrade mui
jmattheis Aug 3, 2025
edddfc9
fix: remove js-base64
jmattheis Aug 3, 2025
734113d
fix: upgrade eslint
jmattheis Aug 3, 2025
0ca5156
fix: migrate most components to functional components
jmattheis Aug 3, 2025
a7733b4
fix: use notistack
jmattheis Aug 3, 2025
85d10bf
fix: migrate to react virtuoso & FC
jmattheis Aug 3, 2025
6ced902
fix: later scroll button
jmattheis Aug 3, 2025
031461f
fix: upgrade codemirror
jmattheis Aug 3, 2025
918f705
fix: remove inject
jmattheis Aug 3, 2025
de34fab
fix: react SFC & children
jmattheis Aug 3, 2025
2e3328c
fix: upgrade react-{dom,router,router-dom}
jmattheis Aug 3, 2025
da6a1ec
fix: upgrade markdown and other stuff
jmattheis Aug 3, 2025
edee7c4
fix: upgrade axios
jmattheis Aug 4, 2025
f4ca0cc
fix: update puppeteer & fix flaky tests
jmattheis Aug 4, 2025
86285f4
fix: upgrade mobx
jmattheis Aug 4, 2025
9352aab
fix: remove unnecessary @types
jmattheis Aug 4, 2025
cd385fb
fix: upgrade prettier
jmattheis Aug 4, 2025
01c4566
fix: remove @uiw/codemirror-extensions-langs
jmattheis Aug 4, 2025
6ad4477
fix: lazy load plugin detail view
jmattheis Aug 4, 2025
fd9e687
fix: compile before build
jmattheis Aug 4, 2025
6215a2c
fix: remove NODE_OPTIONS openssl workaround
jmattheis Aug 4, 2025
f04cb2d
fix: preserve expanded state
jmattheis Aug 4, 2025
73d42c5
allow building on non docker environment
eternal-flame-AD Aug 4, 2025
d929e66
fix message layout
eternal-flame-AD Aug 4, 2025
8741d1e
suggestions
eternal-flame-AD Aug 5, 2025
3d8bee8
Change vite listen port name to GOTIFY_SERVER_PORT
eternal-flame-AD Aug 6, 2025
43574a0
Merge pull request #819 from gotify/upgrade-ui-fix-message-layout
jmattheis Aug 6, 2025
271f555
fix: always set loading
jmattheis Aug 8, 2025
d99b423
fix: refetch data on reconnect
jmattheis Aug 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ ifdef GOTOOLCHAIN
else

@jmattheis jmattheis Aug 7, 2025

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Reconnecting doesn't correctly refresh the currently visible data.

reconnect.webm

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Not sure how you made this happen, I blocked /message and clicked refresh and can get stuck in this state.

Probably it's MessagesStore#loadMore needs a .finally(() => (this.loading = false)), I think it is an old bug bc IIRC I accidentally got it stuck refreshing before too.

@jmattheis jmattheis Aug 8, 2025

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is a separate problem, I'll fix this too. The one I can reproduce happens after restarting gotify/server. The cause of this bug is that the state is cleared after a reconnect happens (there could be new messages while the websocket was disconnected, so we do a full refresh). This happens here

reaction(
() => stores.currentUser.connectionErrorMessage,
(connectionErrorMessage) => {
if (!connectionErrorMessage) {
clearAll();
loadAll();
}
}
);
the pages like Messages currently do the initial fetch of the data via a useEffect.
React.useEffect(() => {
if (!messagesStore.loaded(appId)) {
messagesStore.loadMore(appId);
}
}, [appId]);

This useEffect isn't reexecuted after the state is cleared by the reaction. On the current master version this is done in a different way that is reexecuted.

GO_VERSION=$(shell go mod edit -json | jq -r .Toolchain | sed -e 's/go//')
endif
DOCKER_BUILD_IMAGE=gotify/build
DOCKER_BUILD_IMAGE=docker.io/gotify/build
DOCKER_WORKDIR=/proj
DOCKER_RUN=docker run --rm -e LD_FLAGS="$$LD_FLAGS" -v "$$PWD/.:${DOCKER_WORKDIR}" -v "`go env GOPATH`/pkg/mod/.:/go/pkg/mod:ro" -w ${DOCKER_WORKDIR}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@eternal-flame-AD Do you have time to give this PR a small manual test? It should work as the e2e tests are successful, but there may be something I overlooked.

The UI coloring changed a little because the defaults of the material-ui lib changed.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Sure, will do today or tomorrow!

DOCKER_GO_BUILD=go build -mod=readonly -a -installsuffix cgo -ldflags "$$LD_FLAGS"
DOCKER_TEST_LEVEL ?= 0 # Optionally run a test during docker build
NODE_OPTIONS=$(shell if node --help | grep -q -- "--openssl-legacy-provider"; then echo --openssl-legacy-provider; fi)

test: test-coverage test-js
check: check-go check-swagger check-js
Expand Down Expand Up @@ -116,7 +115,7 @@ _build_within_docker:
${DOCKER_GO_BUILD} -o ${OUTPUT}

build-js:
(cd ui && NODE_OPTIONS="${NODE_OPTIONS}" yarn build)
(cd ui && yarn build)

build-linux-amd64:
${DOCKER_RUN} ${DOCKER_BUILD_IMAGE}:$(GO_VERSION)-linux-amd64 make _build_within_docker OUTPUT=${BUILD_DIR}/gotify-linux-amd64
Expand Down
2 changes: 0 additions & 2 deletions ui/.eslintignore

This file was deleted.

91 changes: 0 additions & 91 deletions ui/.eslintrc.yml

This file was deleted.

6 changes: 6 additions & 0 deletions ui/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// @ts-check

import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';

export default tseslint.config(eslint.configs.recommended, tseslint.configs.recommended);
7 changes: 3 additions & 4 deletions ui/public/index.html → ui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#3f51b5">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="manifest" href="manifest.json">
<title>Gotify</title>

<link rel="apple-touch-icon-precomposed" sizes="57x57" href="static/apple-touch-icon-57x57.png" />
Expand Down Expand Up @@ -35,8 +35,7 @@
Gotify requires JavaScript.
</noscript>
<div id="root"></div>
<% if (process.env.NODE_ENV === 'production') { %>
<script>window.config = %CONFIG%;</script>
<% } %>
<script>window.config = %CONFIG%;</script>
<script type="module" src="/src/index.tsx"></script>
</body>
</html>
101 changes: 44 additions & 57 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,72 +5,59 @@
"homepage": ".",
"proxy": "http://localhost:80",
"dependencies": {
"@material-ui/core": "^4.11.4",
"@material-ui/icons": "^4.9.1",
"axios": "^0.21.1",
"codemirror": "^5.61.1",
"detect-browser": "^5.2.0",
"js-base64": "^3.6.1",
"mobx": "^5.15.6",
"mobx-react": "^6.3.0",
"mobx-utils": "^5.6.1",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@mui/icons-material": "^7.2.0",
"@mui/material": "^7.2.0",
"@uiw/codemirror-theme-material": "^4.24.2",
"@uiw/react-codemirror": "^4.24.2",
"@vitejs/plugin-react": "^4.7.0",
"axios": "^1.11.0",
"detect-browser": "^5.3.0",
"mobx": "^6.13.7",
"mobx-react": "^9.2.0",
"mobx-utils": "^6.1.1",
"notifyjs": "^3.0.0",
"prop-types": "^15.6.2",
"react": "^16.4.2",
"react-codemirror2": "^7.2.1",
"react-dom": "^16.4.2",
"react-infinite": "^0.13.0",
"react-markdown": "^6.0.2",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"react-timeago": "^6.2.1",
"remark-gfm": "^1.0.0",
"remove-markdown": "^0.3.0",
"typeface-roboto": "1.1.13"
"notistack": "^3.0.2",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-markdown": "^10.1.0",
"react-router": "^7.7.1",
"react-router-dom": "^7.7.1",
"react-timeago": "^8.3.0",
"react-virtuoso": "^4.13.0",
"remark-gfm": "^4.0.1",
"remove-markdown": "^0.6.2",
"tss-react": "^4.9.19",
"typeface-roboto": "1.1.13",
"vite": "^7.0.6",
"vitest": "^3.2.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=node",
"eject": "react-scripts eject",
"start": "vite",
"prebuild": "tsc",
"build": "vite build",
"test": "vitest --disable-console-intercept --no-file-parallelism",
"lint": "eslint \"src/**/*.{ts,tsx}\"",
"format": "prettier \"src/**/*.{ts,tsx}\" --write",
"testformat": "prettier \"src/**/*.{ts,tsx}\" --list-different"
},
"devDependencies": {
"@types/codemirror": "5.60.0",
"@types/detect-browser": "^4.0.0",
"@types/get-port": "^4.0.0",
"@types/jest": "^26.0.23",
"@types/js-base64": "^3.3.1",
"@types/node": "^15.12.2",
"@types/notifyjs": "^3.0.2",
"@types/puppeteer": "^5.4.6",
"@types/react": "^16.9.49",
"@types/react-dom": "^16.9.8",
"@types/react-infinite": "0.0.35",
"@types/react-router-dom": "^5.1.7",
"@types/remove-markdown": "^0.3.0",
"@types/rimraf": "^3.0.0",
"@typescript-eslint/eslint-plugin": "^4.1.0",
"@typescript-eslint/parser": "^4.1.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-jest": "^24.0.0",
"eslint-plugin-prefer-arrow": "^1.2.2",
"eslint-plugin-react": "^7.20.6",
"eslint-plugin-unicorn": "^21.0.0",
"get-port": "^5.1.1",
"prettier": "^2.3.1",
"puppeteer": "^17.1.3",
"react-scripts": "^4.0.3",
"rimraf": "^3.0.2",
"@eslint/js": "^9.32.0",
"@types/notifyjs": "^3.0.5",
"@types/react": "^19.1.9",
"@types/react-dom": "^19.1.7",
"@types/react-router-dom": "^5.3.3",
"@types/remove-markdown": "^0.3.4",
"eslint": "^9.32.0",
"get-port": "^7.1.0",
"prettier": "^3.6.2",
"puppeteer": "^24.15.0",
"rimraf": "^6.0.1",
"tree-kill": "^1.2.0",
"typescript": "4.0.2",
"wait-on": "^5.3.0"
},
"eslintConfig": {
"extends": "react-app"
"typescript": "^5.9.2",
"typescript-eslint": "^8.38.0",
"wait-on": "^8.0.4"
},
"browserslist": {
"production": [
Expand Down
1 change: 0 additions & 1 deletion ui/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ func Register(r *gin.Engine, version model.VersionInfo, register bool) {
ui.GET("/", serveFile("index.html", "text/html", replaceConfig))
ui.GET("/index.html", serveFile("index.html", "text/html", replaceConfig))
ui.GET("/manifest.json", serveFile("manifest.json", "application/json", noop))
ui.GET("/asset-manifest.json", serveFile("asset-manifest.json", "application/json", noop))

subBox, err := fs.Sub(box, "build")
if err != nil {
Expand Down
Loading
Loading