🔀 Merge pull request #663 from Lissy93/FIX/user-suggestions-fixes-2.0.9

[FIX] User Suggestions & Bug Fixes
Fixes #564
Fixes #590
Fixes #626
Fixes #629
Fixes #632
Fixes #640
Fixes #644
Fixes #645
Fixes #646
Fixes #651
Fixes #654
Fixes #655
Fixes #657
Fixes #659
Fixes #660
This commit is contained in:
Alicia Sykes 2022-05-20 14:07:15 +01:00 committed by GitHub
commit 78bed0fb92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 251 additions and 118 deletions

18
.github/CHANGELOG.md vendored
View File

@ -1,5 +1,23 @@
# Changelog
## ✨ 2.0.9 Adds Multi-Page Support [PR #663](https://github.com/Lissy93/dashy/pull/663)
- Fix KeyCloak API URL (#564)
- Fix guest has config access (#590)
- Fix collapsible content in multi-page support (#626)
- Fix layout and item size buttons ( #629)
- Refactor make request in RSS widget (#632)
- Fix material-design-icons header in schema (#640)
- Add option to hide seconds in clock widget (#644)
- Fix pageInfo not being read in router (#645)
- Fix startingView not honored (#646)
- Fix Status Check default (#651)
- Add option to hide image in SportsScores Widget (#654)
- Add Adventure-basic theme (#655)
- Write docs for sub-items (#657)
- Add Font-Awesome displaying as square to troubleshooting guide (#659)
- Show expand / collapse in context menu (#660)
- Only deploy new release when relevant files have changed
## ✨ 2.0.8 Adds Multi-Page Support [PR #617](https://github.com/Lissy93/dashy/pull/617)
- Adds support for multiple pages per-dashboard
- Adds new attribute at root of main config file: `pages`

View File

@ -1,4 +1,17 @@
## ✨ 2.0.8 Adds Multi-Page Support [PR #617](https://github.com/Lissy93/dashy/pull/617)
- Adds support for multiple pages per-dashboard
- Adds new attribute at root of main config file: `pages`
- Updates router and nav-bar to automatically create paths for both local and remote configs
## ✨ 2.0.9 Adds Multi-Page Support [PR #663](https://github.com/Lissy93/dashy/pull/663)
- Fix KeyCloak API URL (#564)
- Fix guest has config access (#590)
- Fix collapsible content in multi-page support (#626)
- Fix layout and item size buttons ( #629)
- Refactor make request in RSS widget (#632)
- Fix material-design-icons header in schema (#640)
- Add option to hide seconds in clock widget (#644)
- Fix pageInfo not being read in router (#645)
- Fix startingView not honored (#646)
- Fix Status Check default (#651)
- Add option to hide image in SportsScores Widget (#654)
- Add Adventure-basic theme (#655)
- Write docs for sub-items (#657)
- Add Font-Awesome displaying as square to troubleshooting guide (#659)
- Show expand / collapse in context menu (#660)
- Only deploy new release when relevant files have changed

View File

@ -6,6 +6,11 @@ on:
push:
branches: ['master']
tags: [v*]
paths:
- '**.js'
- 'src/**'
- 'public/**'
- 'services/**'
env:
DH_IMAGE: ${{ secrets.DOCKER_REPO }}

View File

@ -408,14 +408,15 @@ Dashy supports multiple languages and locales. When available, your language sho
- 🇳🇱 **Dutch**: `nl` - Contributed by **[@evroon](https://github.com/evroon)**
- 🇲🇫 **French**: `fr` - Contributed by **[@EVOTk](https://github.com/EVOTk)**
- 🇩🇪 **German**: `de` - Contributed by **[@Niklashere](https://github.com/Niklashere)**
- 🇮🇹 **Italian**: `it` - Contributed by **[@alexdelprete](https://github.com/alexdelprete)**
- 🇳🇴 **Norwegian Bokmål**: `nb` - Contributed by **[@rubjo](https://github.com/rubjo)**
- 🇵🇱 **Polish**: `pl` - Contributed by **[@skaarj1989](https://github.com/skaarj1989)**
- 🇵🇹 **Portuguese**: `pt` - Contributed by **[@LeoColman](https://github.com/LeoColman)**
- 🇪🇸 **Spanish**: `es` - Contributed by **[@lu4t](https://github.com/lu4t)**
- 🇸🇮 **Slovenian**: `sl` - Contributed by **[@UrekD](https://github.com/UrekD)**
- 🇸🇪 **Swedish**: `sv` - Contributed by **[@BOZG](https://github.com/BOZG)**
- 🇮🇹 **Italian**: `it` - Contributed by **[@alexdelprete](https://github.com/alexdelprete)**
- 🇵🇹 **Portuguese**: `pt` - Contributed by **[@LeoColman](https://github.com/LeoColman)**
- 🇷🇺 **Russian**: `ru` - Contributed by Anon
- 🇹🇼 **Traditional Chinese**: `zh-TW` - Contributed by **[@stanly0726](https://github.com/stanly0726)**
- 🇷🇺 **Russian**: `ru`
- 🇦🇪 **Arabic**: `ar`
- 🇮🇳 **Hindi**: `hi`
- 🇯🇵 **Japanese**: `ja`

View File

@ -146,6 +146,8 @@ appConfig:
clientId: 'dashy'
```
Note that if you are using Keycloak V 17 or older, you will also need to set `legacySupport: true` (also under `appConfig.auth.keycloak`). This is because the API endpoint was updated in later versions.
### 4. Add groups and roles (Optional)
Keycloak allows you to assign users roles and groups. You can use these values to configure who can access various sections in Dashy.
Keycloak server administration and configuration is a deep topic; please refer to the [server admin guide](https://www.keycloak.org/docs/latest/server_admin/index.html#assigning-permissions-and-access-using-roles-and-groups) to see details about creating and assigning roles and groups.

View File

@ -163,6 +163,7 @@ For more info, see the **[Authentication Docs](/docs/authentication.md)**
**`serverUrl`** | `string` | Required | The URL (or URL/ IP + Port) where your keycloak server is running
**`realm`** | `string` | Required | The name of the realm (must already be created) that you want to use
**`clientId`** | `string` | Required | The Client ID of the client you created for use with Dashy
**`legacySupport`** | `boolean` | _Optional_ | If using Keycloak 17 or older, then set this to `true`
**[⬆️ Back to Top](#configuring)**

View File

@ -56,3 +56,43 @@ The following example shows creating a config, publishing it as a [Gist](https:/
Only top-level fields supported by sub-pages are `pageInfo` and `sections`. The `appConfig` and `pages` will always be inherited from your main `conf.yml` file. Other than that, sub-pages behave exactly the same as your default view, and can contain sections, items, widgets and page info like nav links, title and logo.
Note that since page paths are required by the router, they are set at build-time, not run-time, and so a rebuild (happens automatically) is required for changes to page paths to take effect (this only applies to changes to the `pages` array, rebuild isn't required for editing page content).
## Sub-Items
A normal section will contain zero or more items, for example:
```yaml
- name: Coding
icon: far fa-code
items:
- title: GitHub
url: https://github.com/
- title: StackOverflow
url: http://stackoverflow.com/
```
But items can also be grouped together, referred to as sub-items. This is useful for a group of less frequently used items, which you don't want to take up too much space, or for action buttons (_coming soon_).
Item groups may also have an optional title.
```yaml
- name: Coding
icon: far fa-code
items:
- title: Normal Item 1
- title: Normal Item 2
- subItems:
- title: JavaScript
url: https://developer.mozilla.org
icon: si-javascript
- title: TypeScript
url: https://www.typescriptlang.org/docs
icon: si-typescript
- title: Svelt
url: https://svelte.dev/docs
icon: si-svelte
- title: Go
url: https://go.dev/doc
icon: si-go
```

View File

@ -30,12 +30,13 @@
- [Diagnosing Widget Errors](#widget-errors)
- [Fixing Widget CORS Errors](#widget-cors-errors)
- [Weather Forecast Widget 401](#weather-forecast-widget-401)
- [Keycloak Redirect Error](#keycloak-redirect-error)
- [Font Awesome Icons not Displaying](#font-awesome-icons-not-displaying)
- [How-To Open Browser Console](#how-to-open-browser-console)
- [Git Contributions not Displaying](#git-contributions-not-displaying)
---
## `Refused to Connect` in Modal or Workspace View
This is not an issue with Dashy, but instead caused by the target app preventing direct access through embedded elements.
@ -214,6 +215,13 @@ You should also ensure that Keycloak is correctly configured, with a user, realm
For more details on how to set headers, see the [Example Headers](/docs/management.md#setting-headers) in the management docs, or reference the documentation for your proxy.
If you're running in Kubernetes, you will need to enable CORS ingress rules, see [docs](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#enable-cors), e.g:
```
nginx.ingress.kubernetes.io/cors-allow-origin: "https://dashy.example.com"
nginx.ingress.kubernetes.io/enable-cors: "true"
```
See also: #479, #409, #507, #491, #341, #520
---
@ -412,22 +420,19 @@ A future update will be pushed out, to use a free weather forecasting API.
---
## Keycloak Redirect Error
## Font Awesome Icons not Displaying
Firstly, ensure that in your Keycloak instance you have populated the Valid Redirect URIs field ([screenshot](https://user-images.githubusercontent.com/1862727/148599768-db4ee4f8-72c5-402d-8f00-051d999e6267.png)) with the URL to your Dashy instance.
Usually, Font Awesome will be automatically enabled if one or more of your icons are using Font-Awesome. If this is not happening, then you can always manually enable (or disable) Font Awesome by setting: [`appConfig`](/docs/configuring.md#appconfig-optional).`enableFontAwesome` to `true`.
You may need to specify CORS headers on your Keycloak instance, to allow requests coming from Dashy, e.g:
If you are trying to use a premium icon, then you must have a [Pro License](https://fontawesome.com/plans). You'll then need to specify your Pro plan API key under `appConfig.fontAwesomeKey`. You can find this key, by logging into your FA account, navigate to Account → [Kits](https://fontawesome.com/kits) → New Kit → Copy Kit Code. The code is a 10-digit alpha-numeric code, and is also visible within the new kit's URL, for example: `81e48ce079`.
```
Access-Control-Allow-Origin: https://dashy.example.com
```
Be sure that you're specifying the icon category and name correctly. You're icon should look be `[category] fa-[icon-name]`. The following categories are supported: `far` _(regular)_, `fas` _(solid)_, `fal`_(light)_, `fad` _(duo-tone)_ and `fab`_(brands)_. With the exception of brands, you'll usually want all your icons to be in from same category, so they look uniform.
If you're running in Kubernetes, you will need to enable CORS ingress rules, see [docs](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#enable-cors), e.g:
Ensure the icon you are trying to use, is available within [FontAwesome Version 5](https://fontawesome.com/v5/search).
```
nginx.ingress.kubernetes.io/cors-allow-origin: "https://dashy.example.com"
nginx.ingress.kubernetes.io/enable-cors: "true"
```
Examples: `fab fa-raspberry-pi`, `fas fa-database`, `fas fa-server`, `fas fa-ethernet`
Finally, check the [browser console](#how-to-open-browser-console) for any error messages, and raise a ticket if the issue persists.
---

View File

@ -97,6 +97,7 @@ A simple, live-updating time and date widget with time-zone support. All fields
**`format`** | `string` | _Optional_ | A country code for displaying the date and time in local format.<br>Specified as `[ISO-3166]-[ISO-639]`, for example: `en-AU`. See [here](https://www.fincher.org/Utilities/CountryLanguageList.shtml) for a full list of locales. Defaults to the browser / device's region
**`customCityName`** | `string` | _Optional_ | By default the city from the time-zone is shown, but setting this value will override that text
**`hideDate`** | `boolean` | _Optional_ | If set to `true`, the date and city will not be shown. Defaults to `false`
**`hideSeconds`** | `boolean` | _Optional_ | If set to `true`, seconds will not be shown. Defaults to `false`
##### Example
@ -713,6 +714,7 @@ Show recent scores and upcoming matches from your favourite sports team. Data is
**`pastOrFuture`** | `string` | __Optional__ | Set to `past` to show scores for recent games, or `future` to show upcoming games. Defaults to `past`. You can change this within the UI
**`apiKey`** | `string` | __Optional__ | Optionally specify your API key, which you can sign up for at [TheSportsDB.com](https://www.thesportsdb.com/)
**`limit`** | `number` | __Optional__ | To limit output to a certain number of matches, defaults to `15`
**`hideImage`** | `boolean` | __Optional__ | Set to `true` to not render the team / match banner image, defaults to `false`
##### Example

View File

@ -1,6 +1,6 @@
{
"name": "Dashy",
"version": "2.0.8",
"version": "2.0.9",
"license": "MIT",
"main": "server",
"author": "Alicia Sykes <alicia@omg.lol> (https://aliciasykes.com)",
@ -47,6 +47,7 @@
},
"devDependencies": {
"@architect/sandbox": "^4.5.2",
"@babel/preset-env": "^7.17.10",
"@vue/cli-plugin-babel": "^4.5.15",
"@vue/cli-plugin-eslint": "^4.5.15",
"@vue/cli-plugin-pwa": "^4.5.15",
@ -87,6 +88,12 @@
"parser": "babel-eslint"
}
},
"babel": {
"presets": [
"@vue/app",
"@babel/preset-env"
]
},
"postcss": {
"plugins": {
"autoprefixer": {}

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M296 391.1c-6.141 0-12.28 2.344-16.97 7.031L248 430.1v-86.06c0-13.25-10.75-24-24-24s-24 10.75-24 24v86.06l-31.03-31.03C164.3 394.3 158.1 391.1 152 391.1c-12.82 0-24 10.33-24 24c0 6.141 2.344 12.28 7.031 16.97l72 72.01C209.6 507.5 215.5 512 224 512s14.4-4.461 16.97-7.031l72-72.01C317.7 428.3 320 422.1 320 415.1C320 402.3 308.8 391.1 296 391.1zM152 119.1c6.141 0 12.28-2.344 16.97-7.031L200 81.91v86.07c0 13.25 10.75 24 24 24s24-10.75 24-24V81.91l31.03 31.03C283.7 117.6 289.8 119.1 296 119.1c18.79 0 24-17.2 24-23.1c0-6.141-2.344-12.28-7.031-16.97l-72-72.01C234.9 .9766 227.7 0 223.1 0C220.3 0 213.1 .9687 207 7l-72 72.01C130.3 83.7 128 89.84 128 95.98C128 109.7 139.2 119.1 152 119.1zM424 232H24C10.75 232 0 242.7 0 255.1S10.75 280 24 280h400c13.25 0 24-10.76 24-24.01S437.3 232 424 232z"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -195,6 +195,7 @@
"section": {
"open-section": "Open Section",
"edit-section": "Edit",
"expand-collapse": "Expand / Collapse",
"move-section": "Move To",
"remove-section": "Remove"
}

View File

@ -1,14 +1,16 @@
<template>
<div
:class="`collapsable ${rowColSpanClass} ${collapseClass} ${!cutToHeight ? 'full-height' : ''}`"
v-bind:class="[
{ 'is-open': isExpanded, 'full-height': cutToHeight },
`collapsable ${rowColSpanClass}`
]"
:style="`${color ? 'background: '+color : ''}; ${sanitizeCustomStyles(customStyles)};`"
>
<input
:id="sectionKey"
class="toggle"
type="checkbox"
:checked="isExpanded"
@change="collapseChanged"
v-model="checkboxState"
tabIndex="-1"
>
<label :for="sectionKey" class="lbl-toggle" tabindex="-1"
@ -72,14 +74,42 @@ export default {
const { rows, cols, checkSpanNum } = this;
return `${checkSpanNum(cols, 'col')} ${checkSpanNum(rows, 'row')}`;
},
/* Used to fetch initial collapse state, and set new collapse state on change */
isExpanded: {
get() {
if (this.collapsed !== undefined) return !this.collapsed;
const collapseStateObject = this.locallyStoredCollapseStates();
if (collapseStateObject[this.uniqueKey] !== undefined) {
return collapseStateObject[this.uniqueKey];
}
return true;
},
set(newState) {
const collapseState = this.locallyStoredCollapseStates();
collapseState[this.uniqueKey] = newState;
localStorage.setItem(localStorageKeys.COLLAPSE_STATE, JSON.stringify(collapseState));
},
},
},
data: () => ({
isExpanded: false,
checkboxState: true,
}),
mounted() {
this.isExpanded = this.getCollapseState();
this.checkboxState = this.isExpanded;
},
watch: {
checkboxState(newState) {
this.isExpanded = newState;
},
uniqueKey() {
this.checkboxState = this.isExpanded;
},
},
methods: {
/* Either expand or collapse section, based on it's current state */
toggle() {
this.checkboxState = !this.checkboxState;
},
/* Check that row & column span is valid, and not over the max */
checkSpanNum(span, classPrefix) {
const maxSpan = 6;
@ -92,44 +122,14 @@ export default {
return userCss ? userCss.replace(/[^a-zA-Z0-9- :;.]/g, '') : '';
},
/* Returns local storage collapse state data, and if not yet set then initialized is */
initialiseStorage() {
const storageKey = localStorageKeys.COLLAPSE_STATE;
/* Initialize function will create and set a blank object to storage */
const initStorage = () => localStorage.setItem(storageKey, JSON.stringify({}));
locallyStoredCollapseStates() {
// If not yet set, then call initialize
if (!localStorage[storageKey]) {
initStorage();
if (!localStorage[localStorageKeys.COLLAPSE_STATE]) {
localStorage.setItem(localStorageKeys.COLLAPSE_STATE, JSON.stringify({}));
return {};
}
// Otherwise, return value of local storage
return JSON.parse(localStorage[storageKey]);
},
/* If specified by user, return conf collapse state, otherwise check local storage */
getCollapseState() {
if (this.collapsed !== undefined) return !this.collapsed; // Check users config
const collapseStateObject = this.initialiseStorage(); // Check local storage
if (collapseStateObject[this.uniqueKey] !== undefined) {
return collapseStateObject[this.uniqueKey];
}
// Nothing specified, return Open
return true;
},
/* When section collapsed, update local storage, to remember for next time */
setCollapseState(id, newState) {
// Get the current localstorage collapse state object
const collapseState = JSON.parse(localStorage[localStorageKeys.COLLAPSE_STATE]);
// Add the new state to it
collapseState[id] = newState;
// Stringify, and set the new object into local storage
localStorage.setItem(localStorageKeys.COLLAPSE_STATE, JSON.stringify(collapseState));
},
/* Called when collapse state changes, trigger local storage update if needed */
collapseChanged(whatChanged) {
this.isExpanded = whatChanged.srcElement.checked;
if (this.collapseState === undefined) { // Only run, if user hasn't manually set prop
this.initialiseStorage();
this.setCollapseState(this.uniqueKey.toString(), this.isExpanded);
}
return JSON.parse(localStorage[localStorageKeys.COLLAPSE_STATE]);
},
openEditModal() {
this.$emit('openEditSection');
@ -216,7 +216,8 @@ export default {
vertical-align: middle;
margin-right: .7rem;
transform: translateY(-2px);
transition: transform .2s ease-out;
opacity: 0.3;
transition: all 0.4s ease-in-out;
}
}
@ -254,6 +255,16 @@ export default {
top: 0.5rem;
margin-left: 0.2rem;
margin-right: 0.2rem;
opacity: 0.3;
transition: all 0.4s ease-in-out;
}
/* On section hover, set interface icons to full visible */
&:hover {
.edit-mode-item, label.lbl-toggle::before {
opacity: 1;
transition: all 0.2s ease-out;
}
}
/* Makes sections fill available space */

View File

@ -94,6 +94,7 @@
v-click-outside="closeContextMenu"
@openEditSection="openEditSection"
@navigateToSection="navigateToSection"
@expandCollapseSection="expandCollapseSection"
@removeSection="removeSection"
/>
</Collapsable>
@ -250,6 +251,12 @@ export default {
router.push({ path: `/home/${sectionIdentifier}` });
this.closeContextMenu();
},
/* Toggle sections collapse state */
expandCollapseSection() {
const secElem = this.$refs[this.sectionRef];
if (secElem) secElem.toggle();
this.closeContextMenu();
},
/* Open the Section Edit Menu */
openEditSection() {
this.editMenuOpen = true;

View File

@ -12,6 +12,10 @@
<EditIcon />
<span>{{ $t('context-menus.section.edit-section') }}</span>
</li>
<li @click="expandCollapseSection">
<ExpandCollapseIcon />
<span>{{ $t('context-menus.section.expand-collapse') }}</span>
</li>
<li v-if="isEditMode" @click="removeSection">
<BinIcon />
<span>{{ $t('context-menus.section.remove-section') }}</span>
@ -26,6 +30,7 @@
import EditIcon from '@/assets/interface-icons/config-edit-json.svg';
import BinIcon from '@/assets/interface-icons/interactive-editor-remove.svg';
import SameTabOpenIcon from '@/assets/interface-icons/open-current-tab.svg';
import ExpandCollapseIcon from '@/assets/interface-icons/section-expand-collapse.svg';
export default {
name: 'ContextMenu',
@ -33,6 +38,7 @@ export default {
EditIcon,
BinIcon,
SameTabOpenIcon,
ExpandCollapseIcon,
},
props: {
posX: Number, // The X coordinate for positioning
@ -56,6 +62,9 @@ export default {
openEditSectionMenu() {
this.$emit('openEditSection');
},
expandCollapseSection() {
this.$emit('expandCollapseSection');
},
removeSection() {
this.$emit('removeSection');
},

View File

@ -20,7 +20,6 @@
</button>
</div>
</div>
<KeyboardShortcutInfo />
<AppInfoModal />
</section>
</template>
@ -32,7 +31,6 @@ import ThemeSelector from '@/components/Settings/ThemeSelector';
import LayoutSelector from '@/components/Settings/LayoutSelector';
import ItemSizeSelector from '@/components/Settings/ItemSizeSelector';
import AuthButtons from '@/components/Settings/AuthButtons';
import KeyboardShortcutInfo from '@/components/Settings/KeyboardShortcutInfo';
import AppInfoModal from '@/components/Configuration/AppInfoModal';
import IconOpen from '@/assets/interface-icons/config-open-settings.svg';
import IconClose from '@/assets/interface-icons/config-close.svg';
@ -57,7 +55,6 @@ export default {
LayoutSelector,
ItemSizeSelector,
AuthButtons,
KeyboardShortcutInfo,
AppInfoModal,
IconOpen,
IconClose,

View File

@ -106,15 +106,17 @@ export default {
mounted() {
const initialTheme = this.getInitialTheme();
this.selectedTheme = initialTheme;
// Pass all user custom stylesheets to the themehelper
const added = Object.keys(this.externalThemes).map(
name => this.themeHelper.add(name, this.externalThemes[name]),
);
// Quicker loading, if the theme is local we can apply it immidiatley
if (this.isThemeLocal(initialTheme)) {
this.updateTheme(initialTheme);
}
// If it's an external stylesheet, then wait for promise to resolve
} else if (initialTheme !== Defaults.theme) {
if (this.externalThemes && Object.entries(this.externalThemes).length > 0) {
const added = Object.keys(this.externalThemes).map(
name => this.themeHelper.add(name, this.externalThemes[name]),
);
// Once, added, then apply users initial theme
Promise.all(added).then(() => {
this.updateTheme(initialTheme);
});

View File

@ -58,7 +58,7 @@ export default {
if (!this.ipAddress) {
this.getUsersIpAddress(); return;
}
this.defaultTimeout = 20000;
this.defaultTimeout = 200000;
const options = { Authorization: `Basic ${this.apiKey}` };
this.makeRequest(this.endpoint, options).then(this.processData);
},

View File

@ -36,6 +36,9 @@ export default {
if (this.options.customCityName) return this.options.customCityName;
return this.timeZone.split('/')[1].replaceAll('_', ' ');
},
showSeconds() {
return !this.options.hideSeconds;
},
},
methods: {
update() {
@ -48,7 +51,7 @@ export default {
timeZone: this.timeZone,
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
...(this.showSeconds && { second: 'numeric' }),
}).format();
},
/* Get and format the date */

View File

@ -31,7 +31,6 @@
</template>
<script>
import axios from 'axios';
import WidgetMixin from '@/mixins/WidgetMixin';
import { widgetApiEndpoints } from '@/utils/defaults';
@ -91,16 +90,7 @@ export default {
methods: {
/* Make GET request to Rss2Json */
fetchData() {
axios.get(this.endpoint)
.then((response) => {
this.processData(response.data);
})
.catch((error) => {
this.error('Unable to RSS feed', error);
})
.finally(() => {
this.finishLoading();
});
this.makeRequest(this.endpoint).then(this.processData);
},
/* Assign data variables to the returned data */
processData(data) {

View File

@ -28,7 +28,7 @@
</div>
<div class="match-row" v-for="match in matches" :key="match.id">
<!-- Banner Image -->
<div class="match-thumbnail-wrap">
<div class="match-thumbnail-wrap" v-if="!hideImage">
<img :src="match.thumbnail" :alt="`${match.title} Banner Image`" class="match-thumbnail" />
</div>
<!-- Team Scores -->
@ -101,6 +101,9 @@ export default {
pastOrFuture() {
return this.options.pastOrFuture || 'past';
},
hideImage() {
return this.options.hideImage || false;
},
endpoint() {
this.initiate();
const endpoint = widgetApiEndpoints.sportsScores;

View File

@ -26,6 +26,9 @@ const HomeMixin = {
modalOpen() {
return this.$store.state.modalOpen;
},
pageId() {
return (this.subPageInfo && this.subPageInfo.pageId) ? this.subPageInfo.pageId : 'home';
},
},
data: () => ({
searchValue: '',

View File

@ -41,13 +41,13 @@ export default {
size() {
const validSizes = ['small', 'medium', 'large'];
if (this.itemSize && validSizes.includes(this.itemSize)) return this.itemSize;
return this.appConfig.iconSize || defaultSize;
return this.$store.getters.iconSize || defaultSize;
},
/* Determines if user has enabled online status checks */
enableStatusCheck() {
const globalPref = this.appConfig.statusCheck || false;
const itemPref = this.item.statusCheck || false;
return itemPref || globalPref;
const itemPref = this.item.statusCheck;
return typeof itemPref === 'boolean' ? itemPref : globalPref;
},
/* Determine how often to re-fire status checks */
statusCheckInterval() {

View File

@ -20,7 +20,7 @@ const WidgetMixin = {
overrideUpdateInterval: null,
disableLoader: false, // Prevent ever showing the loader
updater: null, // Stores interval
defaultTimeout: 1000,
defaultTimeout: 10000,
}),
/* When component mounted, fetch initial data */
mounted() {

View File

@ -11,7 +11,6 @@ import { Progress } from 'rsup-progress';
// Import views, that are not lazy-loaded
import Home from '@/views/Home.vue';
import ConfigAccumulator from '@/utils/ConfigAccumalator';
// Import helper functions, config data and defaults
import { isAuthEnabled, isLoggedIn, isGuestAccessEnabled } from '@/utils/Auth';
@ -19,7 +18,8 @@ import { makePageSlug, makePageName } from '@/utils/ConfigHelpers';
import { metaTagData, startingView, routePaths } from '@/utils/defaults';
import ErrorHandler from '@/utils/ErrorHandler';
import { pages } from '../public/conf.yml';
// Import data from users conf file. Note that rebuild is required for this to update.
import { pages, pageInfo, appConfig } from '../public/conf.yml';
Vue.use(Router);
const progress = new Progress({ color: 'var(--progress-bar)' });
@ -32,17 +32,6 @@ const isAuthenticated = () => {
return (!authEnabled || userLoggedIn || guestEnabled);
};
const getConfig = () => {
const Accumulator = new ConfigAccumulator();
return {
appConfig: Accumulator.appConfig(),
pageInfo: Accumulator.pageInfo(),
pages: Accumulator.pages(),
};
};
const { appConfig, pageInfo } = getConfig();
/* Get the users chosen starting view from app config, or return default */
const getStartingView = () => appConfig.startingView || startingView;
@ -61,7 +50,7 @@ const getStartingComponent = () => {
/* Returns the meta tags for each route */
const makeMetaTags = (defaultTitle) => ({
title: pageInfo.title || defaultTitle,
title: pageInfo && pageInfo.title ? pageInfo.title : defaultTitle,
metaTags: metaTagData,
});
@ -73,10 +62,12 @@ const makeSubConfigPath = (rawPath) => {
/* For each additional config file, create routes for home, minimal and workspace views */
const makeMultiPageRoutes = (userPages) => {
if (!userPages) return [];
// If no multi pages specified, or is not array, then return nothing
if (!userPages || !Array.isArray(userPages)) return [];
const multiPageRoutes = [];
// For each user page, create an additional route
userPages.forEach((page) => {
if (!page.name || !page.path) {
if (!page.name || !page.path) { // Sumin not right, show warning
ErrorHandler('Additional pages must have both a `name` and `path`');
}
// Props to be passed to home mixin

View File

@ -89,7 +89,7 @@ const store = new Vuex.Store({
perms.allowSaveLocally = false;
}
// Disable saving changes to disk, only
if (appConfig.preventWriteToDisk || !isUserAdmin) {
if (appConfig.preventWriteToDisk || !isUserAdmin()) {
perms.allowWriteToDisk = false;
}
// Legacy Option: Will be removed in V 2.1.0

View File

@ -1357,7 +1357,7 @@ html[data-theme="dashy-docs"] {
}
}
html[data-theme="adventure"] {
html[data-theme="adventure"], html[data-theme="adventure-basic"] {
// Main colors
--primary: #ffffffe6;
--background: #0b1021;
@ -1376,11 +1376,6 @@ html[data-theme="adventure"] {
--item-group-shadow: none;
--item-group-background: none;
--item-group-outer-background: none;
// Background Image
body {
background: url('https://i.ibb.co/wdqSsGh/adventure-bg.jpg');
background-size: cover;
}
// Remove background from certain components
div.home, div.options-outer, div.options-container, section.filter-container,
section.settings-outer, div.show-hide-container.hide-btn, div.show-hide-container.show-btn {
@ -1401,6 +1396,14 @@ html[data-theme="adventure"] {
}
}
html[data-theme="adventure"] {
// Background Image
body {
background: url('https://i.ibb.co/wdqSsGh/adventure-bg.jpg');
background-size: cover;
}
}
html[data-theme="color-block"] {
// Main colors
--primary: #E94560;

View File

@ -40,11 +40,11 @@ export default class ConfigAccumulator {
usersAppConfig = appConfigFile;
}
// Some settings have their own local storage keys, apply them here
usersAppConfig.layout = appConfigFile.layout
|| localStorage[localStorageKeys.LAYOUT_ORIENTATION]
usersAppConfig.layout = localStorage[localStorageKeys.LAYOUT_ORIENTATION]
|| appConfigFile.layout
|| defaultLayout;
usersAppConfig.iconSize = appConfigFile.iconSize
|| localStorage[localStorageKeys.ICON_SIZE]
usersAppConfig.iconSize = localStorage[localStorageKeys.ICON_SIZE]
|| appConfigFile.iconSize
|| defaultIconSize;
// Don't let users modify users locally
if (appConfigFile.auth) usersAppConfig.auth = appConfigFile.auth;
@ -60,7 +60,7 @@ export default class ConfigAccumulator {
try { localPageInfo = JSON.parse(localStorage[localStorageKeys.PAGE_INFO]); }
catch (e) { ErrorHandler('Malformed pageInfo data in local storage'); }
}
const filePageInfo = this.conf ? this.conf.pageInfo || {} : {};
const filePageInfo = (this.conf && this.conf.pageInfo) ? this.conf.pageInfo : {};
return { ...defaultPageInfo, ...filePageInfo, ...localPageInfo };
}

View File

@ -322,7 +322,7 @@
"description": "If true, font-awesome will be loaded, if false they will not be. If left empty, icons will only be loaded if needed"
},
"enableMaterialDesignIcons": {
"title": "Enable Font-Awesome?",
"title": "Enable Material Design Icons?",
"type": "boolean",
"default": false,
"description": "If true, material-design-icons will be loaded, if false they will not be. If left empty, icons will only be loaded if needed"
@ -475,6 +475,11 @@
"title": "Client ID",
"type": "string",
"description": "The Client ID of the client you created for use with Dashy"
},
"legacySupport": {
"title": "Legacy Support",
"type": "boolean",
"description": "If using Keycloak 17 or older, then set this to true"
}
}
}
@ -849,6 +854,15 @@
"title": "Item ID",
"type": "string",
"description": "Unique ID for each item. Generated automatically, shouldn't need to be set manually."
},
"subItems": {
"title": "Sub-Items",
"type": "array",
"description": "List of sub-items",
"items": {
"type": "object",
"properties": {}
}
}
}
}

View File

@ -12,9 +12,12 @@ const getAppConfig = () => {
class KeycloakAuth {
constructor() {
const { auth } = getAppConfig();
const { serverUrl, realm, clientId } = auth.keycloak;
const {
serverUrl, realm, clientId, legacySupport,
} = auth.keycloak;
const url = legacySupport ? `${serverUrl}/auth` : serverUrl;
const initOptions = {
url: `${serverUrl}`, realm, clientId, onLoad: 'login-required',
url, realm, clientId, onLoad: 'login-required',
};
this.keycloakClient = Keycloak(initOptions);

View File

@ -82,6 +82,7 @@ module.exports = {
'material-dark-original',
'high-contrast-dark',
'high-contrast-light',
'adventure-basic',
'basic',
],
/* Default color options for the theme configurator swatches */

View File

@ -34,7 +34,7 @@
:title="section.name"
:icon="section.icon || undefined"
:displayData="getDisplayData(section)"
:groupId="`section-${index}`"
:groupId="`${pageId}-section-${index}`"
:items="filterTiles(section.items, searchValue)"
:widgets="section.widgets"
:searchTerm="searchValue"