diff --git a/src/assets/images/galaxy2.jpg b/src/assets/images/galaxy2.jpg
new file mode 100644
index 000000000..bcc2ebe3b
Binary files /dev/null and b/src/assets/images/galaxy2.jpg differ
diff --git a/src/components/settings/settings/EditSettingsForm.js b/src/components/settings/settings/EditSettingsForm.js
index 97f535594..dc73158ea 100644
--- a/src/components/settings/settings/EditSettingsForm.js
+++ b/src/components/settings/settings/EditSettingsForm.js
@@ -1,15 +1,16 @@
import { remote } from 'electron';
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
import { observer } from 'mobx-react';
+import PropTypes from 'prop-types';
+import React, { Component } from 'react';
import { defineMessages, intlShape } from 'react-intl';
+import { FRANZ_TRANSLATION } from '../../../config';
+
import Form from '../../../lib/Form';
import Button from '../../ui/Button';
-import Toggle from '../../ui/Toggle';
+import Input from '../../ui/Input';
import Select from '../../ui/Select';
-
-import { FRANZ_TRANSLATION } from '../../../config';
+import Toggle from '../../ui/Toggle';
const messages = defineMessages({
headline: {
@@ -80,11 +81,28 @@ const messages = defineMessages({
id: 'settings.app.restartRequired',
defaultMessage: '!!!Changes require restart',
},
+ theme: {
+ id: 'settings.app.headlineAppTheme',
+ defaultMessage: '!!!Pick Franz theme',
+ },
+ appBackground: {
+ id: 'settings.app.headlineBackground',
+ defaultMessage: '!!!Add app background',
+ },
+ setBackground: {
+ id: 'settings.app.setBackground',
+ defaultMessage: '!!!Set background',
+ },
+ resetBackground: {
+ id: 'settings.app.resetBackground',
+ defaultMessage: '!!!Reset background',
+ },
});
@observer
export default class EditSettingsForm extends Component {
static propTypes = {
+ actions: PropTypes.any.isRequired,
checkForUpdates: PropTypes.func.isRequired,
installUpdate: PropTypes.func.isRequired,
form: PropTypes.instanceOf(Form).isRequired,
@@ -102,6 +120,23 @@ export default class EditSettingsForm extends Component {
intl: intlShape,
};
+ compareCurrentBg(newBg) {
+ if (!newBg) {
+ return false;
+ }
+ const current = window.getComputedStyle(document.body)
+ .backgroundImage
+ .replace('url("', '')
+ .replace('")', '');
+ if (!current || current === 'none') {
+ return false;
+ }
+ if (newBg.startsWith('./')) {
+ return current.includes(newBg.substring(1));
+ }
+ return newBg !== current;
+ }
+
submit(e) {
e.preventDefault();
this.props.form.submit({
@@ -115,6 +150,7 @@ export default class EditSettingsForm extends Component {
render() {
const {
+ actions,
checkForUpdates,
installUpdate,
form,
@@ -127,6 +163,7 @@ export default class EditSettingsForm extends Component {
cacheSize,
} = this.props;
const { intl } = this.context;
+ const { setBackground, resetBackground } = actions;
let updateButtonLabelMessage = messages.buttonSearchForUpdate;
if (isCheckingForUpdates) {
@@ -162,6 +199,30 @@ export default class EditSettingsForm extends Component {
+ {/* Theme */}
+ {intl.formatMessage(messages.theme)}
+
+
+ {/* Backgrounds */}
+ {intl.formatMessage(messages.appBackground)}
+
+
+
+
+
+
{/* Language */}
{intl.formatMessage(messages.headlineLanguage)}
diff --git a/src/components/ui/Input.js b/src/components/ui/Input.js
index 0bb9f23bf..129a31c1d 100644
--- a/src/components/ui/Input.js
+++ b/src/components/ui/Input.js
@@ -32,7 +32,7 @@ export default class Input extends Component {
state = {
showPassword: false,
passwordScore: 0,
- }
+ };
componentDidMount() {
if (this.props.focus) {
@@ -45,7 +45,7 @@ export default class Input extends Component {
field.onChange(e);
- if (scorePassword) {
+ if (field.type === 'password' && scorePassword) {
this.setState({ passwordScore: scorePasswordFunc(field.value) });
}
}
diff --git a/src/components/ui/SearchInput.js b/src/components/ui/SearchInput.js
index a94cde201..64dca81ed 100644
--- a/src/components/ui/SearchInput.js
+++ b/src/components/ui/SearchInput.js
@@ -1,9 +1,9 @@
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import { observer } from 'mobx-react';
import classnames from 'classnames';
-import uuidv1 from 'uuid/v1';
import { debounce } from 'lodash';
+import { observer } from 'mobx-react';
+import PropTypes from 'prop-types';
+import React, { Component } from 'react';
+import uuidv1 from 'uuid/v1';
@observer
export default class SearchInput extends Component {
@@ -17,6 +17,7 @@ export default class SearchInput extends Component {
throttle: PropTypes.bool,
throttleDelay: PropTypes.number,
autoFocus: PropTypes.bool,
+ showLabels: PropTypes.bool,
};
static defaultProps = {
@@ -29,7 +30,8 @@ export default class SearchInput extends Component {
onChange: () => null,
onReset: () => null,
autoFocus: false,
- }
+ showLabels: true,
+ };
constructor(props) {
super(props);
@@ -78,7 +80,7 @@ export default class SearchInput extends Component {
input = null;
render() {
- const { className, name, placeholder } = this.props;
+ const { className, name, showLabels, placeholder } = this.props;
const { value } = this.state;
return (
@@ -88,10 +90,12 @@ export default class SearchInput extends Component {
'search-input',
])}
>
-
+ {showLabels && (
+
+ )}
this.onChange(e)}
ref={(ref) => { this.input = ref; }}
/>
- {value.length > 0 && (
+ {showLabels && value.length > 0 && (
this.reset()}
diff --git a/src/config.js b/src/config.js
index 77fa92eca..90dc14d14 100644
--- a/src/config.js
+++ b/src/config.js
@@ -7,7 +7,12 @@ export const CHECK_INTERVAL = 1000 * 3600; // How often should we perform checks
export const LOCAL_API = 'http://localhost:3000';
export const DEV_API = 'https://dev.franzinfra.com';
export const LIVE_API = 'https://api.franzinfra.com';
-export const GA_ID = 'UA-74126766-6';
+
+export const APP_THEMES = {
+ 'theme-regular': 'Regular',
+ 'theme-dark': 'Dark',
+ 'theme-transparent-dark': 'Transparent dark',
+};
export const DEFAULT_APP_SETTINGS = {
autoLaunchInBackground: false,
@@ -16,6 +21,8 @@ export const DEFAULT_APP_SETTINGS = {
minimizeToSystemTray: false,
showDisabledServices: true,
showMessageBadgeWhenMuted: true,
+ theme: 'theme-regular',
+ appBackground: './assets/images/galaxy2.jpg',
enableSpellchecking: true,
locale: '',
fallbackLocale: 'en-US',
diff --git a/src/containers/auth/ImportScreen.js b/src/containers/auth/ImportScreen.js
index ddd56ffb6..5253e0174 100644
--- a/src/containers/auth/ImportScreen.js
+++ b/src/containers/auth/ImportScreen.js
@@ -3,14 +3,9 @@ import PropTypes from 'prop-types';
import { inject, observer } from 'mobx-react';
import Import from '../../components/auth/Import';
import UserStore from '../../stores/UserStore';
-import { gaPage } from '../../lib/analytics';
@inject('stores', 'actions') @observer
export default class ImportScreen extends Component {
- componentDidMount() {
- gaPage('Auth/Import');
- }
-
render() {
const { actions, stores } = this.props;
diff --git a/src/containers/auth/InviteScreen.js b/src/containers/auth/InviteScreen.js
index 059888c99..bd75301ee 100644
--- a/src/containers/auth/InviteScreen.js
+++ b/src/containers/auth/InviteScreen.js
@@ -2,13 +2,9 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { inject, observer } from 'mobx-react';
import Invite from '../../components/auth/Invite';
-import { gaPage } from '../../lib/analytics';
@inject('stores', 'actions') @observer
export default class InviteScreen extends Component {
- componentDidMount() {
- gaPage('Auth/Invite');
- }
render() {
const { actions } = this.props;
diff --git a/src/containers/auth/LoginScreen.js b/src/containers/auth/LoginScreen.js
index 9e22c5141..6a33790d8 100644
--- a/src/containers/auth/LoginScreen.js
+++ b/src/containers/auth/LoginScreen.js
@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
import { inject, observer } from 'mobx-react';
import Login from '../../components/auth/Login';
import UserStore from '../../stores/UserStore';
-import { gaPage } from '../../lib/analytics';
import { globalError as globalErrorPropType } from '../../prop-types';
@@ -13,10 +12,6 @@ export default class LoginScreen extends Component {
error: globalErrorPropType.isRequired,
};
- componentDidMount() {
- gaPage('Auth/Login');
- }
-
render() {
const { actions, stores, error } = this.props;
return (
diff --git a/src/containers/auth/PasswordScreen.js b/src/containers/auth/PasswordScreen.js
index d88cb08e6..ba5383b1c 100644
--- a/src/containers/auth/PasswordScreen.js
+++ b/src/containers/auth/PasswordScreen.js
@@ -3,14 +3,9 @@ import PropTypes from 'prop-types';
import { inject, observer } from 'mobx-react';
import Password from '../../components/auth/Password';
import UserStore from '../../stores/UserStore';
-import { gaPage } from '../../lib/analytics';
@inject('stores', 'actions') @observer
export default class PasswordScreen extends Component {
- componentDidMount() {
- gaPage('Auth/Password Retrieve');
- }
-
render() {
const { actions, stores } = this.props;
diff --git a/src/containers/auth/PricingScreen.js b/src/containers/auth/PricingScreen.js
index 7e1586535..19573b98a 100644
--- a/src/containers/auth/PricingScreen.js
+++ b/src/containers/auth/PricingScreen.js
@@ -6,7 +6,6 @@ import { RouterStore } from 'mobx-react-router';
import Pricing from '../../components/auth/Pricing';
import UserStore from '../../stores/UserStore';
import PaymentStore from '../../stores/PaymentStore';
-import { gaPage } from '../../lib/analytics';
import { globalError as globalErrorPropType } from '../../prop-types';
@@ -16,10 +15,6 @@ export default class PricingScreen extends Component {
error: globalErrorPropType.isRequired,
};
- componentDidMount() {
- gaPage('Auth/Pricing');
- }
-
render() {
const { actions, stores, error } = this.props;
diff --git a/src/containers/auth/SignupScreen.js b/src/containers/auth/SignupScreen.js
index 3b86ab138..0d77547b0 100644
--- a/src/containers/auth/SignupScreen.js
+++ b/src/containers/auth/SignupScreen.js
@@ -4,7 +4,6 @@ import { inject, observer } from 'mobx-react';
import Signup from '../../components/auth/Signup';
import UserStore from '../../stores/UserStore';
-import { gaPage } from '../../lib/analytics';
import { globalError as globalErrorPropType } from '../../prop-types';
@@ -14,10 +13,6 @@ export default class SignupScreen extends Component {
error: globalErrorPropType.isRequired,
};
- componentDidMount() {
- gaPage('Auth/Signup');
- }
-
render() {
const { actions, stores, error } = this.props;
return (
diff --git a/src/containers/auth/WelcomeScreen.js b/src/containers/auth/WelcomeScreen.js
index e413264a6..0bdf4e037 100644
--- a/src/containers/auth/WelcomeScreen.js
+++ b/src/containers/auth/WelcomeScreen.js
@@ -5,13 +5,9 @@ import { inject, observer } from 'mobx-react';
import Welcome from '../../components/auth/Welcome';
import UserStore from '../../stores/UserStore';
import RecipePreviewsStore from '../../stores/RecipePreviewsStore';
-import { gaPage } from '../../lib/analytics';
@inject('stores', 'actions') @observer
export default class LoginScreen extends Component {
- componentDidMount() {
- gaPage('Auth/Welcome');
- }
render() {
const { user, recipePreviews } = this.props.stores;
diff --git a/src/containers/settings/AccountScreen.js b/src/containers/settings/AccountScreen.js
index c5c2982b0..14ea86058 100644
--- a/src/containers/settings/AccountScreen.js
+++ b/src/containers/settings/AccountScreen.js
@@ -6,7 +6,6 @@ import { inject, observer } from 'mobx-react';
import PaymentStore from '../../stores/PaymentStore';
import UserStore from '../../stores/UserStore';
import AppStore from '../../stores/AppStore';
-import { gaPage } from '../../lib/analytics';
import AccountDashboard from '../../components/settings/account/AccountDashboard';
@@ -14,9 +13,6 @@ const { BrowserWindow } = remote;
@inject('stores', 'actions') @observer
export default class AccountScreen extends Component {
- componentDidMount() {
- gaPage('Settings/Account Dashboard');
- }
onCloseWindow() {
const { user, payment } = this.props.stores;
diff --git a/src/containers/settings/EditServiceScreen.js b/src/containers/settings/EditServiceScreen.js
index 67c2731fc..96621ff1f 100644
--- a/src/containers/settings/EditServiceScreen.js
+++ b/src/containers/settings/EditServiceScreen.js
@@ -7,7 +7,6 @@ import UserStore from '../../stores/UserStore';
import RecipesStore from '../../stores/RecipesStore';
import ServicesStore from '../../stores/ServicesStore';
import Form from '../../lib/Form';
-import { gaPage } from '../../lib/analytics';
import ServiceError from '../../components/settings/services/ServiceError';
import EditServiceForm from '../../components/settings/services/EditServiceForm';
@@ -58,10 +57,6 @@ export default class EditServiceScreen extends Component {
intl: intlShape,
};
- componentDidMount() {
- gaPage('Settings/Service/Edit');
- }
-
onSubmit(data) {
const { action } = this.props.router.params;
const { recipes, services } = this.props.stores;
diff --git a/src/containers/settings/EditSettingsScreen.js b/src/containers/settings/EditSettingsScreen.js
index 018ce663f..83f700b57 100644
--- a/src/containers/settings/EditSettingsScreen.js
+++ b/src/containers/settings/EditSettingsScreen.js
@@ -8,8 +8,7 @@ import SettingsStore from '../../stores/SettingsStore';
import UserStore from '../../stores/UserStore';
import Form from '../../lib/Form';
import { APP_LOCALES } from '../../i18n/languages';
-import { gaPage } from '../../lib/analytics';
-import { DEFAULT_APP_SETTINGS } from '../../config';
+import { APP_THEMES, DEFAULT_APP_SETTINGS } from '../../config';
import EditSettingsForm from '../../components/settings/settings/EditSettingsForm';
@@ -47,6 +46,14 @@ const messages = defineMessages({
id: 'settings.app.form.showMessagesBadgesWhenMuted',
defaultMessage: '!!!Show unread message badge when notifications are disabled',
},
+ theme: {
+ id: 'settings.app.form.theme',
+ defaultMessage: '!!!Pick Franz theme',
+ },
+ appBackground: {
+ id: 'settings.app.headlineBackground',
+ defaultMessage: '!!!Add app background',
+ },
enableSpellchecking: {
id: 'settings.app.form.enableSpellchecking',
defaultMessage: '!!!Enable spell checking',
@@ -71,10 +78,6 @@ export default class EditSettingsScreen extends Component {
intl: intlShape,
};
- componentDidMount() {
- gaPage('Settings/App');
- }
-
onSubmit(settingsData) {
const { app, settings, user } = this.props.actions;
@@ -92,6 +95,8 @@ export default class EditSettingsScreen extends Component {
enableGPUAcceleration: settingsData.enableGPUAcceleration,
showDisabledServices: settingsData.showDisabledServices,
showMessageBadgeWhenMuted: settingsData.showMessageBadgeWhenMuted,
+ theme: settingsData.theme,
+ appBackground: settingsData.appBackground || DEFAULT_APP_SETTINGS.appBackground,
enableSpellchecking: settingsData.enableSpellchecking,
beta: settingsData.beta, // we need this info in the main process as well
locale: settingsData.locale, // we need this info in the main process as well
@@ -117,6 +122,13 @@ export default class EditSettingsScreen extends Component {
label: APP_LOCALES[key],
});
});
+ const themes = [];
+ Object.keys(APP_THEMES).sort(Intl.Collator().compare).forEach((key) => {
+ themes.push({
+ value: key,
+ label: APP_THEMES[key],
+ });
+ });
const config = {
fields: {
@@ -155,6 +167,17 @@ export default class EditSettingsScreen extends Component {
value: settings.all.app.showMessageBadgeWhenMuted,
default: DEFAULT_APP_SETTINGS.showMessageBadgeWhenMuted,
},
+ theme: {
+ label: intl.formatMessage(messages.theme),
+ value: settings.all.app.theme,
+ options: themes,
+ default: DEFAULT_APP_SETTINGS.theme,
+ },
+ appBackground: {
+ label: intl.formatMessage(messages.appBackground),
+ value: settings.all.app.appBackground,
+ default: DEFAULT_APP_SETTINGS.appBackground,
+ },
enableSpellchecking: {
label: intl.formatMessage(messages.enableSpellchecking),
value: settings.all.app.enableSpellchecking,
@@ -198,6 +221,7 @@ export default class EditSettingsScreen extends Component {
return (
{
const { filter } = this.props.params;
const { currentFilter } = this.state;
if (filter === 'all' && currentFilter !== 'all') {
- gaPage('Settings/Recipe Dashboard/All');
this.setState({ currentFilter: 'all' });
} else if (filter === 'featured' && currentFilter !== 'featured') {
- gaPage('Settings/Recipe Dashboard/Featured');
this.setState({ currentFilter: 'featured' });
} else if (filter === 'dev' && currentFilter !== 'dev') {
- gaPage('Settings/Recipe Dashboard/Dev');
this.setState({ currentFilter: 'dev' });
}
});
diff --git a/src/containers/settings/ServicesScreen.js b/src/containers/settings/ServicesScreen.js
index 12db1bcd3..94483ff28 100644
--- a/src/containers/settings/ServicesScreen.js
+++ b/src/containers/settings/ServicesScreen.js
@@ -6,15 +6,11 @@ import { RouterStore } from 'mobx-react-router';
// import RecipePreviewsStore from '../../stores/RecipePreviewsStore';
import UserStore from '../../stores/UserStore';
import ServiceStore from '../../stores/ServicesStore';
-import { gaPage } from '../../lib/analytics';
import ServicesDashboard from '../../components/settings/services/ServicesDashboard';
@inject('stores', 'actions') @observer
export default class ServicesScreen extends Component {
- componentDidMount() {
- gaPage('Settings/Service Dashboard');
- }
componentWillUnmount() {
this.props.actions.service.resetFilter();
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index 0c62da44a..64f510ad6 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -146,6 +146,8 @@
"settings.app.headlineLanguage": "Language",
"settings.app.headlineUpdates": "Updates",
"settings.app.headlineAppearance": "Appearance",
+ "settings.app.headlineAppTheme": "Application theme",
+ "settings.app.headlineBackground": "Application background",
"settings.app.headlineAdvanced": "Advanced",
"settings.app.buttonSearchForUpdate": "Check for updates",
"settings.app.buttonInstallUpdate": "Restart & install update",
@@ -153,6 +155,8 @@
"settings.app.updateStatusAvailable": "Update available, downloading...",
"settings.app.updateStatusUpToDate": "You are using the latest version of Franz",
"settings.app.subheadlineCache": "Cache",
+ "settings.app.setBackground": "Set application background",
+ "settings.app.resetBackground": "Reset application background",
"settings.app.cacheInfo": "Franz cache is currently using {size} of disk space.",
"settings.app.buttonClearAllCache": "Clear cache",
"settings.app.form.autoLaunchOnStart": "Launch Franz on start",
@@ -167,6 +171,8 @@
"settings.app.form.enableGPUAcceleration": "Enable GPU Acceleration",
"settings.app.form.showDisabledServices": "Display disabled services tabs",
"settings.app.form.showMessagesBadgesWhenMuted": "Show unread message badge when notifications are disabled",
+ "settings.app.form.theme": "Pick Franz theme",
+ "settings.app.form.setBackground": "Set background",
"settings.app.form.beta": "Include beta versions",
"settings.app.translationHelp": "Help us to translate Franz into your language.",
"settings.app.currentVersion": "Current version:",
diff --git a/src/index.js b/src/index.js
index 5ba901b89..52a8e167b 100644
--- a/src/index.js
+++ b/src/index.js
@@ -83,6 +83,7 @@ const createWindow = () => {
titleBarStyle: isMac ? 'hidden' : '',
frame: isLinux,
backgroundColor: '#3498db',
+ show: false,
});
// Initialize System Tray
@@ -134,6 +135,10 @@ const createWindow = () => {
}
});
+ mainWindow.once('ready-to-show', () => {
+ mainWindow.show();
+ });
+
mainWindow.on('maximize', () => {
app.isMaximized = true;
});
diff --git a/src/lib/Menu.js b/src/lib/Menu.js
index 19b8d5b15..6960dd49c 100644
--- a/src/lib/Menu.js
+++ b/src/lib/Menu.js
@@ -644,6 +644,10 @@ export default class FranzMenu {
{
label: intl.formatMessage(menuItems.quit),
role: 'quit',
+ accelerator: 'Alt+F4',
+ click: () => {
+ app.quit();
+ },
},
];
diff --git a/src/lib/analytics.js b/src/lib/analytics.js
deleted file mode 100644
index 585cbcdba..000000000
--- a/src/lib/analytics.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import { remote } from 'electron';
-import { GA_ID } from '../config';
-// import { isDevMode } from '../environment';
-
-const debug = require('debug')('Analytics');
-
-const { app } = remote;
-
-/* eslint-disable */
-(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
-(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
-m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
-})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
-/* eslint-enable */
-
-const GA_LOCAL_STORAGE_KEY = 'gaUid';
-
-ga('create', GA_ID, {
- storage: 'none',
- clientId: localStorage.getItem(GA_LOCAL_STORAGE_KEY),
-});
-
-ga((tracker) => {
- localStorage.setItem(GA_LOCAL_STORAGE_KEY, tracker.get('clientId'));
-});
-ga('set', 'checkProtocolTask', null);
-ga('set', 'version', app.getVersion());
-ga('send', 'App');
-
-export function gaPage(page) {
- ga('send', 'pageview', page);
-
- debug('GA track page', page);
-}
-
-export function gaEvent(category, action, label) {
- ga('send', 'event', category, action, label);
-
- debug('GA track page', category, action);
-}
-
-setTimeout(() => {
- ga('send', 'Ping');
-}, 1000 * 60 * 10); // Ping GA every 10 Minutes
diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js
index 38edff1b4..c809bc95b 100644
--- a/src/stores/AppStore.js
+++ b/src/stores/AppStore.js
@@ -11,7 +11,6 @@ import Request from './lib/Request';
import { CHECK_INTERVAL, DEFAULT_APP_SETTINGS } from '../config';
import { isMac, isLinux, isWindows } from '../environment';
import locales from '../i18n/translations';
-import { gaEvent } from '../lib/analytics';
import { getServiceIdsFromPartitions, removeServicePartitionDirectory } from '../helpers/service-helpers.js';
@@ -216,7 +215,6 @@ export default class AppStore extends Store {
console.warn(err);
}
- gaEvent('App', enable ? 'enable autostart' : 'disable autostart');
}
@action _openExternalUrl({ url }) {
diff --git a/src/stores/PaymentStore.js b/src/stores/PaymentStore.js
index 9e348d14e..7c4e4e75e 100644
--- a/src/stores/PaymentStore.js
+++ b/src/stores/PaymentStore.js
@@ -1,9 +1,8 @@
-import { action, observable, computed } from 'mobx';
-
-import Store from './lib/Store';
+import { action, computed, observable } from 'mobx';
import CachedRequest from './lib/CachedRequest';
import Request from './lib/Request';
-import { gaEvent } from '../lib/analytics';
+
+import Store from './lib/Store';
export default class PaymentStore extends Store {
@observable plansRequest = new CachedRequest(this.api.payment, 'plans');
@@ -30,18 +29,10 @@ export default class PaymentStore extends Store {
}
@action _createHostedPage({ planId }) {
- const request = this.createHostedPageRequest.execute(planId);
-
- gaEvent('Payment', 'createHostedPage', planId);
-
- return request;
+ return this.createHostedPageRequest.execute(planId);
}
@action _createDashboardUrl() {
- const request = this.createDashboardUrlRequest.execute();
-
- gaEvent('Payment', 'createDashboardUrl');
-
- return request;
+ return this.createDashboardUrlRequest.execute();
}
}
diff --git a/src/stores/RecipePreviewsStore.js b/src/stores/RecipePreviewsStore.js
index e25936f15..4f85e18d7 100644
--- a/src/stores/RecipePreviewsStore.js
+++ b/src/stores/RecipePreviewsStore.js
@@ -4,7 +4,6 @@ import { debounce } from 'lodash';
import Store from './lib/Store';
import CachedRequest from './lib/CachedRequest';
import Request from './lib/Request';
-import { gaEvent } from '../lib/analytics';
export default class RecipePreviewsStore extends Store {
@observable allRecipePreviewsRequest = new CachedRequest(this.api.recipePreviews, 'all');
@@ -38,13 +37,6 @@ export default class RecipePreviewsStore extends Store {
@action _search({ needle }) {
if (needle !== '') {
this.searchRecipePreviewsRequest.execute(needle);
-
- this._analyticsSearch(needle);
}
}
-
- // Helper
- _analyticsSearch = debounce((needle) => {
- gaEvent('Recipe', 'search', needle);
- }, 3000);
}
diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js
index ccb85421a..057653e75 100644
--- a/src/stores/ServicesStore.js
+++ b/src/stores/ServicesStore.js
@@ -5,7 +5,6 @@ import Store from './lib/Store';
import Request from './lib/Request';
import CachedRequest from './lib/CachedRequest';
import { matchRoute } from '../helpers/routing-helpers';
-import { gaEvent } from '../lib/analytics';
const debug = require('debug')('ServiceStore');
@@ -154,7 +153,6 @@ export default class ServicesStore extends Store {
if (redirect) {
this.stores.router.push('/settings/recipes');
- gaEvent('Service', 'create', recipeId);
}
}
@@ -216,7 +214,6 @@ export default class ServicesStore extends Store {
if (redirect) {
this.stores.router.push('/settings/services');
- gaEvent('Service', 'update', service.recipe.id);
}
}
@@ -231,19 +228,14 @@ export default class ServicesStore extends Store {
remove(result, c => c.id === serviceId);
});
- const service = this.one(serviceId);
-
await request._promise;
this.actionStatus = request.result.status;
-
- gaEvent('Service', 'delete', service.recipe.id);
}
@action async _clearCache({ serviceId }) {
this.clearCacheRequest.reset();
const request = this.clearCacheRequest.execute(serviceId);
await request._promise;
- gaEvent('Service', 'clear cache');
}
@action _setActive({ serviceId }) {
@@ -457,8 +449,6 @@ export default class ServicesStore extends Store {
service.order = services[s.id];
});
});
-
- this._reorderAnalytics();
}
@action _toggleNotifications({ serviceId }) {
@@ -620,10 +610,6 @@ export default class ServicesStore extends Store {
}
}
- _reorderAnalytics = debounce(() => {
- gaEvent('Service', 'order');
- }, 5000);
-
_wrapIndex(index, delta, size) {
return (((index + delta) % size) + size) % size;
}
diff --git a/src/stores/SettingsStore.js b/src/stores/SettingsStore.js
index d8519c609..b27088493 100644
--- a/src/stores/SettingsStore.js
+++ b/src/stores/SettingsStore.js
@@ -1,10 +1,11 @@
import { action, computed, observable } from 'mobx';
import localStorage from 'mobx-localstorage';
-
-import Store from './lib/Store';
+import { DEFAULT_APP_SETTINGS } from '../config';
import SettingsModel from '../models/Settings';
-import Request from './lib/Request';
import CachedRequest from './lib/CachedRequest';
+import Request from './lib/Request';
+
+import Store from './lib/Store';
const debug = require('debug')('SettingsStore');
@@ -18,6 +19,8 @@ export default class SettingsStore extends Store {
// Register action handlers
this.actions.settings.update.listen(this._update.bind(this));
this.actions.settings.remove.listen(this._remove.bind(this));
+ this.actions.settings.setBackground.listen(this._setBackground.bind(this));
+ this.actions.settings.resetBackground.listen(this._resetBackground.bind(this));
}
async setup() {
@@ -48,10 +51,23 @@ export default class SettingsStore extends Store {
this.appSettingsRequest.patch((result) => {
if (!result) return;
Object.assign(result, data);
+ this.actions.ui.changeTheme(result.theme);
});
}
}
+ @action _setBackground(background) {
+ if (background && typeof background === 'string') {
+ document.body.style.backgroundImage = `url("${background}")`;
+ this._update({ type: 'app', data: { backgrounds: window.bcgs, appBackground: background } });
+ }
+ }
+
+ @action _resetBackground() {
+ document.body.style.backgroundImage = '';
+ this._update({ type: 'app', data: { backgrounds: window.bcgs, appBackground: DEFAULT_APP_SETTINGS.appBackground } });
+ }
+
@action async _remove({ type, key }) {
if (type === 'app') return; // app keys can't be deleted
@@ -69,6 +85,9 @@ export default class SettingsStore extends Store {
// Helper
_migrate() {
const legacySettings = localStorage.getItem('app');
+ if (!legacySettings) {
+ return;
+ }
if (!this.all.migration['5.0.0-beta.17-settings']) {
this.actions.settings.update({
@@ -81,6 +100,8 @@ export default class SettingsStore extends Store {
isAppMuted: legacySettings.isAppMuted,
enableGPUAcceleration: legacySettings.enableGPUAcceleration,
showMessageBadgeWhenMuted: legacySettings.showMessageBadgeWhenMuted,
+ theme: legacySettings.theme,
+ appBackground: legacySettings.appBackground,
showDisabledServices: legacySettings.showDisabledServices,
enableSpellchecking: legacySettings.enableSpellchecking,
},
diff --git a/src/stores/UIStore.js b/src/stores/UIStore.js
index b391bdcae..80ed297d8 100644
--- a/src/stores/UIStore.js
+++ b/src/stores/UIStore.js
@@ -12,6 +12,7 @@ export default class UIStore extends Store {
this.actions.ui.openSettings.listen(this._openSettings.bind(this));
this.actions.ui.closeSettings.listen(this._closeSettings.bind(this));
this.actions.ui.toggleServiceUpdatedInfoBar.listen(this._toggleServiceUpdatedInfoBar.bind(this));
+ this.actions.ui.changeTheme.listen(this._changeTheme.bind(this));
}
@computed get showMessageBadgesEvenWhenMuted() {
@@ -37,4 +38,23 @@ export default class UIStore extends Store {
}
this.showServicesUpdatedInfoBar = visibility;
}
+
+ @action _changeTheme(themeName) {
+ const currentClassList = document.body.classList;
+ if (themeName && !currentClassList.contains(themeName)) {
+ let name = themeName;
+ if (!themeName.startsWith('theme-')) {
+ name = `theme-${themeName}`;
+ }
+ [...currentClassList].forEach((c) => {
+ if (c && c.startsWith('theme-')) {
+ document.body.classList.remove(c);
+ }
+ });
+ if (name === 'theme-regular') {
+ return;
+ }
+ document.body.classList.add(name);
+ }
+ }
}
diff --git a/src/stores/UserStore.js b/src/stores/UserStore.js
index 574616925..02c99a0fb 100644
--- a/src/stores/UserStore.js
+++ b/src/stores/UserStore.js
@@ -7,7 +7,6 @@ import { isDevMode } from '../environment';
import Store from './lib/Store';
import Request from './lib/Request';
import CachedRequest from './lib/CachedRequest';
-import { gaEvent } from '../lib/analytics';
const debug = require('debug')('UserStore');
@@ -137,8 +136,6 @@ export default class UserStore extends Store {
this._setUserData(authToken);
this.stores.router.push('/');
-
- gaEvent('User', 'login');
}
@action async _signup({ firstname, lastname, email, password, accountType, company }) {
@@ -156,8 +153,6 @@ export default class UserStore extends Store {
this._setUserData(authToken);
this.stores.router.push(this.PRICING_ROUTE);
-
- gaEvent('User', 'signup');
}
@action async _retrievePassword({ email }) {
@@ -165,8 +160,6 @@ export default class UserStore extends Store {
await request._promise;
this.actionStatus = request.result.status || [];
-
- gaEvent('User', 'retrievePassword');
}
@action async _invite({ invites }) {
@@ -180,8 +173,6 @@ export default class UserStore extends Store {
if (this.stores.router.location.pathname.includes(this.INVITE_ROUTE)) {
this.stores.router.push('/');
}
-
- gaEvent('User', 'inviteUsers');
}
@action async _update({ userData }) {
@@ -189,8 +180,6 @@ export default class UserStore extends Store {
this.getUserInfoRequest.patch(() => response.data);
this.actionStatus = response.status || [];
-
- gaEvent('User', 'update');
}
@action _resetStatus() {
diff --git a/src/styles/main.scss b/src/styles/main.scss
index 784a04d3d..6e51517d7 100644
--- a/src/styles/main.scss
+++ b/src/styles/main.scss
@@ -39,3 +39,6 @@ $mdi-font-path: '../node_modules/mdi/fonts';
@import './searchInput.scss';
@import './select.scss';
@import './image-upload.scss';
+
+//themes
+@import './themes.scss';
diff --git a/src/styles/themes.scss b/src/styles/themes.scss
new file mode 100644
index 000000000..9d19d7a73
--- /dev/null
+++ b/src/styles/themes.scss
@@ -0,0 +1,210 @@
+$theme-dark: (
+ theme-black: black,
+ theme-white: white,
+ theme-gray-dark: #b5bec4,
+ theme-gray: #989ea3,
+ theme-gray-light: #646b70,
+ theme-gray-lighter: #373a3c,
+ theme-gray-lightest: #2d2d2d
+);
+
+$theme-transparent-dark: (
+ theme-black: black,
+ theme-white: white,
+ theme-gray-dark: transparentize(#b5bec4, .5),
+ theme-gray: transparentize(#989ea3, .5),
+ theme-gray-light: transparentize(#646b70, .5),
+ theme-gray-lighter: transparentize(#373a3c, .5),
+ theme-gray-lightest: transparentize(#2d2d2d, .5)
+);
+
+/* TODO out */
+body.theme-transparent-dark {
+ background-size: cover;
+ background-image: url('../assets/images/galaxy2.jpg');
+ &.transparent-dark .auth {
+ .auth__layout>div>span {
+ justify-content: flex-start;
+ }
+ .welcome {
+ margin-left: 10%;
+ }
+ .auth__container {
+ margin: 40px auto 0 10%;
+ }
+ }
+ .services,
+ .services .services__webview-wrapper,
+ .services .services__webview-wrapper webview{
+ background: transparent;
+ }
+}
+.add-bg-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ button {
+ flex: 1;
+ }
+ button:not(:first-child) {
+ margin-left: 16px;
+ }
+}
+.bcgs-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+}
+
+$all-themes: (
+ theme-dark: $theme-dark,
+ theme-transparent-dark: $theme-transparent-dark
+) !default;
+
+@function map-deep-get($map, $keys...) {
+ @each $key in $keys {
+ $map: map-get($map, $key);
+ }
+ @return $map;
+}
+
+@function get-color($theme-name, $color-name) {
+ @return map-get(map-get($all-themes,$theme-name), $color-name);
+}
+
+@mixin set-theme($theme-name) {
+ .#{$theme-name} {
+ #electron-app-title-bar {
+ background: get-color($theme-name, 'theme-gray-lightest');
+ .toolbar-dropdown:not(.open)>.toolbar-button > button:hover {
+ background: get-color($theme-name, 'theme-gray-lighter');
+ }
+ .list-item {
+ &.selected, &.selected:focus .menu-item {
+ .menu-item {
+ background: get-color($theme-name, 'theme-gray-lighter');
+ }
+ }
+ }
+ .menu-pane hr {
+ border-color: get-color($theme-name, 'theme-gray-lighter');
+ }
+ .toolbar-dropdown.open > .toolbar-button > button {
+ background-color: get-color($theme-name, 'theme-gray-lighter');
+ color: get-color($theme-name, 'theme-white');
+ }
+ #app-menu-bar #foldout-container .foldout .menu-pane,
+ .list .ReactVirtualized__Grid {
+ background: get-color($theme-name, 'theme-gray-lightest');
+ color: get-color($theme-name, 'theme-gray');
+ }
+ .list svg path {
+ fill: get-color($theme-name, 'theme-gray');
+ }
+ }
+ .sidebar {
+ background: get-color($theme-name, 'theme-gray-lightest');
+ .tab-item.is-active {
+ background: get-color($theme-name, 'theme-gray-lighter');
+ }
+ }
+ .settings-wrapper {
+ .settings {
+ background: get-color($theme-name, 'theme-gray-lighter');
+ color: get-color($theme-name, 'theme-white');
+ .recipe-teaser {
+ background: get-color($theme-name, 'theme-gray-lightest');
+ color: get-color($theme-name, 'theme-white');
+ }
+ .account {
+ .account__box {
+ background: get-color($theme-name, 'theme-gray-lightest');
+ .franz-form__radio {
+ color: get-color($theme-name, 'theme-white');
+ }
+ .franz-form__radio.is-selected {
+ background: $theme-brand-primary;
+ }
+ }
+ }
+ .settings__controls {
+ background: get-color($theme-name, 'theme-gray-lighter');
+ }
+ .settings-navigation {
+ background: get-color($theme-name, 'theme-gray-lightest');
+ a {
+ color: get-color($theme-name, 'theme-gray');
+ }
+ .settings-navigation__link.is-active {
+ background: get-color($theme-name, 'theme-gray-lighter');
+ color: get-color($theme-name, 'theme-white');
+ }
+ .settings-navigation__link:hover {
+ background: get-color($theme-name, 'theme-gray-light');
+ color: get-color($theme-name, 'theme-white');
+ }
+ }
+ .account, .settings-navigation a {
+ .badge {
+ background: $theme-brand-primary;
+ color: get-color($theme-name, 'theme-white');
+ }
+ }
+ .settings__header, button.settings__close {
+ background: get-color($theme-name, 'theme-gray-lightest');
+ }
+ .settings__body select {
+ background: get-color($theme-name, 'theme-gray-lightest');
+ border-color: get-color($theme-name, 'theme-gray-light');
+ color: get-color($theme-name, 'theme-white');
+ }
+ button.settings__close {
+ border-color: get-color($theme-name, 'theme-gray-lighter');
+ }
+ table.service-table .service-table__row {
+ border-color: get-color($theme-name, 'theme-gray-light');
+ &:hover {
+ background: get-color($theme-name, 'theme-gray-lightest');
+ }
+ }
+ }
+ }
+ .auth {
+ @if $theme-name == 'theme-transparent-dark' {
+ background: transparent;
+ } @else {
+ background: get-color($theme-name, 'theme-gray-lightest');
+ }
+ .auth__layout>div>span {
+ display: flex;
+ justify-content: center;
+ }
+ .welcome {
+ background: transparentize(get-color($theme-name, 'theme-black'), 0.5);
+ padding: 16px;
+ border-radius: 8px;
+ .welcome__featured-services {
+ background: transparentize(get-color($theme-name, 'theme-white'), 0.8);;
+ }
+ }
+ .auth__container {
+ background: transparentize(get-color($theme-name, 'theme-black'),0.5);
+ .franz-form .franz-form__label, h1{
+ color: get-color($theme-name, 'theme-white');
+ }
+ }
+ .auth__links {
+ background: transparent;
+ border-top: 1px solid get-color($theme-name, 'theme-gray-light');
+ height: auto;
+ a {
+ color: get-color($theme-name, 'theme-white');
+ }
+ }
+ }
+ }
+}
+
+@each $theme in map-keys($all-themes) {
+ @include set-theme($theme);
+}
diff --git a/yarn.lock b/yarn.lock
index 49ddfc31a..441d04b65 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1959,7 +1959,7 @@ electron-download@^4.0.0:
semver "^5.3.0"
sumchecker "^2.0.1"
-electron-fetch@^1.1.0:
+electron-fetch@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/electron-fetch/-/electron-fetch-1.1.0.tgz#74b0ea547fe149620d38596a84fb104d34218e31"
dependencies: