Merge pull request #482 from bastienwirtz/vuejs-3

Vuejs 3
This commit is contained in:
Bastien Wirtz 2022-07-13 13:46:03 -07:00 committed by GitHub
commit c11c45a661
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 2146 additions and 7829 deletions

View File

@ -1,3 +0,0 @@
> 1%
last 2 versions
not dead

17
.eslintrc.cjs Normal file
View File

@ -0,0 +1,17 @@
/* eslint-env node */
require("@rushstack/eslint-patch/modern-module-resolution");
module.exports = {
root: true,
extends: [
"plugin:vue/vue3-essential",
"eslint:recommended",
"@vue/eslint-config-prettier",
],
env: {
"vue/setup-compiler-macros": true,
},
rules: {
"vue/multi-word-component-names": "off",
},
};

View File

@ -1,15 +0,0 @@
module.exports = {
root: true,
env: {
node: true,
},
extends: ["plugin:vue/essential", "eslint:recommended", "@vue/prettier"],
parserOptions: {
parser: "babel-eslint",
},
rules: {
"no-console": "off",
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
"vue/require-v-for-key": "off",
},
};

View File

@ -20,12 +20,19 @@ jobs:
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
-
name: Checkout
uses: actions/checkout@v3
-
name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
cache: 'yarn'
- run: yarn install
- run: yarn lint
-
name: install dependencies
run: yarn install
-
name: Check code style & potentential issues
run: yarn lint

View File

