dashy/src/mixins/ItemMixin.js

218 lines
7.6 KiB
JavaScript

/** Reusable mixin for items */
import axios from 'axios';
import router from '@/router';
import longPress from '@/directives/LongPress';
import {
openingMethod as defaultOpeningMethod,
serviceEndpoints,
localStorageKeys,
iconSize as defaultSize,
} from '@/utils/defaults';
export default {
directives: {
longPress,
},
props: {
item: Object,
isAddNew: Boolean,
},
data() {
return {
statusResponse: undefined,
contextMenuOpen: false,
contextPos: {
posX: undefined,
posY: undefined,
},
customStyles: {
color: this.item.color,
background: this.item.backgroundColor,
},
};
},
computed: {
appConfig() {
return this.$store.getters.appConfig;
},
isEditMode() {
return this.$store.state.editMode;
},
size() {
const validSizes = ['small', 'medium', 'large'];
if (this.itemSize && validSizes.includes(this.itemSize)) return this.itemSize;
return this.appConfig.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;
},
/* Determine how often to re-fire status checks */
statusCheckInterval() {
let interval = this.item.statusCheckInterval || this.appConfig.statusCheckInterval;
if (!interval) return 0;
if (interval > 60) interval = 60;
if (interval < 1) interval = 0;
return interval;
},
accumulatedTarget() {
return this.target || this.appConfig.defaultOpeningMethod || defaultOpeningMethod;
},
/* Convert config target value, into HTML anchor target attribute */
anchorTarget() {
if (this.isEditMode) return '_self';
const target = this.accumulatedTarget;
switch (target) {
case 'sametab': return '_self';
case 'newtab': return '_blank';
case 'parent': return '_parent';
case 'top': return '_top';
default: return undefined;
}
},
/* Get href for anchor, if not in edit mode, or opening in modal/ workspace */
hyperLinkHref() {
const nothing = '#';
const url = this.url || this.item.url || nothing;
if (this.isEditMode) return nothing;
const noAnchorNeeded = ['modal', 'workspace', 'clipboard'];
return noAnchorNeeded.includes(this.accumulatedTarget) ? nothing : url;
},
/* Pulls together all user options, returns URL + Get params for ping endpoint */
statusCheckApiUrl() {
const {
url,
statusCheckUrl,
statusCheckHeaders,
statusCheckAllowInsecure,
statusCheckAcceptCodes,
statusCheckMaxRedirects,
} = this.item;
const encode = (str) => encodeURIComponent(str);
this.statusResponse = undefined;
// Find base URL, where the API is hosted
const baseUrl = process.env.VUE_APP_DOMAIN || window.location.origin;
// Find correct URL to check, and encode
const urlToCheck = `?&url=${encode(statusCheckUrl || url)}`;
// Get, stringify and encode any headers
const headers = statusCheckHeaders
? `&headers=${encode(JSON.stringify(statusCheckHeaders))}` : '';
// Deterimine if user disabled security
const enableInsecure = statusCheckAllowInsecure ? '&enableInsecure=true' : '';
const acceptCodes = statusCheckAcceptCodes ? `&acceptCodes=${statusCheckAcceptCodes}` : '';
const maxRedirects = statusCheckMaxRedirects ? `&maxRedirects=${statusCheckMaxRedirects}` : '';
// Construct the full API endpoint's URL with GET params
return `${baseUrl}${serviceEndpoints.statusCheck}/${urlToCheck}`
+ `${headers}${enableInsecure}${acceptCodes}${maxRedirects}`;
},
customStyle() {
return `--open-icon:${this.unicodeOpeningIcon};`
+ `color:${this.item.color};`
+ `background:${this.item.backgroundColor}`;
},
},
methods: {
/* Checks if a given service is currently online */
checkWebsiteStatus() {
const endpoint = this.statusCheckApiUrl;
axios.get(endpoint)
.then((response) => {
if (response.data) this.statusResponse = response.data;
})
.catch(() => { // Something went very wrong.
this.statusResponse = {
statusText: 'Failed to make request',
statusSuccess: false,
};
});
},
/* Called when an item is clicked, manages the opening of modal & resets the search field */
itemClicked(e) {
const url = this.url || this.item.url;
if (this.isEditMode) {
// If in edit mode, open settings, and don't launch app
e.preventDefault();
this.openItemSettings();
return;
}
// For certain opening methods, prevent default and manually navigate
if (e.ctrlKey) {
e.preventDefault();
window.open(url, '_blank');
} else if (e.altKey || this.accumulatedTarget === 'modal') {
e.preventDefault();
this.$emit('triggerModal', url);
} else if (this.accumulatedTarget === 'workspace') {
e.preventDefault();
router.push({ name: 'workspace', query: { url } });
} else if (this.accumulatedTarget === 'clipboard') {
e.preventDefault();
navigator.clipboard.writeText(url);
this.$toasted.show(this.$t('context-menus.item.copied-toast'));
}
// Emit event to clear search field, etc
this.$emit('itemClicked');
// Update the most/ last used ledger, for smart-sorting
if (!this.appConfig.disableSmartSort) {
this.incrementMostUsedCount(this.id);
this.incrementLastUsedCount(this.id);
}
},
/* Open item, using specified method */
launchItem(method, link) {
const url = link || this.item.url;
this.contextMenuOpen = false;
switch (method) {
case 'newtab':
window.open(url, '_blank');
break;
case 'sametab':
window.open(url, '_self');
break;
case 'modal':
this.$emit('triggerModal', url);
break;
case 'workspace':
router.push({ name: 'workspace', query: { url } });
break;
case 'clipboard':
navigator.clipboard.writeText(url);
this.$toasted.show(this.$t('context-menus.item.copied-toast'));
break;
default: window.open(url, '_blank');
}
},
/* Open custom context menu, and set position */
openContextMenu(e) {
this.contextMenuOpen = !this.contextMenuOpen;
if (e && window) {
// Calculate placement based on cursor and scroll position
this.contextPos = {
posX: e.clientX + window.pageXOffset,
posY: e.clientY + window.pageYOffset,
};
}
},
/* Closes the context menu, called when user clicks literally anywhere */
closeContextMenu() {
this.contextMenuOpen = false;
},
/* 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));
},
},
};