Added whitelabeling support using assets/override (#156)

This commit is contained in:
Harrison Healey 2017-01-05 14:32:24 -05:00 committed by enahum
parent 81526fbb09
commit 9d51a3b970
25 changed files with 240 additions and 146 deletions

View file

@ -2,8 +2,10 @@
"presets": [ "react-native" ],
"plugins": [
["module-resolver", {
"root": [".", "config", "test"],
"alias": {}
"root": ["."],
"alias": {
"assets": "./dist/assets"
}
}]
]
}

5
.gitignore vendored
View file

@ -1,3 +1,6 @@
assets/override
dist
# OSX
#
.DS_Store
@ -49,5 +52,3 @@ Session.vim
.netrwhist
*~
tags
config/config.secret.json

View file

@ -12,15 +12,24 @@
touch $@
config/config.secret.json:
@if ! [ $(shell test -f config/config.secret.json) ]; then \
echo "Generating default config/config.secret.json"; \
echo '{}' > "config/config.secret.json"; \
BASE_ASSETS = $(shell find assets/base -type d) $(shell find assets/base -type f -name '*')
OVERRIDE_ASSETS = $(shell find assets/override -type d 2> /dev/null) $(shell find assets/override -type f -name '*' 2> /dev/null)
dist/assets: $(BASE_ASSETS) $(OVERRIDE_ASSETS)
mkdir -p dist
@if [ -e dist/assets ] ; then \
rm -rf dist/assets; \
fi
node scripts/make-dist-assets.js
.PHONY: prepare
pre-run: .npminstall dist/assets
run: run-ios
run-ios: .npminstall config/config.secret.json
run-ios: pre-run
@if ! [ $(shell command -v xcodebuild) ]; then \
echo "xcode is not installed"; \
exit 1; \
@ -35,8 +44,7 @@ run-ios: .npminstall config/config.secret.json
npm run run-ios
open -a Simulator
run-android: .npminstall config/config.secret.json
run-android: pre-run
@if ! [ $(ANDROID_HOME) ]; then \
echo "ANDROID_HOME is not set"; \
exit 1; \
@ -58,7 +66,7 @@ run-android: .npminstall config/config.secret.json
npm run run-android
test: .npminstall config/config.secret.json
test: pre-run
npm test
check-style: .npminstall
@ -72,6 +80,7 @@ clean:
npm cache clean
rm -rf node_modules
rm -f .npminstall
rm -rf dist
post-install:
./node_modules/.bin/remotedev-debugger --hostname localhost --port 5678 --injectserver

View file

