Skip to content

Commit

Permalink
Merge pull request #1455 from cgjgh/Loading-Error-Page
Browse files Browse the repository at this point in the history
Allow App Launch When Offline or Unable to Connect to Node-RED
  • Loading branch information
joepavitt authored Nov 12, 2024
2 parents 767baa6 + 58bd6bc commit 2e0d3cf
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 20 deletions.
38 changes: 36 additions & 2 deletions ui/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,27 @@
<template>
<v-app>
<div v-if="loading" class="nrdb-splash-loading">
<div v-if="error" class="nrdb-placeholder-container">
<div class="nrdb-placeholder">
<img src="./assets/logo.png">
<h1>Node-RED Dashboard 2.0</h1>
<img src="./assets/disconnected.png">
<!-- eslint-disable-next-line vue/no-v-html -->
<p :class="'status-warning'" v-html="error.message" />
<br>
<h4>What you can try:</h4>
<div v-if="error.type === 'server unreachable'" style="border: none" class="nrdb-placeholder">
<v-btn rounded @click="reloadApp">
Reload App
</v-btn>
</div>
<div v-else-if="error.type === 'no internet'" style="border: none" class="nrdb-placeholder">
<v-btn rounded @click="reloadApp">
Reload App
</v-btn>
</div>
</div>
</div>
<div v-else-if="loading" class="nrdb-splash-loading">
<DashboardLoading />
Loading...
</div>
Expand Down Expand Up @@ -43,7 +64,8 @@ export default {
},
computed: {
...mapState('ui', ['dashboards', 'pages', 'widgets']),
...mapState('setup', ['setup']),
...mapState('setup', ['setup', 'error']),
status: function () {
if (this.dashboards) {
const dashboards = Object.keys(this.dashboards)
Expand Down Expand Up @@ -223,6 +245,15 @@ export default {
this.$store.commit('ui/groups', payload.groups)
this.$store.commit('ui/widgets', payload.widgets)
this.$store.commit('ui/themes', payload.themes)
for (const key in payload.themes) {
// check if "Default Theme" theme exists
if (payload.themes[key].name === 'Default Theme') {
// store the default theme in local storage for use when disconnected from Node-RED
localStorage.setItem('ndrb-theme-default', JSON.stringify(payload.themes[key]))
break
}
}
})
},
methods: {
Expand All @@ -233,6 +264,9 @@ export default {
this.$router.push({
name
})
},
reloadApp () {
location.reload()
}
}
}
Expand Down
Binary file added ui/src/assets/disconnected.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
61 changes: 50 additions & 11 deletions ui/src/main.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,30 @@ import './stylesheets/common.css'
import store from './store/index.mjs'
import { useDataTracker } from './widgets/data-tracker.mjs' // eslint-disable-line import/order

// set a base theme on which we will add our custom NR-defined theme
// Retrieve the "Default" theme from cache
function retrieveDefaultThemeFromCache () {
const cachedTheme = localStorage.getItem('ndrb-theme-default')
if (cachedTheme) {
return JSON.parse(cachedTheme)
}
return null
}

const defaultTheme = retrieveDefaultThemeFromCache()

// set a base theme on which we will add our custom NR-defined theme (initially set to the default theme if exists in cache)
const theme = {
dark: false,
colors: {
background: '#fff',
'navigation-background': '#ffffff',
'group-background': '#ffffff',
primary: '#0000ff',
background: defaultTheme ? defaultTheme.colors.bgPage : '#fff',
'navigation-background': defaultTheme ? defaultTheme.colors.surface : '#ffffff',
'group-background': defaultTheme ? defaultTheme.colors.groupBg : '#ffffff',
'group-outline': defaultTheme ? defaultTheme.colors.groupOutline : '#d1d1d1',
primary: defaultTheme ? defaultTheme.colors.primary : '#0094CE',
accent: '#ff6b99',
secondary: '#26ff8c',
success: '#a5d64c',
surface: '#ffffff',
surface: defaultTheme ? defaultTheme.colors.surface : '#ffffff',
info: '#ff53d0',
warning: '#ff8e00',
error: '#ff5252'
Expand Down Expand Up @@ -97,7 +109,7 @@ fetch('_setup')
return
case !response.ok:
console.error('Failed to fetch setup data:', response)
return
throw new Error('Failed to fetch setup data:', response)
case host.origin !== new URL(response.url).origin: {
console.log('Following redirect:', response.url)
const url = new URL(response.url)
Expand Down Expand Up @@ -257,10 +269,37 @@ fetch('_setup')
app.mount('#app')
})
.catch((err) => {
if (err instanceof TypeError && err.message === 'Failed to fetch') {
forcePageReload(err)
function handleOnline () {
// remove the online event listener and reload the page
window.removeEventListener('online', handleOnline)
location.reload()
}

let error = {}
if (navigator.onLine) {
error = { error: err, type: 'server unreachable', message: 'There was an error loading the Dashboard.' }
// Add timer to reload the page every 20 seconds
setInterval(() => {
location.reload()
}, 20000)
} else {
// handle general errors here
console.error('An error occurred:', err)
error = { error: err, type: 'no internet', message: 'Your device appears to be offline.' }
// Add event listener
window.addEventListener('online', handleOnline)
}

store.commit('setup/setError', error) // pass the error to the Vuex store

// load minimal VueJS app to display error message and options to user
const app = Vue.createApp(App)
.use(store)
.use(vuetify)
.use(router)

const head = createHead()
app.use(head)
app.mixin(VueHeadMixin)

// mount the VueJS app into <div id="app"></div> in /ui/public/index.html
app.mount('#app')
})
6 changes: 5 additions & 1 deletion ui/src/store/setup.mjs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
// initial state
const state = () => ({
setup: null
setup: null,
error: null
})

const mutations = {
set (state, setup) {
console.log('setup', setup)
state.setup = setup
},
setError (state, error) {
state.error = error
}
}

Expand Down
13 changes: 7 additions & 6 deletions ui/src/stylesheets/common.css
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ main {
display: flex;
align-items: center;
justify-content: center;
background-color: #eee;
background-color: rgb(var(--v-theme-background, 238, 238, 238)); /* Fallback to light gray (#eee) */
}

.nrdb-placeholder {
Expand All @@ -92,12 +92,13 @@ main {
flex-direction: column;
align-items: center;
justify-content: center;
color: #222;
text-align: center;
padding: 1.5rem 2rem;
border-radius: 6px;
border: 1px solid #d1d1d1;
background-color: #fff;
margin: 1rem;
border-radius: var(--group-border-radius, 6px);
border: 1px solid rgb(var(--v-theme-group-outline, 209, 209, 209)); /* Fallback to light gray (#d1d1d1) */
color: rgb(var(--v-theme-on-group-background, 34, 34, 34)); /* Fallback to dark gray (#222) */
background-color: rgb(var(--v-theme-group-background, 255, 255, 255)); /* Fallback to white (#fff) */
}

.nrdb-placeholder img {
Expand All @@ -119,7 +120,7 @@ main {

.nrdb-placeholder .status-duplicates {
font-weight: 400;
color: #222;
color: rgb(var(--v-theme-on-surface, 34, 34, 34)); /* Fallback to dark gray (#222) */
margin-top: 0.5rem;
}

Expand Down

0 comments on commit 2e0d3cf

Please sign in to comment.