mirror of https://github.com/vizality/vizality
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
351 lines
9.7 KiB
351 lines
9.7 KiB
/**
|
|
* The settings API is meant .
|
|
* @module Settings
|
|
* @memberof API
|
|
* @namespace API.Settings
|
|
* @version 1.0.0
|
|
*/
|
|
|
|
/**
|
|
* @typedef SettingsCategory
|
|
* @property {Function} connectStore Connects a component to the settings store
|
|
* @property {function(string, string): string} get Gets a setting, or fallbacks to default value
|
|
* @property {function(): string[]} getKeys Get all settings key
|
|
* @property {function(string): void} delete Deletes a setting
|
|
* @property {function(string, string): void} set Sets a setting
|
|
*/
|
|
|
|
/**
|
|
* @typedef SettingsTab
|
|
* @property {string} category Settings category. Most of the time, you want this to be the entity ID
|
|
* @property {string|function(): string} label Settings tab label
|
|
* @property {function(): React.ReactNode} render Render method
|
|
* @property {undefined} settings Use it and you'll be fined 69 cookies
|
|
*/
|
|
|
|
import { Sidebar, Content, Layout } from '@vizality/components/dashboard';
|
|
import { toPlural } from '@vizality/util/string';
|
|
import { getCaller } from '@vizality/util/file';
|
|
import { Events } from '@vizality/constants';
|
|
import { Flux } from '@vizality/webpack';
|
|
import { API } from '@vizality/entities';
|
|
import React, { useState } from 'react';
|
|
|
|
import actions from './store/Actions';
|
|
import store from './store/Store';
|
|
|
|
/**
|
|
* @extends API
|
|
* @extends Events
|
|
*/
|
|
export default class Settings extends API {
|
|
constructor () {
|
|
super();
|
|
/**
|
|
* @property {Flux.Store} store Flux store
|
|
*/
|
|
this.store = store;
|
|
}
|
|
|
|
/**
|
|
* Shuts down the API, removing all listeners and stored objects.
|
|
*/
|
|
stop () {
|
|
this.removeAllListeners();
|
|
delete vizality.api.settings;
|
|
}
|
|
|
|
/**
|
|
* Builds a settings category that can be used by a plugin.
|
|
* @private
|
|
* @param {string} category Settings category name
|
|
* @returns {SettingsCategory}
|
|
*/
|
|
_buildCategoryObject (category) {
|
|
return {
|
|
connectStore: component => this.connectStores(category)(component),
|
|
getKeys: () => store.getSettingsKeys(category),
|
|
get: (setting, defaultValue) => store.getSetting(category, setting, defaultValue),
|
|
toggle: (setting, defaultValue) => {
|
|
return actions.toggleSetting(category, setting, defaultValue);
|
|
},
|
|
set: (setting, newValue) => {
|
|
actions.updateSetting(category, setting, newValue);
|
|
},
|
|
delete: (setting) => {
|
|
actions.deleteSetting(category, setting);
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Creates a flux decorator for a given settings category.
|
|
* @param {string} category Settings category
|
|
* @returns {Function}
|
|
*/
|
|
connectStores (category) {
|
|
return Flux.connectStores([ this.store ], () => this._fluxProps(category));
|
|
}
|
|
|
|
/**
|
|
* Registers a settings tab.
|
|
* @private
|
|
* @param {SettingsTab} props Props of the settings tab
|
|
*/
|
|
registerSettings (props) {
|
|
try {
|
|
let { type, addonId, render } = props;
|
|
|
|
type = type || 'plugin';
|
|
|
|
render =
|
|
render?.__esModule
|
|
? render?.default
|
|
: render?.type
|
|
? render.type
|
|
: render;
|
|
|
|
if (!render) {
|
|
throw new Error(`You must specify a render component to register settings for "${addonId}"!`);
|
|
}
|
|
|
|
const addon = vizality.manager[toPlural(type)].get(addonId);
|
|
|
|
if (!addon) {
|
|
throw new Error(`Cannot register settings for "${addonId}" because it isn't installed!`);
|
|
}
|
|
|
|
addon.sections.settings = {
|
|
component: render,
|
|
render: this.connectStores(addonId)(render)
|
|
};
|
|
|
|
const Render = addon.sections.settings.render;
|
|
|
|
vizality.api.routes.registerRoute({
|
|
id: `${type}/${addonId}`,
|
|
path: `/${toPlural(type)}/${addonId}`,
|
|
render: props =>
|
|
<Layout>
|
|
<Content
|
|
header='Settings'
|
|
vz-plugin={Boolean(type === 'plugin') && addonId}
|
|
vz-theme={Boolean(type === 'theme') && addonId}
|
|
vz-plugin-section={Boolean(type === 'plugin') && 'settings'}
|
|
vz-theme-section={Boolean(type === 'theme') && 'settings'}
|
|
>
|
|
<Render {...props} />
|
|
</Content>
|
|
</Layout>,
|
|
sidebar: Sidebar
|
|
});
|
|
|
|
this.emit(Events.VIZALITY_SETTINGS_REGISTER, addonId);
|
|
} catch (err) {
|
|
return this.error(err);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Registers a settings tab.
|
|
* @param {SettingsTab} settings Props of the settings tab
|
|
* @private
|
|
*/
|
|
// registerSettings (settings) {
|
|
// try {
|
|
// let { type, addonId, render } = settings;
|
|
|
|
// type = type || 'plugins';
|
|
|
|
// render =
|
|
// render?.__esModule
|
|
// ? render?.default
|
|
// : render?.type
|
|
// ? render.type
|
|
// : render;
|
|
|
|
// if (!render) {
|
|
// throw new Error(`You must specify a render component to register settings for "${addonId}"!`);
|
|
// }
|
|
|
|
// const addon = vizality.manager[type].get(addonId);
|
|
|
|
// if (!addon) {
|
|
// throw new Error(`Cannot register settings for "${addonId}" because it isn't installed!`);
|
|
// }
|
|
|
|
// addon.sections.settings = {
|
|
// component: render,
|
|
// render: this.connectStores(addonId)(render)
|
|
// };
|
|
|
|
// this.emit(Events.VIZALITY_SETTINGS_REGISTER, addonId);
|
|
// } catch (err) {
|
|
// return this.error(err);
|
|
// }
|
|
// }
|
|
|
|
/**
|
|
* Unregisters a settings tab.
|
|
* @private
|
|
* @param {string} addonId Addon ID of the settings to unregister
|
|
* @param {string} type Type of the addon
|
|
*/
|
|
unregisterSettings (addonId, type) {
|
|
try {
|
|
const addon = vizality.manager[toPlural(type)].get(addonId);
|
|
if (addon?.sections?.settings) {
|
|
delete addon.sections.settings;
|
|
} else {
|
|
throw new Error(`Settings for "${addonId}" are not registered, so they cannot be unregistered!`);
|
|
}
|
|
vizality.api.routes.unregisterRoute(`/${toPlural(type)}/${addonId}`);
|
|
this.emit(Events.VIZALITY_SETTINGS_UNREGISTER, addonId);
|
|
} catch (err) {
|
|
return this.error(err);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* @param {...any} props
|
|
*/
|
|
_registerBuiltinSection (props) {
|
|
try {
|
|
const addonId = getCaller()?.id;
|
|
const { header, description, icon, render } = props;
|
|
const builtin = vizality.manager.builtins.get(addonId);
|
|
builtin.sections.settings = props;
|
|
builtin.sections.settings.render = this.connectStores(addonId)(render);
|
|
const Render = builtin.sections.settings.render;
|
|
vizality.api.routes.registerRoute({
|
|
id: addonId,
|
|
path: `/${addonId}`,
|
|
render: props =>
|
|
<Layout>
|
|
<Content
|
|
header={header}
|
|
description={description}
|
|
icon={icon}
|
|
vz-builtin={addonId}
|
|
>
|
|
<Render {...props} />
|
|
</Content>
|
|
</Layout>,
|
|
sidebar: Sidebar
|
|
});
|
|
} catch (err) {
|
|
return this.error(err);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A hook that allows you to easily update settings values using states, automatically
|
|
* rerendering your component for you.
|
|
* @param {string} settingKey Setting key
|
|
* @param {...any} defaultValue Default setting value
|
|
* @example
|
|
* ```
|
|
* import { useSetting } from '@vizality/settings';
|
|
*
|
|
* export default memo(() => {
|
|
* const [ text, setText ] = useSetting('coolText', 'I like pie');
|
|
* return (
|
|
* {text}
|
|
* <Button onClick={() => setText('I like cake')} />
|
|
* )
|
|
* )};
|
|
* ```
|
|
*/
|
|
useSetting (settingKey, defaultValue, addonId) {
|
|
try {
|
|
const settings = this._fluxProps(addonId);
|
|
|
|
/**
|
|
* If it doesn't find any settings, just return.
|
|
*/
|
|
if (!settings) {
|
|
return;
|
|
}
|
|
|
|
const [ settingValue, setSettingValue ] = useState(settings.getSetting(settingKey, defaultValue));
|
|
/**
|
|
* Updates the setting value.
|
|
* @param {...any} newValue Updated setting value
|
|
* @returns {void}
|
|
*/
|
|
const setSetting = newValue => {
|
|
settings.updateSetting(settingKey, newValue);
|
|
setSettingValue(newValue);
|
|
};
|
|
return [ settingValue, setSetting ];
|
|
} catch (err) {
|
|
console.log('this-out', this);
|
|
return this.error(err);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @private
|
|
* @param {string} [addonId] Addon ID
|
|
*/
|
|
_fluxProps (addonId) {
|
|
/**
|
|
* If no addonId is provided, try to use getCaller to determine one.
|
|
*/
|
|
addonId = addonId || getCaller()?.id;
|
|
/**
|
|
* If the addonId is vizality, it was most likely returned from getCaller
|
|
* and is used in some core area, so we want to use the core Vizality settings.json
|
|
* file for its store.
|
|
*/
|
|
if (addonId === 'vizality') {
|
|
addonId = 'settings';
|
|
}
|
|
return {
|
|
/**
|
|
*
|
|
*/
|
|
settings: store.getSettings(addonId),
|
|
|
|
/**
|
|
*
|
|
* @param {*} setting
|
|
* @param {*} defaultValue
|
|
*/
|
|
getSetting: (setting, defaultValue) => {
|
|
return store.getSetting(addonId, setting, defaultValue);
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {*} setting
|
|
* @param {*} value
|
|
*/
|
|
updateSetting: (setting, value) => {
|
|
if (addonId === 'settings') {
|
|
this.emit(Events.VIZALITY_SETTING_UPDATE, setting, value);
|
|
} else {
|
|
this.emit(Events.VIZALITY_ADDON_SETTING_UPDATE, addonId, setting, value);
|
|
}
|
|
return actions.updateSetting(addonId, setting, value);
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {*} setting
|
|
* @param {*} defaultValue
|
|
*/
|
|
toggleSetting: (setting, defaultValue) => {
|
|
if (addonId === 'settings') {
|
|
this.emit(Events.VIZALITY_SETTING_TOGGLE, setting, defaultValue);
|
|
} else {
|
|
this.emit(Events.VIZALITY_ADDON_SETTING_TOGGLE, addonId, setting, defaultValue);
|
|
}
|
|
return actions.toggleSetting(addonId, setting, defaultValue);
|
|
}
|
|
};
|
|
}
|
|
}
|