diff --git a/server/events/events.go b/server/events/events.go index 00d991e6b..fb9d6ad71 100644 --- a/server/events/events.go +++ b/server/events/events.go @@ -40,6 +40,7 @@ type KeepAlive struct { type ServerStart struct { baseEvent StartTime time.Time `json:"startTime"` + Version string `json:"version"` } const Any = "*" diff --git a/server/events/sse.go b/server/events/sse.go index 4a4887ddf..a9f808b0b 100644 --- a/server/events/sse.go +++ b/server/events/sse.go @@ -208,7 +208,7 @@ func (b *broker) listen() { log.Debug("Client added to event broker", "numClients", len(clients), "newClient", c.String()) // Send a serverStart event to new client - c.diode.put(b.prepareMessage(&ServerStart{StartTime: consts.ServerStart})) + c.diode.put(b.prepareMessage(&ServerStart{StartTime: consts.ServerStart, Version: consts.Version()})) case c := <-b.unsubscribing: // A client has detached and we want to diff --git a/ui/src/i18n/en.json b/ui/src/i18n/en.json index 41aedffae..f1d67903a 100644 --- a/ui/src/i18n/en.json +++ b/ui/src/i18n/en.json @@ -278,7 +278,8 @@ "data_provider_error": "dataProvider error. Check the console for details.", "i18n_error": "Cannot load the translations for the specified language", "canceled": "Action cancelled", - "logged_out": "Your session has ended, please reconnect." + "logged_out": "Your session has ended, please reconnect.", + "new_version": "New version available! Please refresh this window." }, "toggleFieldsMenu": { "columnsToDisplay": "Columns To Display", diff --git a/ui/src/layout/ActivityPanel.js b/ui/src/layout/ActivityPanel.js index 1c937a60e..6b67e95a7 100644 --- a/ui/src/layout/ActivityPanel.js +++ b/ui/src/layout/ActivityPanel.js @@ -1,6 +1,6 @@ import React, { useState, useEffect } from 'react' import { useDispatch, useSelector } from 'react-redux' -import { useTranslate } from 'react-admin' +import { useNotify, useTranslate } from 'react-admin' import { Popover, Badge, @@ -22,6 +22,7 @@ import subsonic from '../subsonic' import { scanStatusUpdate } from '../actions' import { useInterval } from '../common' import { formatDuration } from '../utils' +import config from '../config' const useStyles = makeStyles((theme) => ({ wrapper: { @@ -58,9 +59,10 @@ const Uptime = () => { const ActivityPanel = () => { const serverStart = useSelector((state) => state.activity.serverStart) - const up = serverStart && serverStart.startTime + const up = serverStart.startTime const classes = useStyles({ up }) const translate = useTranslate() + const notify = useNotify() const [anchorEl, setAnchorEl] = useState(null) const open = Boolean(anchorEl) const dispatch = useDispatch() @@ -82,6 +84,12 @@ const ActivityPanel = () => { }) }, [dispatch]) + useEffect(() => { + if (serverStart.version !== config.version) { + notify('ra.notification.new_version', 'info', {}, false, 604800000 * 50) + } + }, [serverStart, notify]) + return (
diff --git a/ui/src/reducers/activityReducer.js b/ui/src/reducers/activityReducer.js index 703c086e7..d2a7d310b 100644 --- a/ui/src/reducers/activityReducer.js +++ b/ui/src/reducers/activityReducer.js @@ -3,17 +3,14 @@ import { EVENT_SCAN_STATUS, EVENT_SERVER_START, } from '../actions' +import config from '../config' -const defaultState = { +const initialState = { scanStatus: { scanning: false, folderCount: 0, count: 0 }, + serverStart: { version: config.version }, } -export const activityReducer = ( - previousState = { - scanStatus: defaultState, - }, - payload -) => { +export const activityReducer = (previousState = initialState, payload) => { const { type, data } = payload switch (type) { case EVENT_SCAN_STATUS: @@ -23,6 +20,7 @@ export const activityReducer = ( ...previousState, serverStart: { startTime: data.startTime && Date.parse(data.startTime), + version: data.version, }, } case EVENT_REFRESH_RESOURCE: