diff --git a/CHANGELOG.md b/CHANGELOG.md index cdc89b325..1d56e9681 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +# [5.0.0-beta.19](https://github.com/meetfranz/franz/compare/v5.0.0-beta.18...v5.0.0-beta.19) (2018-11-02) + + +### Bug Fixes + +* **App:** Allow hidden directories in franz recipes directory ([90d495f](https://github.com/meetfranz/franz/commit/90d495f)) +* **App:** Fix broken quit button and shortcut + body theme ([3e9eac5](https://github.com/meetfranz/franz/commit/3e9eac5)) + + +### Features + +* **App:** Add themes picker and two basic themes ([99612e8](https://github.com/meetfranz/franz/commit/99612e8)) +* **App:** Remove Google Analytics ([a1bc702](https://github.com/meetfranz/franz/commit/a1bc702)) +* **App:** Set background image UI ([ac1ace3](https://github.com/meetfranz/franz/commit/ac1ace3)) + + + # [5.0.0-beta.18](https://github.com/meetfranz/franz/compare/v5.0.0-beta.16...v5.0.0-beta.18) (2018-04-03) diff --git a/README.md b/README.md index 5163fe46f..c4d97944e 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,17 @@ +**This is my fork of Franz 5. Created mainly to add dark theme and later transparent and background image.** +--- + + +### Transparent theme instructions +Transparent theme works best with transparent services (apps). I added transparent themes for only those apps I use. +At the moment: Slack, Gmail, Messenger, Slack, Telegram, GoogleCalendar. If you wish some other you can create issue with a request. + +To use transparent services it's best to clone whole repository to franz directory. Detailed instruction [here](https://github.com/MikeDabrowski/franz-transparent-recipes) + + +--- + + **This repository is only for Franz 5 and later, previous versions are no longer maintained.** --- diff --git a/package.json b/package.json index 3647e963b..7922e86db 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "franz", "productName": "Franz", "appId": "com.meetfranz.franz", - "version": "5.0.0-beta.18", + "version": "5.0.0-beta.19", "description": "Messaging app for WhatsApp, Slack, Telegram, HipChat, Hangouts and many many more.", "copyright": "adlk x franz - Stefan Malzner", "main": "index.js", @@ -35,7 +35,7 @@ "classnames": "^2.2.5", "debug-electron": "^0.0.4", "du": "^0.1.0", - "electron-fetch": "^1.1.0", + "electron-fetch": "1.1.0", "electron-react-titlebar": "^0.7.1", "electron-spellchecker": "^1.1.2", "electron-updater": "^2.4.3", diff --git a/src/actions/settings.js b/src/actions/settings.js index fd29b798b..f1aeec69c 100644 --- a/src/actions/settings.js +++ b/src/actions/settings.js @@ -9,4 +9,6 @@ export default { type: PropTypes.string.isRequired, key: PropTypes.string.isRequired, }, + setBackground: {}, + resetBackground: {}, }; diff --git a/src/actions/ui.js b/src/actions/ui.js index b913b430b..ab9c75d78 100644 --- a/src/actions/ui.js +++ b/src/actions/ui.js @@ -8,4 +8,5 @@ export default { toggleServiceUpdatedInfoBar: { visible: PropTypes.bool, }, + changeTheme: {}, }; diff --git a/src/api/server/ServerApi.js b/src/api/server/ServerApi.js index 353faa7f4..e09100ea0 100644 --- a/src/api/server/ServerApi.js +++ b/src/api/server/ServerApi.js @@ -269,6 +269,7 @@ export default class ServerApi { fs.statSync(path.join(recipesDirectory, file)).isDirectory() && file !== 'temp' && file !== 'dev' + && !file.startsWith('.') )); this.recipes = paths.map((id) => { diff --git a/src/app.js b/src/app.js index 814bfacf1..51a4e8e21 100644 --- a/src/app.js +++ b/src/app.js @@ -16,7 +16,6 @@ import apiFactory from './api'; import actions from './actions'; import MenuFactory from './lib/Menu'; import TouchBarFactory from './lib/TouchBar'; -import * as analytics from './lib/analytics'; import I18N from './I18n'; import AppLayoutContainer from './containers/layout/AppLayoutContainer'; @@ -59,7 +58,6 @@ window.addEventListener('load', () => { api, menu, touchBar, - analytics, render() { const preparedApp = ( 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)}

+ + +
+
+ {/* Language */}

{intl.formatMessage(messages.headlineLanguage)}

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: