mirror of
https://github.com/Lissy93/dashy.git
synced 2025-07-23 13:45:33 +02:00
🔀 Merge pull request #203 from Lissy93/FEATURE/smart-sort
[FEATURE] Adds support for custom sort order for items
This commit is contained in:
commit
4004b1cf8d
6
.github/CHANGELOG.md
vendored
6
.github/CHANGELOG.md
vendored
@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## ✨ 1.7.2 - Item Sort Options [PR #203](https://github.com/Lissy93/dashy/pull/203)
|
||||
- Adds option for user to specify `sortBy` to order items within a section
|
||||
- Can sort by last clicked, most used, alphabetically and more
|
||||
- And fixes UI of the item tooltip and, if specified, will show Provider in the tooltip
|
||||
- Also improves error logging and console warning message
|
||||
|
||||
## 🐛 1.7.1 - Lots of Tiny Fixes and Improvements [PR #200](https://github.com/Lissy93/dashy/pull/201)
|
||||
- Removes background in console art
|
||||
- Updates auto environmental variables
|
||||
|
34
README.md
34
README.md
@ -74,7 +74,7 @@
|
||||
- 🛩️ A minimal view, for use as a fast-loading browser startpage
|
||||
- 🖱️ Choose how to launch apps, either new tab, same tab, a pop-up modal or in the workspace view
|
||||
- 🌎 Multi-language support, with more languages being added regularly
|
||||
- 📏 Customizable layout, sizes, text, component visibility, behavior and colors etc
|
||||
- 📏 Customizable layout, sizes, text, component visibility, sort order, behavior etc
|
||||
- 🖼️ Option for full-screen background image, custom nav-bar links, html footer, title, and more
|
||||
- 🚀 Easy to setup with Docker, or on bare metal, or with 1-Click cloud deployment
|
||||
- ⚙️ Easy single-file YAML-based configuration, with option to configure app directly through the UI
|
||||
@ -214,7 +214,7 @@ Dashy comes with a number of built-in themes, but it's also easy to make you're
|
||||
|
||||
> For full iconography documentation, see: [**Icons**](./docs/icons.md)
|
||||
|
||||
Both sections and items can have an icon associated with them, and defined under the `icon` attribute. There are many options for icons, including Font Awesome support, automatic fetching from favicon, programmatically generated icons and direct local or remote URLs.
|
||||
Both sections and items can have an icon associated with them, and defined under the `icon` attribute. There are many options for icons, including Font Awesome support, automatic fetching from favicon, emojis, programmatically generated icons and direct local or remote URLs.
|
||||
|
||||
<p align="center">
|
||||
<img width="400" src="https://i.ibb.co/GTVmZnc/dashy-example-icons.png" />
|
||||
@ -222,11 +222,12 @@ Both sections and items can have an icon associated with them, and defined under
|
||||
|
||||
- **Favicon**: Set `icon: favicon` to fetch a services icon automatically from the URL of the corresponding application
|
||||
- **Font-Awesome**: To use any font-awesome icon, specify the category, followed by the icon name, e.g. `fas fa-rocket` or `fab fa-monero`. You can also use Pro icons if you have a license key, just set it under `appConfig.fontAwesomeKey`
|
||||
- **Generative**: Setting `icon: generative`, will generate a unique for a given service, based on it's URL or IP
|
||||
- **Emoji**: Use an emoji as a tile icon, by putting the emoji's code as the icon attribute. Emojis can be specified either as emojis (`🚀`), unicode (`'U+1F680'`) or shortcode (`':rocket:'`).
|
||||
- **URL**: You can also pass in a URL to an icon asset, hosted either locally or using any CDN service. E.g. `icon: https://i.ibb.co/710B3Yc/space-invader-x256.png`.
|
||||
- **Local Image**: To use a local image, store it in `./public/item-icons/` (or create a volume in Docker: `-v /local/image/directory:/app/public/item-icons/`) , and reference it by name and extension - e.g. set `icon: image.png` to use `./public/item-icon/image.png`. You can also use sub-folders here.
|
||||
- **Material Design Icons**: You can also use any icon from [materialdesignicons.com](https://dev.materialdesignicons.com/icons) by setting the icon to `mdi-[icon-name]`.
|
||||
- **Simple Icons**: Use any brand/ logo icon from [simpleicons.org](https://simpleicons.org/) by setting the icon to `si-[icon-name]`
|
||||
- **Emoji**: Use an emoji as a tile icon, by putting the emoji's code as the icon attribute. Emojis can be specified either as emojis (`🚀`), unicode (`'U+1F680'`) or shortcode (`':rocket:'`)
|
||||
- **Generative**: Setting `icon: generative`, will generate a unique logo for a given service, based on it's specified URL or IP
|
||||
- **URL**: You can also pass in a URL to an icon asset, hosted either locally or using any CDN service. E.g. `icon: https://i.ibb.co/710B3Yc/space-invader-x256.png`
|
||||
- **Local Image**: To use a local image, store it in `./public/item-icons/` (or create a volume in Docker: `-v /local/image/directory:/app/public/item-icons/`) , and reference it by name and extension - e.g. set `icon: image.png` to use `./public/item-icon/image.png`. You can also use sub-folders here
|
||||
- **Material Design Icons**: You can also use any icon from [materialdesignicons.com](https://dev.materialdesignicons.com/icons) by setting the icon to `mdi-[icon-name]`
|
||||
|
||||
**[⬆️ Back to Top](#dashy)**
|
||||
|
||||
@ -254,7 +255,7 @@ You can also specify an time interval in seconds under `appConfig.statusCheckInt
|
||||
|
||||
> For full authentication documentation, see: [**Authentication**](./docs/authentication.md)
|
||||
|
||||
Dashy now has full support for [Keycloak](https://www.keycloak.org/)!
|
||||
Dashy now has full support for secure single-sign-on using [Keycloak](https://www.keycloak.org/)! See [setup docs](/docs/authentication.md#keycloak) for a full usage guide
|
||||
|
||||
There is also a simple login feature for basic access control, which doesn't require any additional setup. To enable this feature, add an `auth` attribute under `appConfig`, containing an array of `users`, each with a username, SHA-256 hashed password and optional user type.
|
||||
|
||||
@ -269,7 +270,10 @@ appConfig:
|
||||
|
||||
**Guest Access**: By default, when authentication is configured no user can access your dashboard without first logging in. If you would like to allow for read-only access by unauthenticated users, then you can enable guest mode, by setting `appConfig.auth.enableGuestAccess: true`.
|
||||
|
||||
**Note**: Using the above method involves access control being handled on the frontend, and therefore in security-critical situations, it is recommended to use an alternate method for authentication. Keycloak is [natively supported](docs/authentication.md#keycloak), but you could also use [Authelia](https://www.authelia.com/), a VPN or web server and firewall rules. Instructions for all of these can be found [in the docs](docs/authentication.md#alternative-authentication-methods).
|
||||
**Granular Controls**: With basic login, it is also possible to control which sections are visible to which users. Under the `displayData` property of a section, you can pass an array of usernames to one of the following attributes:
|
||||
- `hideForUsers` - Section will be visible to all users, except for those specified in this list
|
||||
- `showForUsers` - Section will be hidden from all users, except for those specified in this list
|
||||
- `hideForGuests` - Section will be visible for all logged in users, but not for guests (if guest access is enabled)
|
||||
|
||||
<p align="center">
|
||||
<img
|
||||
@ -280,10 +284,7 @@ appConfig:
|
||||
/>
|
||||
</p>
|
||||
|
||||
**Granular Controls**: With basic login, it is also possible to control which sections are visible to which users. Under the `displayData` property of a section, you can pass an array of usernames to one of the following attributes:
|
||||
- `hideForUsers` - Section will be visible to all users, except for those specified in this list
|
||||
- `showForUsers` - Section will be hidden from all users, except for those specified in this list
|
||||
- `hideForGuests` - Section will be visible for all logged in users, but not for guests (if guest access is enabled)
|
||||
**Note**: Using the above method involves access control being handled on the frontend, and therefore in security-critical situations, it is recommended to use an alternate method for authentication. Keycloak is [natively supported](docs/authentication.md#keycloak), but you could also use [Authelia](https://www.authelia.com/), a VPN or web server and firewall rules. Instructions for all of these can be found [in the docs](docs/authentication.md#alternative-authentication-methods).
|
||||
|
||||
**[⬆️ Back to Top](#dashy)**
|
||||
|
||||
@ -382,14 +383,16 @@ Hit `Esc` at anytime to close any open apps, clear the search field, or hide any
|
||||
## Config Editor ⚙️
|
||||
> For full config documentation, see: [**Configuring**](./docs/configuring.md)
|
||||
|
||||
From the Settings Menu in Dashy, you can download, backup, edit and rest your config. An interactive editor makes editing the config file easy, it will tell you if you've got any errors. After making your changes, you can either apply them locally, or export into your main config file. After saving to the config file to the disk, the app will be rebuilt automatically, you can also manually trigger a rebuild from the Settings Menu.
|
||||
From the Settings Menu in Dashy, you can download, backup, edit and rest your config. The editor will tell you if you've got any validation warnings.
|
||||
|
||||
A full list of available config options can be found [here](./docs/configuring.md). It's recommend to make a backup of your configuration, as you can then restore it into a new instance of Dashy, without having to set it up again. [json2yaml](https://www.json2yaml.com/) is very useful for converting between YAML to JSON and visa versa.
|
||||
|
||||
<p align="center">
|
||||
<img alt="Workspace view demo" src="https://raw.githubusercontent.com/Lissy93/dashy/master/docs/assets/config-editor-demo.gif" width="600" />
|
||||
<img alt="Config Editor demo" src="https://raw.githubusercontent.com/Lissy93/dashy/master/docs/assets/config-editor-demo.gif" width="600" />
|
||||
</p>
|
||||
|
||||
**Update:** A new and improved drag-and-drop UI editor is in progress, and should be released within the next couple of weeks!
|
||||
|
||||
**[⬆️ Back to Top](#dashy)**
|
||||
|
||||
---
|
||||
@ -637,6 +640,7 @@ The following features and tasks are planned for the near future.
|
||||
## Alternatives 🙌
|
||||
|
||||
There are a few self-hosted web apps, that serve a similar purpose to Dashy. If you're looking for a dashboard, and Dashy doesn't meet your needs, I highly recommend you check these projects out!
|
||||
- [Flame](https://github.com/pawelmalak/flame) by @pawelmalak (`MIT`)
|
||||
- [HomeDash2](https://lamarios.github.io/Homedash2)
|
||||
- [Homer](https://github.com/bastienwirtz/homer) (`Apache License 2.0`)
|
||||
- [Organizr](https://organizr.app/) (`GPL-3.0 License`)
|
||||
|
@ -78,6 +78,7 @@ To disallow any changes from being written to disk via the UI config editor, set
|
||||
**`allowConfigEdit`** | `boolean` | _Optional_ | Should prevent / allow the user to write configuration changes to the conf.yml from the UI. When set to `false`, the user can only apply changes locally using the config editor within the app, whereas if set to `true` then changes can be written to disk directly through the UI. Defaults to `true`. Note that if authentication is enabled, the user must be of type `admin` in order to apply changes globally.
|
||||
**`enableErrorReporting`** | `boolean` | _Optional_ | Enable reporting of unexpected errors and crashes. This is off by default, and **no data will ever be captured unless you explicitly enable it**. Turning on error reporting helps previously unknown bugs get discovered and fixed. Dashy uses [Sentry](https://github.com/getsentry/sentry) for error reporting. Defaults to `false`.
|
||||
**`sentryDsn`** | `boolean` | _Optional_ | If you need to monitor errors in your instance, then you can use Sentry to collect and process bug reports. Sentry can be self-hosted, or used as SaaS, once your instance is setup, then all you need to do is pass in the DSN here, and enable error reporting. You can learn more on the [Sentry DSN Docs](https://docs.sentry.io/product/sentry-basics/dsn-explainer/). Note that this will only ever be used if `enableErrorReporting` is explicitly enabled.
|
||||
**`disableSmartSort`** | `boolean` | _Optional_ | For the most-used and last-used app sort functions to work, a basic open-count is stored in local storage. If you do not want this to happen, then disable smart sort here, but you wil no longer be able to use these sort options. Defaults to `false`.
|
||||
**`disableUpdateChecks`** | `boolean` | _Optional_ | If set to true, Dashy will not check for updates. Defaults to `false`.
|
||||
**`disableServiceWorker`** | `boolean` | _Optional_ | Service workers cache web applications to improve load times and offer basic offline functionality, and are enabled by default in Dashy. The service worker can sometimes cause older content to be cached, requiring the app to be hard-refreshed. If you do not want SW functionality, or are having issues with caching, set this property to `true` to disable all service workers.
|
||||
**`disableContextMenu`** | `boolean` | _Optional_ | If set to `true`, the custom right-click context menu will be disabled. Defaults to `false`.
|
||||
@ -166,7 +167,7 @@ For more info, see the **[Authentication Docs](/docs/authentication.md)**
|
||||
**`statusCheck`** | `boolean` | _Optional_ | When set to `true`, Dashy will ping the URL associated with the current service, and display its status as a dot next to the item. The value here will override `appConfig.statusCheck` so you can turn off or on checks for a given service. Defaults to `appConfig.statusCheck`, falls back to `false`
|
||||
**`statusCheckUrl`** | `string` | _Optional_ | If you've enabled `statusCheck`, and want to use a different URL to what is defined under the item, then specify it here
|
||||
**`statusCheckHeaders`** | `object` | _Optional_ | If you're endpoint requires any specific headers for the status checking, then define them here
|
||||
**`statusCheckAllowInsecure`** | `boolean` | By default, any request to insecure content will be blocked. Setting this option to `true` will disable the `rejectUnauthorized` option, enabling you to ping non-HTTPS services. Should only be used when needed, and for URLs that you know are safe. Defaults to `false`
|
||||
**`statusCheckAllowInsecure`** | `boolean` | _Optional_ | By default, any request to insecure content will be blocked. Setting this option to `true` will disable the `rejectUnauthorized` option, enabling you to ping non-HTTPS services for the current item. Defaults to `false`
|
||||
**`color`** | `string` | _Optional_ | An optional color for the text and font-awesome icon to be displayed in. Note that this will override the current theme and so may not display well
|
||||
**`backgroundColor`** | `string` | _Optional_ | An optional background fill color for the that given item. Again, this will override the current theme and so might not display well against the background
|
||||
**`provider`** | `string` | _Optional_ | The name of the provider for a given service, useful for when including hosted apps. In some themes, this is visible under the item name
|
||||
@ -177,12 +178,13 @@ For more info, see the **[Authentication Docs](/docs/authentication.md)**
|
||||
|
||||
**Field** | **Type** | **Required**| **Description**
|
||||
--- | --- | --- | ---
|
||||
**`sortBy`** | `string` | _Optional_ | The sort order for items within the current section. By default items are displayed in the order in which they are listed in within the config. The following sort options are supported: `most-used` (most opened apps first), `last-used` (the most recently used apps), `alphabetical`, `reverse-alphabetical` and `default`
|
||||
**`collapsed`** | `boolean` | _Optional_ | If true, the section will be collapsed initially, and will need to be clicked to open. Useful for less regularly used, or very long sections. Defaults to `false`
|
||||
**`color`** | `string` | _Optional_ | A custom accent color for the section, as a hex code or HTML color (e.g. `#fff`)
|
||||
**`customStyles`** | `string` | _Optional_ | Custom CSS properties that should be applied to that section, e.g. `border: 2px dashed #ff0000;`
|
||||
**`itemSize`** | `string` | _Optional_ | Specify the size for items within this group, either `small`, `medium` or `large`. Note that this will overide any settings specified through the UI
|
||||
**`rows`** | `number` | _Optional_ | Height of the section, specified as the number of rows it should span vertically, e.g. `2`. Defaults to `1`. Max is `5`.
|
||||
**`cols`** | `number` | _Optional_ | Width of the section, specified as the number of columns the section should span horizontally, e.g. `2`. Defaults to `1`. Max is `5`.
|
||||
**`itemSize`** | `string` | _Optional_ | Specify the size for items within this group, either `small`, `medium` or `large`. Note that this will overide any settings specified through the UI
|
||||
**`color`** | `string` | _Optional_ | A custom accent color for the section, as a hex code or HTML color (e.g. `#fff`)
|
||||
**`customStyles`** | `string` | _Optional_ | Custom CSS properties that should be applied to that section, e.g. `border: 2px dashed #ff0000;`
|
||||
**`sectionLayout`** | `string` | _Optional_ | Specify which CSS layout will be used to responsivley place items. Can be either `auto` (which uses flex layout), or `grid`. If `grid` is selected, then `itemCountX` and `itemCountY` may also be set. Defaults to `auto`
|
||||
**`itemCountX`** | `number` | _Optional_ | The number of items to display per row / horizontally. If not set, it will be calculated automatically based on available space. Can only be set if `sectionLayout` is set to `grid`. Must be a whole number between `1` and `12`
|
||||
**`itemCountY`** | `number` | _Optional_ | The number of items to display per column / vertically. If not set, it will be calculated automatically based on available space. If `itemCountX` is set, then `itemCountY` can be calculated automatically. Can only be set if `sectionLayout` is set to `grid`. Must be a whole number between `1` and `12`
|
||||
@ -196,7 +198,7 @@ For more info, see the **[Authentication Docs](/docs/authentication.md)**
|
||||
|
||||
**Field** | **Type** | **Required**| **Description**
|
||||
--- | --- | --- | ---
|
||||
**`icon`** | `string` | _Optional_ | The icon for a given item or section. Can be a font-awesome icon, favicon, remote URL or local URL. If set to `favicon`, the icon will be automatically fetched from the items website URL. To use font-awesome, specify the category, followed by the icon name, e.g. `fas fa-rocket`, `fab fa-monero` or `fal fa-duck` - note that to use pro icons, you mut specify `appConfig.fontAwesomeKey`. Similarly, you can also use [simple-icons](https://simpleicons.org/) by setting icon to `si-[icon-name]` or [material-design-icons](https://dev.materialdesignicons.com/icons) by setting icon to `mdi-[icon-name]`. If set to `generative`, then a unique icon is generated from the apps URL or IP. You can also use hosted any by specifying it's URL, e.g. `https://i.ibb.co/710B3Yc/space-invader-x256.png`. To use a local image, first store it in `./public/item-icons/` (or `-v /app/public/item-icons/` in Docker) , and reference it by name and extension - e.g. set `image.png` to use `./public/item-icon/image.png`, you can also use sub-folders if you have a lot of icons, to keep them organised.
|
||||
**`icon`** | `string` | _Optional_ | The icon for a given item or section. See [Icon Docs](/docs/icons.md) for all available supported icon types. To auto-fetch icon from a services URL, aet to `favicon`. To use font-awesome, specify the category, followed by the icon name, e.g. `fas fa-rocket`, `fab fa-monero` or `fal fa-duck`. Similarly, for branded icons, you can use [simple-icons](https://simpleicons.org/) by setting icon to `si-[icon-name]` or [material-design-icons](https://dev.materialdesignicons.com/icons) by setting icon to `mdi-[icon-name]`. If set to `generative`, then a unique icon is generated from the apps URL or IP. You can also use hosted any by specifying it's URL, e.g. `https://i.ibb.co/710B3Yc/space-invader-x256.png`. To use a local image, first store it in `./public/item-icons/` (or `-v /app/public/item-icons/` in Docker) , and reference it by name and extension - e.g. set `image.png` to use `./public/item-icon/image.png`, you can also use sub-folders if you have a lot of icons, to keep them organised.
|
||||
|
||||
**[⬆️ Back to Top](#configuring)**
|
||||
|
||||
|
@ -56,15 +56,17 @@ The following section outlines all data that is stored in the browsers, as cooki
|
||||
- `LAYOUT_ORIENTATION` - Preferred section layout, either horizontal, vertical or auto
|
||||
- `COLLAPSE_STATE` - Remembers which sections are collapsed
|
||||
- `ICON_SIZE` - Size of items, either small, medium or large
|
||||
- `THEME: 'theme` - Users applied theme
|
||||
- `THEME` - Users applied theme
|
||||
- `CUSTOM_COLORS` - Any color modifications made to a given theme
|
||||
- `BACKUP_ID` - If a backup has been made, the ID is stored here
|
||||
- `BACKUP_HASH` - A unique hash of the previous backups meta data
|
||||
- `HIDE_SETTINGS` - Lets user hide or show the settings menu
|
||||
- `USERNAME` - If user logged in, store username in order to welcome them
|
||||
- `USERNAME` - If user logged in, store username. Only used to show welcome message, not used for auth
|
||||
- `CONF_SECTIONS` - Array of sections, only used when user applies changes locally
|
||||
- `PAGE_INFO` - Config page info, only used when user applies changes locally
|
||||
- `APP_CONFIG` - App config, only used when user applies changes locally
|
||||
- `MOST_USED` - If smart sort is used to order items by most used, store open count
|
||||
- `LAST_USED` - If smart sort is used to order items by last used, store timestamps
|
||||
|
||||
---
|
||||
|
||||
@ -107,6 +109,18 @@ Dashy supports both basic auth, as well as server-based SSO using Keycloak. Full
|
||||
|
||||
---
|
||||
|
||||
## Disabling Features
|
||||
You may wish to disable features that you don't want to use, if they involve storing data in the browser or making network requests.
|
||||
- To disable update checks (makes external request to GH), set `appConfig.disableUpdateChecks: true`
|
||||
- To disable the service worker (stores cache of app in browser data), set `appConfig.disableServiceWorker: true`
|
||||
- To disable smart-sort (uses local storage), set `appConfig.disableSmartSort: true`
|
||||
- To disable web search (redirect to external / internal content), set `appConfig.disableWebSearch: true`
|
||||
- To keep font-awesome icons disabled (external requests), set `appConfig.enableFontAwesome: false`
|
||||
- To keep error reporting disabled (external requests and data collection), set `appConfig.enableErrorReporting: false`
|
||||
- To keep status checks disabled (external/ internal requests), set `appConfig.statusCheck: false`
|
||||
|
||||
---
|
||||
|
||||
## Reporting a Security Issue
|
||||
If you think you've found a critical issue with Dashy, please send an email to `security@mail.alicia.omg.lol`. You can encrypt it, using [`0688 F8D3 4587 D954 E9E5 1FB8 FEDB 68F5 5C02 83A7`](https://keybase.io/aliciasykes/pgp_keys.asc?fingerprint=0688f8d34587d954e9e51fb8fedb68f55c0283a7). You should receive a response within 48 hours.
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Dashy",
|
||||
"version": "1.7.1",
|
||||
"version": "1.7.2",
|
||||
"license": "MIT",
|
||||
"main": "server",
|
||||
"scripts": {
|
||||
|
@ -49,9 +49,11 @@ import Icon from '@/components/LinkItems/ItemIcon.vue';
|
||||
import ItemOpenMethodIcon from '@/components/LinkItems/ItemOpenMethodIcon';
|
||||
import StatusIndicator from '@/components/LinkItems/StatusIndicator';
|
||||
import ContextMenu from '@/components/LinkItems/ContextMenu';
|
||||
import { localStorageKeys } from '@/utils/defaults';
|
||||
|
||||
export default {
|
||||
name: 'Item',
|
||||
inject: ['config'],
|
||||
props: {
|
||||
id: String, // The unique ID of a tile (e.g. 001)
|
||||
title: String, // The main text of tile, required
|
||||
@ -61,6 +63,7 @@ export default {
|
||||
color: String, // Optional text and icon color, specified in hex code
|
||||
backgroundColor: String, // Optional item background color
|
||||
url: String, // URL to the resource, optional but recommended
|
||||
provider: String, // Optional provider name, for external apps
|
||||
hotkey: Number, // Shortcut for quickly launching app
|
||||
target: { // Where resource will open, either 'newtab', 'sametab' or 'modal'
|
||||
type: String,
|
||||
@ -106,6 +109,11 @@ export default {
|
||||
} else {
|
||||
this.$emit('itemClicked');
|
||||
}
|
||||
// Update the most/ last used ledger, for smart-sorting
|
||||
if (!this.config.appConfig.disableSmartSort) {
|
||||
this.incrementMostUsedCount(this.id);
|
||||
this.incrementLastUsedCount(this.id);
|
||||
}
|
||||
},
|
||||
/* Open custom context menu, and set position */
|
||||
openContextMenu(e) {
|
||||
@ -124,13 +132,16 @@ export default {
|
||||
},
|
||||
/* Returns configuration object for the tooltip */
|
||||
getTooltipOptions() {
|
||||
const hotkeyText = this.hotkey ? `\nPress '${this.hotkey}' to launch` : '';
|
||||
if (!this.description && !this.provider) return {}; // If no description, then skip
|
||||
const description = this.description ? this.description : '';
|
||||
const providerText = this.provider ? `<b>Provider</b>: ${this.provider}` : '';
|
||||
const lb1 = description && providerText ? '<br>' : '';
|
||||
const hotkeyText = this.hotkey ? `<br>Press '${this.hotkey}' to launch` : '';
|
||||
return {
|
||||
disabled: !this.description,
|
||||
content: this.description + hotkeyText,
|
||||
content: providerText + lb1 + description + hotkeyText,
|
||||
trigger: 'hover focus',
|
||||
hideOnTargetClick: true,
|
||||
html: false,
|
||||
html: true,
|
||||
placement: this.statusResponse ? 'left' : 'auto',
|
||||
delay: { show: 600, hide: 200 },
|
||||
classes: 'item-description-tooltip',
|
||||
@ -198,6 +209,20 @@ export default {
|
||||
default: window.open(url, '_blank');
|
||||
}
|
||||
},
|
||||
/* Used for smart-sort when sorting items by most used apps */
|
||||
incrementMostUsedCount(itemId) {
|
||||
const mostUsed = JSON.parse(localStorage.getItem(localStorageKeys.MOST_USED) || '{}');
|
||||
let counter = mostUsed[itemId] || 0;
|
||||
counter += 1;
|
||||
mostUsed[itemId] = counter;
|
||||
localStorage.setItem(localStorageKeys.MOST_USED, JSON.stringify(mostUsed));
|
||||
},
|
||||
/* Used for smart-sort when sorting by last used apps */
|
||||
incrementLastUsedCount(itemId) {
|
||||
const lastUsed = JSON.parse(localStorage.getItem(localStorageKeys.LAST_USED) || '{}');
|
||||
lastUsed[itemId] = new Date().getTime();
|
||||
localStorage.setItem(localStorageKeys.LAST_USED, JSON.stringify(lastUsed));
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// If ststus checking is enabled, then check service status
|
||||
@ -389,16 +414,18 @@ export default {
|
||||
|
||||
<!-- An un-scoped style tag, since tooltip is outside this DOM tree -->
|
||||
<style lang="scss">
|
||||
.tooltip {
|
||||
.tooltip.item-description-tooltip {
|
||||
background: var(--description-tooltip-background);
|
||||
border: 1px solid var(--description-tooltip-color);
|
||||
border-radius: var(--curve-factor-small);
|
||||
color: var(--description-tooltip-color);
|
||||
padding: 0.2rem 0.5rem;
|
||||
background: #0b1021cc;
|
||||
border: 1px solid #0b1021;
|
||||
border-radius: 3px;
|
||||
color: #fff;
|
||||
max-width: 250px;
|
||||
z-index: 5;
|
||||
}
|
||||
.tooltip-arrow {
|
||||
border-width: 5px 5px 0 5px;
|
||||
border-color: var(--description-tooltip-color);
|
||||
border-left-color: transparent!important;
|
||||
border-right-color: transparent!important;
|
||||
border-bottom-color: transparent!important;
|
||||
@ -411,7 +438,6 @@ export default {
|
||||
border-style: solid;
|
||||
position: absolute;
|
||||
margin: 5px;
|
||||
border-color: #0b1021cc;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
|
@ -17,9 +17,9 @@
|
||||
:style="gridStyle"
|
||||
>
|
||||
<Item
|
||||
v-for="(item, index) in items"
|
||||
:id="`${index}_${makeId(item.title)}`"
|
||||
:key="`${index}_${makeId(item.title)}`"
|
||||
v-for="(item) in sortedItems"
|
||||
:id="makeId(title, item.title)"
|
||||
:key="makeId(title, item.title)"
|
||||
:url="item.url"
|
||||
:title="item.title"
|
||||
:description="item.description"
|
||||
@ -31,6 +31,7 @@
|
||||
:statusCheckHeaders="item.statusCheckHeaders"
|
||||
:itemSize="newItemSize"
|
||||
:hotkey="item.hotkey"
|
||||
:provider="item.provider"
|
||||
:enableStatusCheck="shouldEnableStatusCheck(item.statusCheck)"
|
||||
:statusCheckInterval="getStatusCheckInterval()"
|
||||
:statusCheckAllowInsecure="item.statusCheckAllowInsecure"
|
||||
@ -49,6 +50,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { sortOrder as defaultSortOrder, localStorageKeys } from '@/utils/defaults';
|
||||
import ErrorHandler from '@/utils/ErrorHandler';
|
||||
import Item from '@/components/LinkItems/Item.vue';
|
||||
import Collapsable from '@/components/LinkItems/Collapsable.vue';
|
||||
import IframeModal from '@/components/LinkItems/IframeModal.vue';
|
||||
@ -71,6 +74,28 @@ export default {
|
||||
IframeModal,
|
||||
},
|
||||
computed: {
|
||||
sortOrder() {
|
||||
return this.displayData.sortBy || defaultSortOrder;
|
||||
},
|
||||
/* If the sortBy attribute is specified, then return sorted data */
|
||||
sortedItems() {
|
||||
let { items } = this;
|
||||
if (this.config.appConfig.disableSmartSort) return items;
|
||||
if (this.sortOrder === 'alphabetical') {
|
||||
this.sortAlphabetically(items);
|
||||
} else if (this.sortOrder === 'reverse-alphabetical') {
|
||||
this.sortAlphabetically(items).reverse();
|
||||
} else if (this.sortOrder === 'most-used') {
|
||||
items = this.sortByMostUsed(items);
|
||||
} else if (this.sortOrder === 'last-used') {
|
||||
items = this.sortBLastUsed(items);
|
||||
} else if (this.sortOrder === 'random') {
|
||||
items = this.sortRandomly(items);
|
||||
} else if (this.sortOrder && this.sortOrder !== 'default') {
|
||||
ErrorHandler(`Unknown Sort order '${this.sortOrder}' under '${this.title}'`);
|
||||
}
|
||||
return items;
|
||||
},
|
||||
newItemSize() {
|
||||
return this.displayData.itemSize || this.itemSize;
|
||||
},
|
||||
@ -89,8 +114,9 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
/* Returns a unique lowercase string, based on name, for section ID */
|
||||
makeId(str) {
|
||||
return str.replace(/\s+/g, '-').replace(/[^a-zA-Z ]/g, '').toLowerCase();
|
||||
makeId(sectionStr, itemStr) {
|
||||
const charSum = sectionStr.split('').map((a) => a.charCodeAt(0)).reduce((x, y) => x + y);
|
||||
return `${charSum}_${itemStr.replace(/\s+/g, '-').replace(/[^a-zA-Z ]/g, '').toLowerCase()}`;
|
||||
},
|
||||
/* Opens the iframe modal */
|
||||
triggerModal(url) {
|
||||
@ -105,6 +131,7 @@ export default {
|
||||
const globalPreference = this.config.appConfig.statusCheck || false;
|
||||
return itemPreference !== undefined ? itemPreference : globalPreference;
|
||||
},
|
||||
/* Determine how often to re-fire status checks */
|
||||
getStatusCheckInterval() {
|
||||
let interval = this.config.appConfig.statusCheckInterval;
|
||||
if (!interval) return 0;
|
||||
@ -112,6 +139,31 @@ export default {
|
||||
if (interval < 1) interval = 0;
|
||||
return interval;
|
||||
},
|
||||
/* Sorts items alphabetically using the title attribute */
|
||||
sortAlphabetically(items) {
|
||||
return items.sort((a, b) => (a.title > b.title ? 1 : -1));
|
||||
},
|
||||
/* Sorts items by most used to least used, based on click-count */
|
||||
sortByMostUsed(items) {
|
||||
const usageCount = JSON.parse(localStorage.getItem(localStorageKeys.MOST_USED) || '{}');
|
||||
const gmu = (item) => usageCount[this.makeId(this.title, item.title)] || 0;
|
||||
items.reverse().sort((a, b) => (gmu(a) < gmu(b) ? 1 : -1));
|
||||
return items;
|
||||
},
|
||||
/* Sorts items by most recently used */
|
||||
sortBLastUsed(items) {
|
||||
const usageCount = JSON.parse(localStorage.getItem(localStorageKeys.LAST_USED) || '{}');
|
||||
const glu = (item) => usageCount[this.makeId(this.title, item.title)] || 0;
|
||||
items.reverse().sort((a, b) => (glu(a) < glu(b) ? 1 : -1));
|
||||
return items;
|
||||
},
|
||||
/* Sorts items randomly */
|
||||
sortRandomly(items) {
|
||||
return items
|
||||
.map((value) => ({ value, sort: Math.random() }))
|
||||
.sort((a, b) => a.sort - b.sort)
|
||||
.map(({ value }) => value);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -108,4 +108,6 @@
|
||||
--welcome-popup-text-color: var(--primary);
|
||||
--toast-background: var(--primary);
|
||||
--toast-color: var(--background);
|
||||
--description-tooltip-background: var(--background-darker);
|
||||
--description-tooltip-color: var(--primary);
|
||||
}
|
||||
|
@ -217,6 +217,8 @@ html[data-theme='material-original'] {
|
||||
--heading-text-color: #fff;
|
||||
--status-check-tooltip-background: #f2f2f2;
|
||||
--status-check-tooltip-color: #01579b;
|
||||
--description-tooltip-background: #f2f2f2;
|
||||
--description-tooltip-color: #01579b;
|
||||
--login-form-background: #fff;
|
||||
--about-page-accent: #000;
|
||||
--about-page-color: var(--background-darker);
|
||||
@ -262,6 +264,8 @@ html[data-theme='material-dark-original'] {
|
||||
--scroll-bar-background: #131a1f;
|
||||
--status-check-tooltip-background: #131a1f;
|
||||
--status-check-tooltip-color: #08B0BB;
|
||||
--description-tooltip-background: #131a1f;
|
||||
--description-tooltip-color: #08B0BB;
|
||||
&::-webkit-scrollbar-thumb {
|
||||
border-left: 1px solid #131a1f;
|
||||
}
|
||||
@ -510,6 +514,8 @@ html[data-theme='material'] {
|
||||
--context-menu-secondary-color: #f5f5f5;
|
||||
--transparent-white-50: #00000080;
|
||||
--status-check-tooltip-background: #fff;
|
||||
--description-tooltip-background: #fff;
|
||||
--description-tooltip-color: #473f3f;
|
||||
--side-bar-background-lighter: #0c4eba;
|
||||
--side-bar-item-background: #f5f5f5;
|
||||
|
||||
@ -602,6 +608,8 @@ html[data-theme='material-dark'] {
|
||||
|
||||
--status-check-tooltip-background: #131a1f;
|
||||
--status-check-tooltip-color: #e0e0e0;
|
||||
--description-tooltip-background: #131a1f;
|
||||
--description-tooltip-color: #e0e0e0;
|
||||
--curve-factor: 2px;
|
||||
--curve-factor-navbar: 0;
|
||||
|
||||
@ -673,6 +681,8 @@ html[data-theme='minimal-light'] {
|
||||
--curve-factor-navbar: 8px;
|
||||
--status-check-tooltip-background: #f2f2f2;
|
||||
--status-check-tooltip-color: #000;
|
||||
--description-tooltip-background: #f2f2f2;
|
||||
--description-tooltip-color: #000;
|
||||
--login-form-color: #101931;
|
||||
--about-page-background: var(--background);
|
||||
--about-page-color: var(--background-darker);
|
||||
@ -749,6 +759,7 @@ html[data-theme='vaporware'] {
|
||||
--login-form-color: #09bfe6;
|
||||
--config-settings-background: #100e2c;
|
||||
--status-check-tooltip-background: #100e2c;
|
||||
--description-tooltip-background: #100e2c;
|
||||
|
||||
.home {
|
||||
background: linear-gradient(180deg, rgba(16,14,44,1) 10%, rgba(27,24,79,1) 40%, rgba(16,14,44,1) 100%);
|
||||
@ -832,6 +843,7 @@ html[data-theme='cyberpunk'] {
|
||||
--welcome-popup-background: var(--pink);
|
||||
--welcome-popup-text-color: var(--blue);
|
||||
--status-check-tooltip-background: var(--blue);
|
||||
--description-tooltip-background: var(--blue);
|
||||
--font-headings: 'Audiowide', cursive;
|
||||
}
|
||||
|
||||
|
@ -361,6 +361,11 @@
|
||||
"default": false,
|
||||
"description": "Prevents Dashy from checking for updates"
|
||||
},
|
||||
"disableSmartSort": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Prevents the app storing local click count, required for the last-used and most-used sort orders"
|
||||
},
|
||||
"enableErrorReporting": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
@ -397,6 +402,18 @@
|
||||
"additionalProperties": false,
|
||||
"description": "Optional meta data for customizing a section",
|
||||
"properties": {
|
||||
"sortBy": {
|
||||
"enum": [
|
||||
"default",
|
||||
"most-used",
|
||||
"last-used",
|
||||
"alphabetical",
|
||||
"reverse-alphabetical",
|
||||
"random"
|
||||
],
|
||||
"default": "default",
|
||||
"description": "How to sort items within the section. By default items are displayed in the order in which they are listed in within the config"
|
||||
},
|
||||
"collapsed": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
@ -559,4 +576,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +1,19 @@
|
||||
/* eslint no-console: ["error", { allow: ["log"] }] */
|
||||
/* eslint no-console: ["error", { allow: ["log", "info"] }] */
|
||||
|
||||
export const welcomeMsg = () => {
|
||||
const v = process.env.VUE_APP_VERSION ? `V${process.env.VUE_APP_VERSION}` : '';
|
||||
console.log(`%cDashy ${v} 🚀`, 'color:#00af87; background:#0b1021; font-size:36px; padding: 0.5rem 0.5rem 0; margin: 1rem auto; font-family: Rockwell; border: 2px solid #00af87; border-radius: 4px;font-weight: bold; text-shadow: 1px 1px 1px #00af87bf;');
|
||||
console.log(`%cDashy ${v} 🚀`, 'color:#00af87; background:#0b1021; font-size:1.5rem; padding: 0 0.5rem 0; margin: 1rem auto; font-family: Rockwell; border: 2px solid #00af87; border-radius: 4px;font-weight: bold; text-shadow: 1px 1px 1px #00af87bf;');
|
||||
};
|
||||
|
||||
export const warningMsg = () => {
|
||||
console.log('%c⚠️ Error ⚠️', "background:#21bbca; color:#0b1021; font-size:20px; padding:0.25rem 0.5rem; margin: 1rem auto 0.25rem; font-family: 'Trebuchet MS', Helvetica; border: 2px solid yellow; border-radius: 4px; font-weight: bold;");
|
||||
export const warningMsg = (message) => {
|
||||
console.info(
|
||||
`%c⚠️ Warning ⚠️%c \n${message} \n\n%cThis is likely not an issue with Dashy, but rather your configuration. If you think it is a bug, please open a ticket on GitHub: https://git.io/JukXk`,
|
||||
"color:#ceb73f; background: #ceb73f33; font-size:1.2rem; padding:0.15rem; margin: 0.2rem auto 1rem auto; font-family: Rockwell, Tahoma, 'Trebuchet MS', Helvetica; border: 2px solid #ceb73f; border-radius: 4px; font-weight: bold; text-shadow: 1px 1px 1px #000000bf;",
|
||||
'font-weight: bold; font-size: 0.9rem;color: #ceb73f;',
|
||||
"color: #ceb73f; font-size: 0.6rem; font-family: Tahoma, 'Trebuchet MS', Helvetica;",
|
||||
);
|
||||
};
|
||||
|
||||
export const raiseBug = () => {
|
||||
console.log('%c🐛If you have found a bug, raise an issue on GitHub, at:\nhttps://git.io/JnqPR', "color:#dddd10; font-size: 14px; font-family: 'Trebuchet MS', Helvetica;");
|
||||
console.log('%c🐛If you have found a bug, raise an issue on GitHub, at:\nhttps://git.io/JukXk', "color:#dddd10; font-size: 14px; font-family: 'Trebuchet MS', Helvetica;");
|
||||
};
|
||||
|
@ -1,15 +1,14 @@
|
||||
/* eslint no-console: ["error", { allow: ["warn", "error"] }] */
|
||||
|
||||
import { warningMsg, raiseBug } from '@/utils/CoolConsole';
|
||||
import * as Sentry from '@sentry/vue';
|
||||
import { warningMsg } from '@/utils/CoolConsole';
|
||||
|
||||
/**
|
||||
* Function called when an error happens
|
||||
* If you wish to use an error logging service, put code for it here
|
||||
*/
|
||||
const ErrorHandler = function handler(msg) {
|
||||
warningMsg();
|
||||
console.warn(msg);
|
||||
raiseBug();
|
||||
warningMsg(msg);
|
||||
Sentry.captureMessage(msg);
|
||||
};
|
||||
|
||||
export default ErrorHandler;
|
||||
|
@ -12,11 +12,13 @@
|
||||
import ConfigAccumulator from '@/utils/ConfigAccumalator';
|
||||
import { sentryDsn } from '@/utils/defaults';
|
||||
|
||||
const ErrorTracking = (Vue, router) => {
|
||||
const ErrorReporting = (Vue, router) => {
|
||||
// Fetch users config
|
||||
const appConfig = new ConfigAccumulator().appConfig() || {};
|
||||
// Check if error reporting is enabled. Only proceed if user has turned it on.
|
||||
if (appConfig.enableErrorReporting) {
|
||||
// Get current app version
|
||||
const appVersion = process.env.VUE_APP_VERSION ? `Dashy@${process.env.VUE_APP_VERSION}` : '';
|
||||
// Import Sentry
|
||||
const Sentry = require('@sentry/vue');
|
||||
const { Integrations } = require('@sentry/tracing');
|
||||
@ -32,10 +34,11 @@ const ErrorTracking = (Vue, router) => {
|
||||
}),
|
||||
],
|
||||
tracesSampleRate: 1.0,
|
||||
release: appVersion,
|
||||
});
|
||||
} else {
|
||||
// Error reporting not enabled. Do Nothing.
|
||||
// Error reporting has not been enabled by the user. Do Nothing.
|
||||
}
|
||||
};
|
||||
|
||||
export default ErrorTracking;
|
||||
export default ErrorReporting;
|
||||
|
@ -25,6 +25,8 @@ module.exports = {
|
||||
fontAwesomeKey: '0821c65656',
|
||||
/* Default API to use for fetching of user service favicon icons (if enabled) */
|
||||
faviconApi: 'faviconkit',
|
||||
/* The default sort order for sections */
|
||||
sortOrder: 'default',
|
||||
/* The page paths for each route within the app for the router */
|
||||
routePaths: {
|
||||
home: '/home',
|
||||
@ -91,6 +93,8 @@ module.exports = {
|
||||
BACKUP_HASH: 'backupHash',
|
||||
HIDE_SETTINGS: 'hideSettings',
|
||||
USERNAME: 'username',
|
||||
MOST_USED: 'mostUsed',
|
||||
LAST_USED: 'lastUsed',
|
||||
},
|
||||
/* Key names for cookie identifiers */
|
||||
cookieKeys: {
|
||||
|
Loading…
x
Reference in New Issue
Block a user