Allow features to be disabled at build time (#143)

* Add framework for built-time feature selection
* Allow MQTT, NTP, OTA features to be disabled at build time
* Allow Project screens to be disabled at build time
* Allow security features to be disabled at build time
* Switch to std::function for StatefulService function aliases for greater flexibility
* Bump various UI lib versions
* Update docs
This commit is contained in:
rjwats
2020-06-09 21:57:44 +01:00
committed by GitHub
parent 88748ac30d
commit 449d3c91ce
36 changed files with 800 additions and 193 deletions

View File

@ -0,0 +1,23 @@
import React from 'react';
import { Features } from './types';
export interface ApplicationContext {
features: Features;
}
const ApplicationContextDefaultValue = {} as ApplicationContext
export const ApplicationContext = React.createContext(
ApplicationContextDefaultValue
);
export function withAuthenticatedContexApplicationContext<T extends ApplicationContext>(Component: React.ComponentType<T>) {
return class extends React.Component<Omit<T, keyof ApplicationContext>> {
render() {
return (
<ApplicationContext.Consumer>
{authenticatedContext => <Component {...this.props as T} features={authenticatedContext} />}
</ApplicationContext.Consumer>
);
}
};
}

View File

@ -0,0 +1,27 @@
import React from 'react';
import { Features } from './types';
export interface FeaturesContext {
features: Features;
}
const FeaturesContextDefaultValue = {} as FeaturesContext
export const FeaturesContext = React.createContext(
FeaturesContextDefaultValue
);
export interface WithFeaturesProps {
features: Features;
}
export function withFeatures<T extends WithFeaturesProps>(Component: React.ComponentType<T>) {
return class extends React.Component<Omit<T, keyof WithFeaturesProps>> {
render() {
return (
<FeaturesContext.Consumer>
{featuresContext => <Component {...this.props as T} features={featuresContext.features} />}
</FeaturesContext.Consumer>
);
}
};
}

View File

@ -0,0 +1,61 @@
import React, { Component } from 'react';
import { Features } from './types';
import { FeaturesContext } from './FeaturesContext';
import FullScreenLoading from '../components/FullScreenLoading';
import ApplicationError from '../components/ApplicationError';
import { FEATURES_ENDPOINT } from '../api';
interface FeaturesWrapperState {
features?: Features;
error?: string;
};
class FeaturesWrapper extends Component<{}, FeaturesWrapperState> {
state: FeaturesWrapperState = {};
componentDidMount() {
this.fetchFeaturesDetails();
}
fetchFeaturesDetails = () => {
fetch(FEATURES_ENDPOINT)
.then(response => {
if (response.status === 200) {
return response.json();
} else {
throw Error("Unexpected status code: " + response.status);
}
}).then(features => {
this.setState({ features });
})
.catch(error => {
this.setState({ error: error.message });
});
}
render() {
const { features, error } = this.state;
if (features) {
return (
<FeaturesContext.Provider value={{
features
}}>
{this.props.children}
</FeaturesContext.Provider>
);
}
if (error) {
return (
<ApplicationError error={error} />
);
}
return (
<FullScreenLoading />
);
}
}
export default FeaturesWrapper;

View File

@ -0,0 +1,7 @@
export interface Features {
project: boolean;
security: boolean;
mqtt: boolean;
ntp: boolean;
ota: boolean;
}