mirror of https://github.com/rancher/dashboard.git
211 lines
6.3 KiB
JavaScript
211 lines
6.3 KiB
JavaScript
import { mapGetters } from 'vuex';
|
|
import { MANAGEMENT } from '@shell/config/types';
|
|
import { SETTING } from '@shell/config/settings';
|
|
import { createCssVars } from '@shell/utils/color';
|
|
import { setTitle } from '@shell/config/private-label';
|
|
import { setFavIcon, haveSetFavIcon } from '@shell/utils/favicon';
|
|
import { allHash } from '@shell/utils/promise';
|
|
import { fetchInitialSettings } from '@shell/utils/settings';
|
|
import CspAdapterUtils from '@shell/utils/cspAdaptor';
|
|
|
|
export default {
|
|
async fetch() {
|
|
try {
|
|
const res = await allHash({
|
|
// Ensure we read the settings even when we are not authenticated
|
|
globalSettings: fetchInitialSettings(this.$store),
|
|
apps: CspAdapterUtils.fetchCspAdaptorApp(this.$store),
|
|
});
|
|
|
|
// The favicon is implicitly dependent on the initial settings having already been fetched
|
|
if (!haveSetFavIcon()) {
|
|
setFavIcon(this.$store);
|
|
}
|
|
|
|
this.apps = res.apps;
|
|
} catch (e) { }
|
|
|
|
// Setting this up front will remove `computed` churn, and we only care that we've initialised them
|
|
this.haveAppsAndSettings = !!this.apps && !!this.globalSettings;
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
apps: null, haveAppsAndSettings: null, canPaginate: false
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
...mapGetters({ loggedIn: 'auth/loggedIn' }),
|
|
|
|
// added to fix https://github.com/rancher/dashboard/issues/10788
|
|
// because on logout the brand mixin is mounted, but then a management store reset happens
|
|
// since login view get loaded, another fetchInitialSettings get's done
|
|
// which in turn will populate again globalSettings
|
|
globalSettings() {
|
|
return this.$store.getters['management/all'](MANAGEMENT.SETTING);
|
|
},
|
|
|
|
brand() {
|
|
const setting = this.globalSettings?.find((gs) => gs.id === SETTING.BRAND);
|
|
|
|
return setting?.value;
|
|
},
|
|
|
|
color() {
|
|
const setting = this.globalSettings?.find((gs) => gs.id === SETTING.PRIMARY_COLOR);
|
|
|
|
return setting?.value;
|
|
},
|
|
|
|
linkColor() {
|
|
const setting = this.globalSettings?.find((gs) => gs.id === SETTING.LINK_COLOR);
|
|
|
|
return setting?.value;
|
|
},
|
|
|
|
theme() {
|
|
const setting = this.globalSettings?.find((gs) => gs.id === SETTING.THEME);
|
|
|
|
// This handles cases where the settings update after the page loads (like on log out)
|
|
if (setting?.value) {
|
|
return setting?.value;
|
|
}
|
|
|
|
return this.$store.getters['prefs/theme'];
|
|
},
|
|
|
|
cspAdapter() {
|
|
if (!this.canCalcCspAdapter) {
|
|
// We only have a watch on cspAdapter to kick off persisting the brand setting.
|
|
// So we need to ensure we don't return an undefined here... which would match the undefined gave if no csp app was found...
|
|
// .. and wouldn't kick off the watcher
|
|
return '';
|
|
}
|
|
|
|
// Note! this used to be `findBy(this.app)` however for that case we lost reactivity on the collection
|
|
// (computed fires before fetch, fetch happens and update apps, computed would not fire again - even with vue.set)
|
|
// So use `.find` in method instead
|
|
return CspAdapterUtils.hasCspAdapter({ $store: this.$store, apps: this.apps });
|
|
},
|
|
|
|
canCalcCspAdapter() {
|
|
// We need to take consider the loggedIn state, as the brand mixin is used in the logout page where we can be in a mixed state
|
|
// (things in store but user has no auth to make changes)
|
|
return this.loggedIn && this.haveAppsAndSettings;
|
|
}
|
|
},
|
|
|
|
watch: {
|
|
color: {
|
|
handler(neu) {
|
|
if (neu) {
|
|
this.setCustomColor(neu);
|
|
} else {
|
|
this.removeCustomColor();
|
|
}
|
|
},
|
|
immediate: true
|
|
},
|
|
linkColor: {
|
|
handler(neu) {
|
|
if (neu) {
|
|
this.setCustomColor(neu, 'link');
|
|
} else {
|
|
this.removeCustomColor('link');
|
|
}
|
|
},
|
|
immediate: true
|
|
},
|
|
theme: {
|
|
handler() {
|
|
if (this.color) {
|
|
this.setCustomColor(this.color);
|
|
}
|
|
if (this.linkColor) {
|
|
this.setCustomColor(this.linkColor, 'link');
|
|
}
|
|
this.setBodyClass();
|
|
},
|
|
immediate: true
|
|
},
|
|
|
|
cspAdapter: {
|
|
handler(neu) {
|
|
if (!this.canCalcCspAdapter) {
|
|
return;
|
|
}
|
|
|
|
// The brand setting will only get updated if...
|
|
if (neu && !this.brand) {
|
|
// 1) There should be a brand... but there's no brand setting
|
|
const brandSetting = this.globalSettings?.find((gs) => gs.id === SETTING.BRAND);
|
|
|
|
if (brandSetting) {
|
|
brandSetting.value = 'csp';
|
|
brandSetting.save();
|
|
} else {
|
|
const schema = this.$store.getters['management/schemaFor'](MANAGEMENT.SETTING);
|
|
const url = schema?.linkFor('collection');
|
|
|
|
if (url) {
|
|
this.$store.dispatch('management/create', {
|
|
type: MANAGEMENT.SETTING, metadata: { name: SETTING.BRAND }, value: 'csp', default: ''
|
|
}).then((setting) => setting.save());
|
|
}
|
|
}
|
|
}
|
|
},
|
|
immediate: true
|
|
},
|
|
brand: {
|
|
handler() {
|
|
this.setBodyClass();
|
|
},
|
|
immediate: true
|
|
}
|
|
|
|
},
|
|
mounted() {
|
|
this.setBodyClass();
|
|
setTitle();
|
|
},
|
|
methods: {
|
|
setCustomColor(color, name = 'primary') {
|
|
const vars = createCssVars(color, this.theme, name);
|
|
|
|
for (const prop in vars) {
|
|
document.body.style.setProperty(prop, vars[prop]);
|
|
}
|
|
},
|
|
|
|
removeCustomColor(name = 'primary') {
|
|
const vars = createCssVars('rgb(0,0,0)', this.theme, name);
|
|
|
|
for (const prop in vars) {
|
|
document.body.style.removeProperty(prop);
|
|
}
|
|
},
|
|
setBodyClass() {
|
|
const body = document.getElementsByTagName('body')[0];
|
|
const cssClass = `overflow-hidden dashboard-body`;
|
|
let bodyClass = `theme-${ this.theme } ${ cssClass }`;
|
|
|
|
if ( this.brand ) {
|
|
try {
|
|
const brandMeta = require(`~shell/assets/brand/${ this.brand }/metadata.json`);
|
|
|
|
if (brandMeta?.hasStylesheet === 'true') {
|
|
bodyClass = `${ cssClass } ${ this.brand } theme-${ this.theme }`;
|
|
} else {
|
|
bodyClass = `theme-${ this.theme } overflow-hidden dashboard-body`;
|
|
this.$store.dispatch('prefs/setBrandStyle', this.theme === 'dark');
|
|
}
|
|
} catch {}
|
|
}
|
|
body.className = bodyClass;
|
|
}
|
|
}
|
|
|
|
};
|