@ -10,15 +10,20 @@ jobs:
name: Upload Release Asset
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build project
-
name: Checkout
uses: actions/checkout@v3
-
name: Build project
run: |
yarn install
yarn build
- name: Create artifact
-
name: Create artifact
working-directory: "dist"
run: zip -r ../homer.zip ./*
- name: Create Release
-
name: Create Release
id: create_release
uses: softprops/action-gh-release@v1
with:

View File

@ -1,3 +0,0 @@
module.exports = {
presets: ["@vue/cli-plugin-babel/preset"],
};

View File

@ -152,7 +152,7 @@ services:
# background: red # optional color for card to set color directly without custom stylesheet
```
View [Custom Services](customservices.md) for details about all available custom services (like PiHole) and how to configure them.
View **[Custom Services](customservices.md)** for details about all available custom services (like `PiHole`) and how to configure them.
If you choose to fetch message information from an endpoint, the output format should be as follows (or you can [custom map fields as shown in tips-and-tricks](./tips-and-tricks.md#mapping-fields)):
@ -180,27 +180,4 @@ You can read the [bulma modifiers page](https://bulma.io/documentation/modifiers
## PWA Icons
In order to easily generate all required icon preset for the PWA to work, a tool like [vue-pwa-asset-generator](https://www.npmjs.com/package/vue-pwa-asset-generator) can be used:
```bash
npx vue-pwa-asset-generator -a {your_512x512_source_png} -o {your_output_folder}
```
## Supported services
Currently the following services are supported for showing quick infos on the card. They can be used by setting the type to one of the following values at the item.
- PiHole
- AdGuardHome
- PaperlessNG
- Mealie
## Additional configuration
### Paperless
For Paperless you need an API-Key which you have to store at the item in the field `apikey`.
### Mealie
First off make sure to remove an existing `subtitle` as it will take precedence if set. Setting `type: "Mealie"` will then show the number of recipes Mealie is keeping organized or the planned meal for today if one is planned. You will have to set an API key in the field `apikey` which can be created in your Mealie installation.
See icons documentation [here](https://github.com/bastienwirtz/homer/blob/main/public/assets/icons/README.md).

View File

@ -19,6 +19,7 @@ within Homer:
+ [Emby / Jellyfin](#emby--jellyfin)
+ [Uptime Kuma](#uptime-kuma)
+ [Tautulli](#tautulli)
+ [Mealie](#mealie)
+ [Healthchecks](#healthchecks)
If you experiencing any issue, please have a look to the [troubleshooting](troubleshooting.md) page.
@ -225,6 +226,11 @@ endpoint pointing to Tautulli!
apikey: "MY-SUPER-SECRET-API-KEY"
```
## Mealie
First off make sure to remove an existing `subtitle` as it will take precedence if set.
Setting `type: "Mealie"` will then show the number of recipes Mealie is keeping organized or the planned meal for today if one is planned. You will have to set an API key in the field `apikey` which can be created in your Mealie installation.
## Healthchecks
This service displays information about the configured status checks from the Healthchecks application.

View File

@ -5,11 +5,11 @@ If you want to contribute to Homer, please read the [contributing guidelines](ht
```sh
# Using yarn (recommended)
yarn install
yarn serve
yarn dev
# **OR** Using npm
npm install
npm run serve
npm run dev
```
## Custom services

15
index.html Normal file
View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="assets/icons/favicon.ico" />
<link rel="apple-touch-icon" href="assets/icons/apple-touch-icon.png" sizes="180x180">
<link rel="mask-icon" href="assets/icons/logo.svg">
<meta name="viewport" content="width=device-width,initial-scale=1.0,viewport-fit=cover">
<title>Homer</title>
</head>
<body>
<div id="app-mount"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

View File

@ -1,35 +1,29 @@
{
"name": "homer",
"version": "21.09.1",
"version": "22.07.2",
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
"dev": "vite",
"build": "vite build",
"preview": "vite preview --port 5050",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
},
"dependencies": {
"@fortawesome/fontawesome-free": "^6.1.1",
"bulma": "^0.9.4",
"core-js": "^3.22.7",
"js-yaml": "^4.1.0",
"lodash.merge": "^4.6.2",
"register-service-worker": "^1.7.2",
"vue": "^2.6.14"
"vue": "^3.2.33"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.19",
"@vue/cli-plugin-eslint": "~4.5.19",
"@vue/cli-plugin-pwa": "~4.5.19",
"@vue/cli-service": "~4.5.19",
"@vue/eslint-config-prettier": "^6.0.0",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-vue": "^6.2.2",
"prettier": "^2.2.1",
"raw-loader": "^4.0.2",
"sass": "^1.26.5",
"sass-loader": "^8.0.2",
"vue-template-compiler": "^2.6.12"
"@rushstack/eslint-patch": "^1.1.0",
"@vitejs/plugin-vue": "^2.3.1",
"@vue/eslint-config-prettier": "^7.0.0",
"eslint": "^8.5.0",
"eslint-plugin-vue": "^9.2.0",
"prettier": "^2.5.1",
"sass": "^1.52.2",
"vite": "^2.9.14",
"vite-plugin-pwa": "^0.12.3"
},
"license": "Apache-2.0"
}

View File

@ -0,0 +1,12 @@
# PWA Icons / Images
We suggest you to create a svg or png icon (if it is a png icon, with the maximum resolution possible) for your application and use it to generate a favicon package in [Favicon Generator](https://realfavicongenerator.net/).
Once generated, download the ZIP and use android-* icons for pwa-*:
- use `android-chrome-192x192.png` for `pwa-192x192.png`
- use `android-chrome-512x512.png` for `pwa-512x512.png`
- `apple-touch-icon.png` is `apple-touch-icon.png`
- `favicon.ico` is `favicon.ico`
`

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 790 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -1,42 +0,0 @@
{
"name": "Homer Dashboard",
"short_name": "Homer",
"theme_color": "#3367D6",
"start_url": "../",
"icons": [
{
"src": "./icons/favicon-16x16.png",
"sizes": "16x16",
"type": "image/png"
},
{
"src": "./icons/favicon-32x32.png",
"sizes": "32x32",
"type": "image/png"
},
{
"src": "./icons/icon-any.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "./icons/icon-any.svg",
"sizes": "any",
"type": "image/svg+xml",
"purpose": "any"
},
{
"src": "./icons/icon-maskable.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "./icons/safari-pinned-tab.svg",
"sizes": "any",
"type": "image/svg+xml",
"purpose": "monochrome"
}
]
}

View File

@ -1,18 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0,viewport-fit=cover">
<meta name="robots" content="noindex">
<link rel="icon" href="<%= BASE_URL %>favicon.png">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -1,2 +0,0 @@
User-agent: *
Disallow:

View File

@ -49,10 +49,10 @@
<SearchInput
class="navbar-item is-inline-block-mobile"
:hotkey="searchHotkey()"
@input="filterServices"
@input="filterServices($event.target?.value)"
@search-focus="showMenu = true"
@search-open="navigateToFirstService"
@search-cancel="filterServices"
@search-open="navigateToFirstService($event?.target?.value)"
@search-cancel="filterServices()"
/>
</Navbar>
</div>
@ -140,8 +140,8 @@
</template>
<script>
const jsyaml = require("js-yaml");
const merge = require("lodash.merge");
import jsyaml from "js-yaml";
import merge from "lodash.merge";
import Navbar from "./components/Navbar.vue";
import GetStarted from "./components/GetStarted.vue";
@ -153,7 +153,7 @@ import SettingToggle from "./components/SettingToggle.vue";
import DarkMode from "./components/DarkMode.vue";
import DynamicTheme from "./components/DynamicTheme.vue";
import defaultConfig from "./assets/defaults.yml";
import defaultConfig from "./assets/defaults.yml?raw";
export default {
name: "App",
@ -255,11 +255,12 @@ export default {
});
},
matchesFilter: function (item) {
const needle = this.filter?.toLowerCase();
return (
item.name.toLowerCase().includes(this.filter) ||
(item.subtitle && item.subtitle.toLowerCase().includes(this.filter)) ||
(item.tag && item.tag.toLowerCase().includes(this.filter)) ||
(item.keywords && item.keywords.toLowerCase().includes(this.filter))
item.name.toLowerCase().includes(needle) ||
(item.subtitle && item.subtitle.toLowerCase().includes(needle)) ||
(item.tag && item.tag.toLowerCase().includes(needle)) ||
(item.keywords && item.keywords.toLowerCase().includes(needle))
);
},
navigateToFirstService: function (target) {
@ -271,6 +272,7 @@ export default {
}
},
filterServices: function (filter) {
console.log(filter);
this.filter = filter;
if (!filter) {

View File

@ -2,7 +2,7 @@
@import "./webfonts/webfonts.scss";
@import "bulma";
@import "../../node_modules/bulma/bulma";
// Themes import
@import "./themes/sui.scss";
@ -13,7 +13,7 @@
text-overflow: ellipsis;
}
html, body, body #app {
html, body, body #app-mount, body #app {
height: 100%;
background-color: var(--background);
}

View File

@ -56,7 +56,9 @@ export default {
// extra check to make sure we're not offline
let that = this;
const aliveCheckUrl = window.location.href + "?t=" + new Date().valueOf();
const aliveCheckUrl = `${window.location.origin}${
window.location.pathname
}/index.html?t=${new Date().valueOf()}`;
return fetch(aliveCheckUrl, {
method: "HEAD",
cache: "no-store",

View File

@ -1,6 +1,6 @@
<template>
<a
v-on:click="toggleTheme()"
@click="toggleTheme()"
aria-label="Toggle dark mode"
class="navbar-item is-inline-block-mobile"
>

View File

@ -47,7 +47,6 @@ export default {
}
if (this.item.url) {
let fetchedMessage = await this.downloadMessage(this.item.url);
console.log("done");
if (this.item.mapping) {
fetchedMessage = this.mapRemoteMessage(fetchedMessage);
}

View File

@ -75,7 +75,7 @@ export default {
this.$emit("input", value.toLowerCase());
},
},
beforeDestroy() {
beforeUnmount() {
document.removeEventListener("keydown", this._keyListener);
},
};

View File

@ -1,8 +1,9 @@
<template>
<component v-bind:is="component" :item="item" :proxy="proxy"></component>
<component :is="component" :item="item" :proxy="proxy"></component>
</template>
<script>
import { defineAsyncComponent } from "vue";
import Generic from "./services/Generic.vue";
export default {
@ -17,7 +18,7 @@ export default {
if (type === "Generic") {
return Generic;
}
return () => import(`./services/${type}.vue`);
return defineAsyncComponent(() => import(`./services/${type}.vue`));
},
},
};

View File

@ -1,5 +1,8 @@
<template>
<a v-on:click="toggleSetting()" class="navbar-item is-inline-block-mobile">
<a
@click.prevent="toggleSetting()"
class="navbar-item is-inline-block-mobile"
>
<span><i :class="['fas', 'fa-fw', value ? icon : secondaryIcon]"></i></span>
<slot></slot>
</a>

View File

@ -22,7 +22,7 @@
<div v-else>
<p class="title is-4">{{ name }}</p>
<p class="subtitle is-6">
{{ temp | tempSuffix(this.item.units) }}
{{ temperature }}
</p>
</div>
</div>
@ -50,6 +50,19 @@ export default {
conditions: null,
error: false,
}),
computed: {
temperature: function () {
if (!this.temp) return "";
let unit = "K";
if (this.item.units === "metric") {
unit = "°C";
} else if (this.item.units === "imperial") {
unit = "°F";
}
return `${this.temp} ${unit}`;
},
},
created() {
this.fetchWeather();
},
@ -86,19 +99,6 @@ export default {
});
},
},
filters: {
tempSuffix: function (value, type) {
if (!value) return "";
let unit = "K";
if (type === "metric") {
unit = "°C";
} else if (type === "imperial") {
unit = "°F";
}
return `${value} ${unit}`;
},
},
};
</script>

View File

@ -99,6 +99,7 @@ export default {
},
},
created() {
/* eslint-disable */
this.item.url = `${this.item.url}/status/${this.dashboard}`;
this.fetchStatus();
},

