[RN] Add color scheme support - JS
This commit is contained in:
parent
eec7a1b628
commit
2941f5dde4
|
@ -5,6 +5,7 @@ import { Linking } from 'react-native';
|
|||
|
||||
import '../../analytics';
|
||||
import '../../authentication';
|
||||
import { setColorScheme } from '../../base/color-scheme';
|
||||
import { DialogContainer } from '../../base/dialog';
|
||||
import '../../base/jwt';
|
||||
import { Platform } from '../../base/react';
|
||||
|
@ -35,6 +36,11 @@ const logger = require('jitsi-meet-logger').getLogger(__filename);
|
|||
*/
|
||||
type Props = AbstractAppProps & {
|
||||
|
||||
/**
|
||||
* An object of colors that override the default colors of the app/sdk.
|
||||
*/
|
||||
colorScheme: Object,
|
||||
|
||||
/**
|
||||
* Whether Picture-in-Picture is enabled. If {@code true}, a toolbar button
|
||||
* is rendered in the {@link Conference} view to afford entering
|
||||
|
@ -56,6 +62,8 @@ type Props = AbstractAppProps & {
|
|||
* @extends AbstractApp
|
||||
*/
|
||||
export class App extends AbstractApp {
|
||||
_init: Promise<*>;
|
||||
|
||||
/**
|
||||
* Initializes a new {@code App} instance.
|
||||
*
|
||||
|
@ -86,6 +94,12 @@ export class App extends AbstractApp {
|
|||
componentDidMount() {
|
||||
super.componentDidMount();
|
||||
|
||||
this._init.then(() => {
|
||||
// We set the color scheme early enough so then we avoid any
|
||||
// unnecessary re-renders.
|
||||
this.state.store.dispatch(setColorScheme(this.props.colorScheme));
|
||||
});
|
||||
|
||||
Linking.addEventListener('url', this._onLinkingURL);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
// @flow
|
||||
|
||||
import { toState } from '../redux';
|
||||
import { StyleType } from '../styles';
|
||||
|
||||
import defaultScheme from './defaultScheme';
|
||||
|
||||
/**
|
||||
* A registry class to register styles that need to be color-schemed.
|
||||
*
|
||||
* This class uses lazy initialization for scheme-ified style definitions on
|
||||
* request.
|
||||
*/
|
||||
class ColorSchemeRegistry {
|
||||
/**
|
||||
* A map of already scheme-ified style definitions.
|
||||
*/
|
||||
_schemedStyles = new Map();
|
||||
|
||||
/**
|
||||
* A map of registered style templates.
|
||||
*/
|
||||
_styleTemplates = new Map();
|
||||
|
||||
/**
|
||||
* Clears the already scheme-ified style definitions. This is useful when
|
||||
* the {@code SET_COLOR_SCHEME} action is dispatched (again).
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
clear() {
|
||||
this._schemedStyles.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retreives the color-scheme applied style definition of a component.
|
||||
*
|
||||
* @param {Object | Function} stateful - An object or function that can be
|
||||
* resolved to Redux state using the {@code toState} function.
|
||||
* @param {string} componentName - The name of the component whose style we
|
||||
* want to retreive.
|
||||
* @returns {StyleType}
|
||||
*/
|
||||
get(stateful: Object | Function, componentName: string): StyleType {
|
||||
let schemedStyle = this._schemedStyles.get(componentName);
|
||||
|
||||
if (!schemedStyle) {
|
||||
schemedStyle
|
||||
= this._applyColorScheme(
|
||||
stateful,
|
||||
componentName,
|
||||
this._styleTemplates.get(componentName));
|
||||
this._schemedStyles.set(componentName, schemedStyle);
|
||||
}
|
||||
|
||||
return schemedStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a style definition to the registry for color-scheming.
|
||||
*
|
||||
* NOTE: It's suggested to only use this registry on styles where color
|
||||
* scheming is needed, otherwise just use a static style object as before.
|
||||
*
|
||||
* @param {string} componentName - The name of the component to register the
|
||||
* style to (e.g. {@code 'Toolbox'}).
|
||||
* @param {StyleType} style - The style definition to register.
|
||||
* @returns {void}
|
||||
*/
|
||||
register(componentName: string, style: StyleType): void {
|
||||
this._styleTemplates.set(componentName, style);
|
||||
|
||||
// If this is a style overwrite, we need to delete the processed version
|
||||
// of the style from the other map
|
||||
this._schemedStyles.delete(componentName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a color schemed style object applying the color scheme to every
|
||||
* colors in the style object prepared in a special way.
|
||||
*
|
||||
* @param {Object | Function} stateful - An object or function that can be
|
||||
* resolved to Redux state using the {@code toState} function.
|
||||
* @param {string} componentName - The name of the component to apply the
|
||||
* color scheme to.
|
||||
* @param {StyleType} style - The style definition to apply the color scheme
|
||||
* to.
|
||||
* @returns {StyleType}
|
||||
*/
|
||||
_applyColorScheme(
|
||||
stateful: Object | Function,
|
||||
componentName: string,
|
||||
style: StyleType): StyleType {
|
||||
let schemedStyle;
|
||||
|
||||
if (Array.isArray(style)) {
|
||||
// The style is an array of styles, we apply the same transformation
|
||||
// to each, recursively.
|
||||
schemedStyle = [];
|
||||
|
||||
for (const entry of style) {
|
||||
schemedStyle.push(this._applyColorScheme(
|
||||
stateful, componentName, entry));
|
||||
}
|
||||
} else {
|
||||
// The style is an object, we create a copy of it to avoid in-place
|
||||
// modification.
|
||||
schemedStyle = {
|
||||
...style
|
||||
};
|
||||
|
||||
for (const [
|
||||
styleName,
|
||||
styleValue
|
||||
] of Object.entries(schemedStyle)) {
|
||||
if (typeof styleValue === 'object') {
|
||||
// The value is another style object, we apply the same
|
||||
// transformation recusively.
|
||||
schemedStyle[styleName]
|
||||
= this._applyColorScheme(
|
||||
stateful, componentName, styleValue);
|
||||
} else if (typeof styleValue === 'function') {
|
||||
// The value is a function, which indicates that it's a
|
||||
// dynamic, schemed color we need to resolve.
|
||||
schemedStyle[styleName]
|
||||
= this._getColor(stateful, componentName, styleValue());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return schemedStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to get the color value for the provided identifier.
|
||||
*
|
||||
* @param {Object | Function} stateful - An object or function that can be
|
||||
* resolved to Redux state using the {@code toState} function.
|
||||
* @param {string} componentName - The name of the component to get the
|
||||
* color value for.
|
||||
* @param {string} colorDefinition - The string identifier of the color,
|
||||
* e.g. {@code appBackground}.
|
||||
* @returns {string}
|
||||
*/
|
||||
_getColor(
|
||||
stateful: Object | Function,
|
||||
componentName: string,
|
||||
colorDefinition: string): string {
|
||||
const colorScheme = toState(stateful)['features/base/color-scheme'];
|
||||
|
||||
return {
|
||||
...defaultScheme[componentName],
|
||||
...colorScheme[componentName]
|
||||
}[colorDefinition];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default new ColorSchemeRegistry();
|
|
@ -0,0 +1,11 @@
|
|||
// @flow
|
||||
|
||||
/**
|
||||
* Redux action to signal a color scheme change in the app/sdk.
|
||||
*
|
||||
* {
|
||||
* type: SET_COLOR_SCHEME
|
||||
* colorScheme: Object
|
||||
* }
|
||||
*/
|
||||
export const SET_COLOR_SCHEME = Symbol('SET_COLOR_SCHEME');
|
|
@ -0,0 +1,19 @@
|
|||
// @flow
|
||||
|
||||
import { SET_COLOR_SCHEME } from './actionTypes';
|
||||
|
||||
/**
|
||||
* Dispatches a Redux action to set the color scheme of the app/sdk.
|
||||
*
|
||||
* @param {Object} colorScheme - The color scheme to set.
|
||||
* @returns {{
|
||||
* type: SET_COLOR_SCHEME,
|
||||
* colorScheme: Object
|
||||
* }}
|
||||
*/
|
||||
export function setColorScheme(colorScheme: Object): Object {
|
||||
return {
|
||||
type: SET_COLOR_SCHEME,
|
||||
colorScheme
|
||||
};
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
// @flow
|
||||
|
||||
/**
|
||||
* The default color scheme of the application.
|
||||
*/
|
||||
export default {};
|
|
@ -0,0 +1,13 @@
|
|||
// @flow
|
||||
|
||||
/**
|
||||
* A special function to be used in the {@code createColorSchemedStyle} call,
|
||||
* that denotes that the color is a dynamic color.
|
||||
*
|
||||
* @param {string} colorDefinition - The definition of the color to mark to be
|
||||
* resolved.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function schemeColor(colorDefinition: string): Function {
|
||||
return () => colorDefinition;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// @flow
|
||||
|
||||
export * from './actions';
|
||||
export * from './actionTypes';
|
||||
export * from './functions';
|
||||
export { default as ColorSchemeRegistry } from './ColorSchemeRegistry';
|
||||
|
||||
import './reducer';
|
|
@ -0,0 +1,20 @@
|
|||
// @flow
|
||||
|
||||
import { MiddlewareRegistry } from '../redux';
|
||||
|
||||
import { SET_COLOR_SCHEME } from './actionTypes';
|
||||
import ColorSchemeRegistry from './ColorSchemeRegistry';
|
||||
|
||||
/**
|
||||
* The middleware of the feature {@code base/color-scheme}.
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
MiddlewareRegistry.register((/* store */) => next => action => {
|
||||
switch (action.type) {
|
||||
case SET_COLOR_SCHEME:
|
||||
return ColorSchemeRegistry.clear();
|
||||
}
|
||||
|
||||
return next(action);
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
// @flow
|
||||
|
||||
import _ from 'lodash';
|
||||
|
||||
import { ReducerRegistry } from '../redux';
|
||||
|
||||
import { SET_COLOR_SCHEME } from './actionTypes';
|
||||
|
||||
/**
|
||||
* The reducer of the feature {@code base/color-scheme}.
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
ReducerRegistry.register('features/base/color-scheme', (state = {}, action) => {
|
||||
switch (action.type) {
|
||||
case SET_COLOR_SCHEME:
|
||||
return _.cloneDeep(action.colorScheme);
|
||||
}
|
||||
|
||||
return state;
|
||||
});
|
|
@ -27,7 +27,7 @@ export const ColorPalette = {
|
|||
overflowMenuItemUnderlay: '#EEEEEE',
|
||||
red: '#D00000',
|
||||
transparent: 'rgba(0, 0, 0, 0)',
|
||||
white: 'white',
|
||||
white: '#FFFFFF',
|
||||
|
||||
/**
|
||||
* These are colors from the atlaskit to be used on mobile, when needed.
|
||||
|
|
|
@ -6,6 +6,23 @@ import { ColorPalette } from './components';
|
|||
declare type StyleSheet = Object;
|
||||
export type StyleType = StyleSheet | Array<StyleSheet>;
|
||||
|
||||
/**
|
||||
* RegExp pattern for long HEX color format.
|
||||
*/
|
||||
const HEX_LONG_COLOR_FORMAT
|
||||
= /^#([0-9A-F]{2,2})([0-9A-F]{2,2})([0-9A-F]{2,2})$/i;
|
||||
|
||||
/**
|
||||
* RegExp pattern for short HEX color format.
|
||||
*/
|
||||
const HEX_SHORT_COLOR_FORMAT
|
||||
= /^#([0-9A-F]{1,1})([0-9A-F]{1,1})([0-9A-F]{1,1})$/i;
|
||||
|
||||
/**
|
||||
* RegExp pattern for RGB color format.
|
||||
*/
|
||||
const RGB_COLOR_FORMAT = /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/i;
|
||||
|
||||
/**
|
||||
* The list of the well-known style properties which may not be numbers on Web
|
||||
* but must be numbers on React Native.
|
||||
|
@ -87,6 +104,49 @@ export function fixAndroidViewClipping<T: StyleSheet>(styles: T): T {
|
|||
return styles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an rgba format of the provided color if it's in hex or rgb format.
|
||||
*
|
||||
* NOTE: The function will return the same color if it's not in one of those
|
||||
* two formats (e.g. 'white').
|
||||
*
|
||||
* @param {string} color - The string representation of the color in rgb or hex
|
||||
* format.
|
||||
* @param {number} alpha - The alpha value to apply.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getRGBAFormat(color: string, alpha: number): string {
|
||||
let match = color.match(HEX_LONG_COLOR_FORMAT);
|
||||
|
||||
if (match) {
|
||||
return `#${match[1]}${match[2]}${match[3]}${_getAlphaInHex(alpha)}`;
|
||||
}
|
||||
|
||||
match = color.match(HEX_SHORT_COLOR_FORMAT);
|
||||
if (match) {
|
||||
return `#${match[1]}${match[1]}${match[2]}${match[2]}${match[3]}${
|
||||
match[3]}${_getAlphaInHex(alpha)}`;
|
||||
}
|
||||
|
||||
match = color.match(RGB_COLOR_FORMAT);
|
||||
if (match) {
|
||||
return `rgba(${match[1]}, ${match[2]}, ${match[3]}, ${alpha})`;
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an [0..1] alpha value into HEX.
|
||||
*
|
||||
* @param {number} alpha - The alpha value to convert.
|
||||
* @returns {string}
|
||||
*/
|
||||
function _getAlphaInHex(alpha: number): string {
|
||||
return Number(Math.round(255 * alpha)).toString(16)
|
||||
.padStart(2, '0');
|
||||
}
|
||||
|
||||
/**
|
||||
* Shims style properties to work correctly on native. Allows us to minimize the
|
||||
* number of style declarations that need to be set or overridden for specific
|
||||
|
|
Loading…
Reference in New Issue