@ -3,9 +3,11 @@
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import Config from 'config/index';
import Config from 'assets/config.json';
import {setAppState} from 'service/actions/general';
import RootLayout from './root_layout';
function mapStateToProps(state, ownProps) {

View file

@ -3,7 +3,8 @@
import {combineReducers} from 'redux';
import Config from 'config/index';
import Config from 'assets/config.json';
import {UsersTypes} from 'service/constants';
function locale(state = Config.DefaultLocale, action) {

View file

@ -3,7 +3,7 @@
import {combineReducers} from 'redux';
import Config from 'config/index';
import Config from 'assets/config.json';
import {ViewTypes} from 'app/constants';

View file

@ -2,6 +2,7 @@
// See License.txt for license information.
import React, {Component, PropTypes} from 'react';
import {injectIntl, intlShape} from 'react-intl';
import {Text, TextInput, Image, KeyboardAvoidingView} from 'react-native';
import Button from 'app/components/button';
@ -9,9 +10,8 @@ import FormattedText from 'app/components/formatted_text';
import ErrorText from 'app/components/error_text';
import Loading from 'app/components/loading';
import {GlobalStyles} from 'app/styles';
import logo from 'app/images/logo.png';
import {injectIntl, intlShape} from 'react-intl';
import logo from 'assets/images/logo.png';
import {RequestStatus} from 'service/constants';

View file

@ -2,17 +2,17 @@
// See License.txt for license information.
import React, {Component} from 'react';
import {injectIntl, intlShape} from 'react-intl';
import {TextInput, Image, KeyboardAvoidingView} from 'react-native';
import Client from 'service/client';
import Button from 'app/components/button';
import ErrorText from 'app/components/error_text';
import FormattedText from 'app/components/formatted_text';
import {GlobalStyles} from 'app/styles';
import logo from 'app/images/logo.png';
import {injectIntl, intlShape} from 'react-intl';
import logo from 'assets/images/logo.png';
import Client from 'service/client';
import RequestStatus from 'service/constants/request_status';
class SelectServer extends Component {

View file

@ -2,16 +2,17 @@
// See License.txt for license information.
import React, {Component, PropTypes} from 'react';
import {View, Image, Text} from 'react-native';
import Button from 'react-native-button';
import Loading from 'app/components/loading';
import Icon from 'react-native-vector-icons/MaterialIcons';
import ErrorText from 'app/components/error_text';
import {GlobalStyles} from 'app/styles';
import logo from 'app/images/logo.png';
import FormattedText from 'app/components/formatted_text';
import Loading from 'app/components/loading';
import {GlobalStyles} from 'app/styles';
import logo from 'assets/images/logo.png';
import {RequestStatus} from 'service/constants';
export default class SelectTeam extends Component {

View file

@ -1,6 +1,5 @@
{
"DefaultServerUrl": "http://localhost:8065",
"MockFetchInTests": true,
"DefaultLocale": "en",
"DefaultTheme": "default"
}

View file

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View file

Before

Width:  |  Height:  |  Size: 738 B

After

Width:  |  Height:  |  Size: 738 B

View file

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

94
assets/base/themes.json Normal file
View file

@ -0,0 +1,94 @@
{
"default": {
"sidebarBg": "#2071a7",
"sidebarText": "#fff",
"sidebarUnreadText": "#fff",
"sidebarTextHoverBg": "#136197",
"sidebarTextActiveBorder": "#7AB0D6",
"sidebarTextActiveColor": "#FFFFFF",
"sidebarHeaderBg": "#2f81b7",
"sidebarHeaderTextColor": "#FFFFFF",
"onlineIndicator": "#7DBE00",
"awayIndicator": "#DCBD4E",
"mentionBj": "#FBFBFB",
"mentionColor": "#2071A7",
"centerChannelBg": "#f2f4f8",
"centerChannelColor": "#333333",
"newMessageSeparator": "#FF8800",
"linkColor": "#2f81b7",
"buttonBg": "#1dacfc",
"buttonColor": "#FFFFFF",
"mentionHighlightBg": "#fff2bb",
"mentionHighlightLink": "#2f81b7",
"codeTheme": "github"
},
"mattermost": {
"sidebarBg": "#fafafa",
"sidebarText": "#333333",
"sidebarUnreadText": "#333333",
"sidebarTextHoverBg": "#e6f2fa",
"sidebarTextActiveBorder": "#378FD2",
"sidebarTextActiveColor": "#111111",
"sidebarHeaderBg": "#3481B9",
"sidebarHeaderTextColor": "#ffffff",
"onlineIndicator": "#7DBE00",
"awayIndicator": "#DCBD4E",
"mentionBj": "#2389d7",
"mentionColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#333333",
"newMessageSeparator": "#FF8800",
"linkColor": "#2389d7",
"buttonBg": "#23A2FF",
"buttonColor": "#FFFFFF",
"mentionHighlightBg": "#fff2bb",
"mentionHighlightLink": "#2f81b7",
"codeTheme": "github"
},
"mattermostDark": {
"sidebarBg": "#1B2C3E",
"sidebarText": "#fff",
"sidebarUnreadText": "#fff",
"sidebarTextHoverBg": "#4A5664",
"sidebarTextActiveBorder": "#66B9A7",
"sidebarTextActiveColor": "#FFFFFF",
"sidebarHeaderBg": "#1B2C3E",
"sidebarHeaderTextColor": "#FFFFFF",
"onlineIndicator": "#55C5B2",
"awayIndicator": "#A9A14C",
"mentionBj": "#B74A4A",
"mentionColor": "#FFFFFF",
"centerChannelBg": "#2F3E4E",
"centerChannelColor": "#DDDDDD",
"newMessageSeparator": "#5de5da",
"linkColor": "#A4FFEB",
"buttonBg": "#4CBBA4",
"buttonColor": "#FFFFFF",
"mentionHighlightBg": "#984063",
"mentionHighlightLink": "#A4FFEB",
"codeTheme": "solarized-dark"
},
"windows10": {
"sidebarBg": "#171717",
"sidebarText": "#fff",
"sidebarUnreadText": "#fff",
"sidebarTextHoverBg": "#302e30",
"sidebarTextActiveBorder": "#196CAF",
"sidebarTextActiveColor": "#FFFFFF",
"sidebarHeaderBg": "#1f1f1f",
"sidebarHeaderTextColor": "#FFFFFF",
"onlineIndicator": "#0177e7",
"awayIndicator": "#A9A14C",
"mentionBj": "#0177e7",
"mentionColor": "#FFFFFF",
"centerChannelBg": "#1F1F1F",
"centerChannelColor": "#DDDDDD",
"newMessageSeparator": "#CC992D",
"linkColor": "#0D93FF",
"buttonBg": "#0177e7",
"buttonColor": "#FFFFFF",
"mentionHighlightBg": "#784098",
"mentionHighlightLink": "#A4FFEB",
"codeTheme": "monokai"
}
}

View file

@ -1,12 +0,0 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import BaseConfig from './config.json';
import SecretConfig from './config.secret.json';
const Config = {
...BaseConfig,
...SecretConfig
};
export default Config;

View file

@ -0,0 +1,89 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
/* eslint-disable no-console */
var fs = require('fs');
// Takes the files in rootA/path, overwrites or merges them with the corresponding fiel in rootB/path, and places the
// resulting file in dest/path. JSON files that exist in both places are shallowly (TODO maybe deeply) merged and all
// other types of files are overwritten.
function leftMergeDirs(rootA, rootB, dest, path) {
var pathA = rootA + path;
var pathB = rootB + path;
try {
fs.mkdirSync(dest + path);
} catch (e) {
if (e.code !== 'EEXIST') {
console.error('Failed to create destination dir ' + dest + path);
console.log(e);
throw e;
}
}
for (var file of fs.readdirSync(pathA)) {
var filePathA = pathA + file;
var filePathB = pathB + file;
var stat;
try {
stat = fs.statSync(filePathA);
} catch (e) {
console.error('File ' + file + ' doesn\'t exist in ' + pathA);
throw e;
}
if (stat.isDirectory()) {
leftMergeDirs(rootA, rootB, dest, path + file + '/');
} else {
var fileA;
try {
fileA = fs.readFileSync(filePathA);
} catch (e) {
console.error('Failed to read ' + filePathA);
throw e;
}
var outPath = dest + path + file;
var out;
try {
out = fs.createWriteStream(outPath);
} catch (e) {
console.error('Failed to open output file ' + dest + path + file);
throw e;
}
var fileB = null;
try {
fileB = fs.readFileSync(filePathB);
} catch (e) {
// do nothing
}
if (fileB) {
if (file.endsWith('.json')) {
// We need to merge these files
var objA = JSON.parse(fileA);
var objB = JSON.parse(fileB);
console.log('Merging ' + filePathA + ' with ' + filePathB + ' into ' + outPath);
out.write(JSON.stringify(Object.assign({}, objA, objB)));
} else {
// Copy fileB instead of fileA
console.log('Copying ' + filePathB + ' from override to ' + outPath);
out.write(fileB);
}
} else {
// Copy fileA to the destination
console.log('Copying ' + filePathA + ' from base to ' + outPath);
out.write(fileA);
}
}
}
}
// Assumes dist/assets exists and is empty
leftMergeDirs('assets/base/', 'assets/override/', 'dist/assets/', '');
/* eslint-enable no-console */

View file

@ -9,7 +9,6 @@ import TeamsTypes from './teams';
import PostsTypes from './posts';
import PreferencesTypes from './preferences';
import RequestStatus from './request_status';
import Themes from './themes';
import WebsocketEvents from './websocket';
const Preferences = {
@ -27,6 +26,5 @@ export {
PreferencesTypes,
Preferences,
RequestStatus,
Themes,
WebsocketEvents
};

View file

@ -1,97 +0,0 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
export default {
default: {
sidebarBg: '#2071a7',
sidebarText: '#fff',
sidebarUnreadText: '#fff',
sidebarTextHoverBg: '#136197',
sidebarTextActiveBorder: '#7AB0D6',
sidebarTextActiveColor: '#FFFFFF',
sidebarHeaderBg: '#2f81b7',
sidebarHeaderTextColor: '#FFFFFF',
onlineIndicator: '#7DBE00',
awayIndicator: '#DCBD4E',
mentionBj: '#FBFBFB',
mentionColor: '#2071A7',
centerChannelBg: '#f2f4f8',
centerChannelColor: '#333333',
newMessageSeparator: '#FF8800',
linkColor: '#2f81b7',
buttonBg: '#1dacfc',
buttonColor: '#FFFFFF',
mentionHighlightBg: '#fff2bb',
mentionHighlightLink: '#2f81b7',
codeTheme: 'github'
},
mattermost: {
sidebarBg: '#fafafa',
sidebarText: '#333333',
sidebarUnreadText: '#333333',
sidebarTextHoverBg: '#e6f2fa',
sidebarTextActiveBorder: '#378FD2',
sidebarTextActiveColor: '#111111',
sidebarHeaderBg: '#3481B9',
sidebarHeaderTextColor: '#ffffff',
onlineIndicator: '#7DBE00',
awayIndicator: '#DCBD4E',
mentionBj: '#2389d7',
mentionColor: '#ffffff',
centerChannelBg: '#ffffff',
centerChannelColor: '#333333',
newMessageSeparator: '#FF8800',
linkColor: '#2389d7',
buttonBg: '#23A2FF',
buttonColor: '#FFFFFF',
mentionHighlightBg: '#fff2bb',
mentionHighlightLink: '#2f81b7',
codeTheme: 'github'
},
mattermostDark: {
sidebarBg: '#1B2C3E',
sidebarText: '#fff',
sidebarUnreadText: '#fff',
sidebarTextHoverBg: '#4A5664',
sidebarTextActiveBorder: '#66B9A7',
sidebarTextActiveColor: '#FFFFFF',
sidebarHeaderBg: '#1B2C3E',
sidebarHeaderTextColor: '#FFFFFF',
onlineIndicator: '#55C5B2',
awayIndicator: '#A9A14C',
mentionBj: '#B74A4A',
mentionColor: '#FFFFFF',
centerChannelBg: '#2F3E4E',
centerChannelColor: '#DDDDDD',
newMessageSeparator: '#5de5da',
linkColor: '#A4FFEB',
buttonBg: '#4CBBA4',
buttonColor: '#FFFFFF',
mentionHighlightBg: '#984063',
mentionHighlightLink: '#A4FFEB',
codeTheme: 'solarized-dark'
},
windows10: {
sidebarBg: '#171717',
sidebarText: '#fff',
sidebarUnreadText: '#fff',
sidebarTextHoverBg: '#302e30',
sidebarTextActiveBorder: '#196CAF',
sidebarTextActiveColor: '#FFFFFF',
sidebarHeaderBg: '#1f1f1f',
sidebarHeaderTextColor: '#FFFFFF',
onlineIndicator: '#0177e7',
awayIndicator: '#A9A14C',
mentionBj: '#0177e7',
mentionColor: '#FFFFFF',
centerChannelBg: '#1F1F1F',
centerChannelColor: '#DDDDDD',
newMessageSeparator: '#CC992D',
linkColor: '#0D93FF',
buttonBg: '#0177e7',
buttonColor: '#FFFFFF',
mentionHighlightBg: '#784098',
mentionHighlightLink: '#A4FFEB',
codeTheme: 'monokai'
}
};

View file

@ -3,8 +3,8 @@
import 'intl';
import en from './en.json';
import es from './es.json';
import en from 'assets/i18n/en.json';
import es from 'assets/i18n/es.json';
const DEFAULT_LOCALE = 'en';

View file

@ -1,9 +1,10 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import Config from 'config';
import Config from 'assets/config.json';
import {Themes} from 'assets/themes.json';
import {Preferences, Themes} from 'service/constants';
import {Preferences} from 'service/constants';
export function getTheme(state) {
const myPreferences = state.entities.preferences.myPreferences;

View file

@ -3,11 +3,14 @@
import assert from 'assert';
import * as Actions from 'service/actions/general';
import Config from 'config';
import Client from 'service/client';
import configureStore from 'app/store';
import Config from 'assets/config.json';
import * as Actions from 'service/actions/general';
import Client from 'service/client';
import {RequestStatus} from 'service/constants';
import TestHelper from 'test/test_helper';
describe('Actions.General', () => {

View file

@ -3,9 +3,10 @@
import assert from 'assert';
import Config from 'config';
import Config from 'assets/config.json';
import Themes from 'assets/themes.json';
import {Preferences, Themes} from 'service/constants';
import {Preferences} from 'service/constants';
import {getTheme} from 'service/selectors/entities/preferences';
describe('Selectors.Preferences', () => {

View file

@ -2,8 +2,10 @@
// See License.txt for license information.
import assert from 'assert';
import Config from 'assets/config.json';
import Client from 'service/client/client';
import Config from 'config';
const PASSWORD = 'password1';