View File

@ -1,19 +1,13 @@
import Vue from "vue";
import { createApp, h } from "vue";
import App from "./App.vue";
import "./registerServiceWorker";
import "@fortawesome/fontawesome-free/css/all.css";
import "./assets/app.scss";
Vue.config.productionTip = false;
const app = createApp(App);
Vue.component("DynamicStyle", {
render: function (createElement) {
return createElement("style", this.$slots.default);
},
app.component("DynamicStyle", (_props, context) => {
return h("style", {}, context.slots);
});
new Vue({
render: (h) => h(App),
}).$mount("#app");
app.mount("#app-mount");

View File

@ -1,34 +0,0 @@
/* eslint-disable no-console */
import { register } from "register-service-worker";
if (process.env.NODE_ENV === "production") {
register(`${process.env.BASE_URL}service-worker.js`, {
ready() {
console.log(
"App is being served from cache by a service worker.\n" +
"For more details, visit https://goo.gl/AFskqB"
);
},
registered() {
console.log("Service worker has been registered.");
},
cached() {
console.log("Content has been cached for offline use.");
},
updatefound() {
console.log("New content is downloading.");
},
updated() {
console.log("New content is available; please refresh.");
},
offline() {
console.log(
"No internet connection found. App is running in offline mode."
);
},
error(error) {
console.error("Error during service worker registration:", error);
},
});
}

44
vite.config.js Normal file
View File

@ -0,0 +1,44 @@
import { VitePWA } from "vite-plugin-pwa";
import { fileURLToPath, URL } from "url";
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
// https://vitejs.dev/config/
export default defineConfig({
base: "",
build: {
assetsDir: "resources",
},
plugins: [
vue(),
VitePWA({
registerType: "autoUpdate",
useCredentials: true,
manifestFilename: "assets/manifest.json",
manifest: {
name: "Homer dashboard",
short_name: "Homer",
description: "Home Server Dashboard",
theme_color: "#3367D6",
icons: [
{
src: "./icons/pwa-192x192.png",
sizes: "192x192",
type: "image/png",
},
{
src: "./icons/pwa-512x512.png",
sizes: "512x512",
type: "image/png",
},
],
},
}),
],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
});

View File

@ -1,32 +0,0 @@
const manifestOptions = require("./public/assets/manifest.json");
module.exports = {
chainWebpack: (config) => {
config.module
.rule("yaml")
.test(/\.ya?ml$/)
.use("raw-loader")
.loader("raw-loader")
.end();
},
publicPath: "",
pwa: {
manifestPath: "assets/manifest.json",
manifestCrossorigin: "use-credentials",
appleMobileWebAppStatusBarStyle: "black",
appleMobileWebAppCapable: "yes",
name: manifestOptions.name,
themeColor: manifestOptions.theme_color,
manifestOptions,
iconPaths: {
favicon32: "assets/icons/favicon-32x32.png",
favicon16: "assets/icons/favicon-16x16.png",
appleTouchIcon: "assets/icons/icon-maskable.png",
maskIcon: "assets/icons/safari-pinned-tab.svg",
msTileImage: "assets/icons/icon-any.png",
},
},
devServer: {
disableHostCheck: true,
},
};

9540
yarn.lock

File diff suppressed because it is too large Load Diff