Permalink
Browse files

feat(Windows): Replace window frame with custom menu bar

Title bar for Windows & Linux
  • Loading branch information...
adlk committed Mar 4, 2018
2 parents 27b0c02 + a17cfb3 commit 9af5fd05910615b8101ef84bd1b43d7fe78dedc4
@@ -9,7 +9,8 @@
"program": "${workspaceFolder}/build/index.js",
"protocol": "inspector",
"env": {
"NODE_ENV": "development"
"NODE_ENV": "development",
"OS_PLATFORM": "win32"
}
},
{
@@ -18,9 +19,10 @@
"name": "Franz – Live API",
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
"program": "${workspaceFolder}/build/index.js",
"protocol": "inspector",
"protocol": "inspector",
"env": {
"LIVE_API": "1"
"LIVE_API": "1",
"OS_PLATFORM": "win32"
}
},
{
@@ -31,7 +33,8 @@
"program": "${workspaceFolder}/build/index.js",
"protocol": "inspector",
"env": {
"LOCAL_API": "1"
"LOCAL_API": "1",
"OS_PLATFORM": "win32"
}
}
]
@@ -35,6 +35,7 @@
"classnames": "^2.2.5",
"du": "^0.1.0",
"electron-fetch": "^1.1.0",
"electron-react-titlebar": "^0.7.1",
"electron-spellchecker": "^1.1.2",
"electron-updater": "^2.4.3",
"electron-window-state": "^4.1.0",
@@ -9,11 +9,18 @@ import UserStore from './stores/UserStore';
@inject('stores') @observer
export default class I18N extends Component {
componentDidUpdate() {
window.franz.menu.rebuild();
}
render() {
const { stores, children } = this.props;
const { locale } = stores.app;
return (
<IntlProvider {...{ locale, key: locale, messages: translations[locale] }}>
<IntlProvider
{...{ locale, key: locale, messages: translations[locale] }}
ref={(intlProvider) => { window.franz.intl = intlProvider ? intlProvider.getChildContext().intl : null; }}
>
{children}
</IntlProvider>
);
@@ -2,10 +2,13 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { observer, PropTypes as MobxPropTypes } from 'mobx-react';
import { defineMessages, intlShape } from 'react-intl';
import { TitleBar } from 'electron-react-titlebar';
import InfoBar from '../ui/InfoBar';
import globalMessages from '../../i18n/globalMessages';
import { isMac } from '../../environment';
function createMarkup(HTMLString) {
return { __html: HTMLString };
}
@@ -87,64 +90,67 @@ export default class AppLayout extends Component {
return (
<div>
<div className="app">
{sidebar}
<div className="app__service">
{news.length > 0 && news.map(item => (
<InfoBar
key={item.id}
position="top"
type={item.type}
sticky={item.sticky}
onHide={() => removeNewsItem({ newsId: item.id })}
>
<span dangerouslySetInnerHTML={createMarkup(item.message)} />
</InfoBar>
))}
{!isOnline && (
<InfoBar
type="danger"
>
<span className="mdi mdi-flash" />
{intl.formatMessage(globalMessages.notConnectedToTheInternet)}
</InfoBar>
)}
{!areRequiredRequestsSuccessful && showRequiredRequestsError && (
<InfoBar
type="danger"
ctaLabel="Try again"
ctaLoading={areRequiredRequestsLoading}
sticky
onClick={retryRequiredRequests}
>
<span className="mdi mdi-flash" />
{intl.formatMessage(messages.requiredRequestsFailed)}
</InfoBar>
)}
{showServicesUpdatedInfoBar && (
<InfoBar
type="primary"
ctaLabel={intl.formatMessage(messages.buttonReloadServices)}
onClick={reloadServicesAfterUpdate}
sticky
>
<span className="mdi mdi-power-plug" />
{intl.formatMessage(messages.servicesUpdated)}
</InfoBar>
)}
{appUpdateIsDownloaded && (
<InfoBar
type="primary"
ctaLabel={intl.formatMessage(messages.buttonInstallUpdate)}
onClick={installAppUpdate}
sticky
>
<span className="mdi mdi-information" />
{intl.formatMessage(messages.updateAvailable)} <a href="https://meetfranz.com/changelog" target="_blank">
<u>{intl.formatMessage(messages.changelog)}</u>
</a>
</InfoBar>
)}
{services}
{!isMac && <TitleBar menu={window.franz.menu.template} icon={'assets/images/logo.svg'} />}
<div className="app__content">
{sidebar}
<div className="app__service">
{news.length > 0 && news.map(item => (
<InfoBar
key={item.id}
position="top"
type={item.type}
sticky={item.sticky}
onHide={() => removeNewsItem({ newsId: item.id })}
>
<span dangerouslySetInnerHTML={createMarkup(item.message)} />
</InfoBar>
))}
{!isOnline && (
<InfoBar
type="danger"
>
<span className="mdi mdi-flash" />
{intl.formatMessage(globalMessages.notConnectedToTheInternet)}
</InfoBar>
)}
{!areRequiredRequestsSuccessful && showRequiredRequestsError && (
<InfoBar
type="danger"
ctaLabel="Try again"
ctaLoading={areRequiredRequestsLoading}
sticky
onClick={retryRequiredRequests}
>
<span className="mdi mdi-flash" />
{intl.formatMessage(messages.requiredRequestsFailed)}
</InfoBar>
)}
{showServicesUpdatedInfoBar && (
<InfoBar
type="primary"
ctaLabel={intl.formatMessage(messages.buttonReloadServices)}
onClick={reloadServicesAfterUpdate}
sticky
>
<span className="mdi mdi-power-plug" />
{intl.formatMessage(messages.servicesUpdated)}
</InfoBar>
)}
{appUpdateIsDownloaded && (
<InfoBar
type="primary"
ctaLabel={intl.formatMessage(messages.buttonInstallUpdate)}
onClick={installAppUpdate}
sticky
>
<span className="mdi mdi-information" />
{intl.formatMessage(messages.updateAvailable)} <a href="https://meetfranz.com/changelog" target="_blank">
<u>{intl.formatMessage(messages.changelog)}</u>
</a>
</InfoBar>
)}
{services}
</div>
</div>
</div>
{children}
@@ -4,11 +4,17 @@ export const isDevMode = Boolean(process.execPath.match(/[\\/]electron/));
export const useLiveAPI = process.env.LIVE_API;
export const useLocalAPI = process.env.LOCAL_API;
export const isMac = process.platform === 'darwin';
export const isWindows = process.platform === 'win32';
export const isLinux = process.platform === 'linux';
let platform = process.platform;
if (process.env.OS_PLATFORM) {
platform = process.env.OS_PLATFORM;
}
export const isMac = platform === 'darwin';
export const isWindows = platform === 'win32';
export const isLinux = platform === 'linux';
export const ctrlKey = isMac ? '' : 'Ctrl';
export const cmdKey = isMac ? 'Cmd' : 'Ctrl';
let api;
if (!isDevMode || (isDevMode && useLiveAPI)) {
@@ -1,6 +1,31 @@
import { defineMessages } from 'react-intl';
const messages = defineMessages({
required: {
id: 'validation.required',
defaultMessage: '!!!Field is required',
},
email: {
id: 'validation.email',
defaultMessage: '!!!Email not valid',
},
url: {
id: 'validation.url',
defaultMessage: '!!!Not a valid URL',
},
minLength: {
id: 'validation.minLength',
defaultMessage: '!!!Too few characters',
},
oneRequired: {
id: 'validation.oneRequired',
defaultMessage: '!!!At least one is required',
},
});
export function required({ field }) {
const isValid = (field.value.trim() !== '');
return [isValid, `${field.label} is required`];
return [isValid, window.franz.intl.formatMessage(messages.required, { field: field.label })];
}
export function email({ field }) {
@@ -13,7 +38,7 @@ export function email({ field }) {
isValid = true;
}
return [isValid, `${field.label} not valid`];
return [isValid, window.franz.intl.formatMessage(messages.email, { field: field.label })];
}
export function url({ field }) {
@@ -27,7 +52,7 @@ export function url({ field }) {
isValid = true;
}
return [isValid, `${field.label} is not a valid url`];
return [isValid, window.franz.intl.formatMessage(messages.url, { field: field.label })];
}
export function minLength(length) {
@@ -36,13 +61,13 @@ export function minLength(length) {
if (field.touched) {
isValid = field.value.length >= length;
}
return [isValid, `${field.label} should be at least ${length} characters long.`];
return [isValid, window.franz.intl.formatMessage(messages.minLength, { field: field.label, length })];
};
}
export function oneRequired(targets) {
return ({ field, form }) => {
const invalidFields = targets.filter(target => form.$(target).value === '');
return [targets.length !== invalidFields.length, `${field.label} is required`];
return [targets.length !== invalidFields.length, window.franz.intl.formatMessage(messages.required, { field: field.label })];
};
}
@@ -199,5 +199,52 @@
"service.crashHandler.action": "Reload {name}",
"service.crashHandler.autoReload": "Trying to automatically restore {name} in {seconds} seconds",
"service.disabledHandler.headline": "{name} is disabled",
"service.disabledHandler.action": "Enable {name}"
"service.disabledHandler.action": "Enable {name}",
"menu.edit": "Edit",
"menu.edit.undo": "Undo",
"menu.edit.redo": "Redo",
"menu.edit.cut": "Cut",
"menu.edit.copy": "Copy",
"menu.edit.paste": "Paste",
"menu.edit.pasteAndMatchStyle": "Paste And Match Style",
"menu.edit.delete": "Delete",
"menu.edit.selectAll": "Select All",
"menu.edit.speech": "Speech",
"menu.edit.startSpeaking": "Start Speaking",
"menu.edit.stopSpeaking": "Stop Speaking",
"menu.edit.startDictation": "Start Dictation",
"menu.edit.emojiSymbols": "Emoji & Symbols",
"menu.view.resetZoom": "Actual Size",
"menu.view.zoomIn": "Zoom In",
"menu.view.zoomOut": "Zoom Out",
"menu.view.enterFullScreen": "Enter Full Screen",
"menu.view.exitFullScreen": "Exit Full Screen",
"menu.view.toggleFullScreen": "Toggle Full Screen",
"menu.view.toggleDevTools": "Toggle Developer Tools",
"menu.view.toggleServiceDevTools": "Toggle Service Developer Tools",
"menu.view.reloadService": "Reload Service",
"menu.view.reloadFranz": "Reload Franz",
"menu.window.minimize": "Minimize",
"menu.window.close": "Close",
"menu.help.learnMore": "Learn More",
"menu.help.changelog": "Changelog",
"menu.help.support": "Support",
"menu.help.tos": "Terms of Service",
"menu.help.privacy": "Privacy Statement",
"menu.file": "File",
"menu.view": "View",
"menu.services": "Services",
"menu.window": "Window",
"menu.help": "Help",
"menu.app.about": "About Franz",
"menu.app.settings": "Settings",
"menu.app.hide": "Hide",
"menu.app.hideOthers": "Hide Others",
"menu.app.unhide": "Unhide",
"menu.app.quit": "Quit",
"menu.services.addNewService": "Add New Service...",
"validation.required": "{field} is required",
"validation.email": "{field} is not valid",
"validation.url": "{field} is not a valid URL",
"validation.minLength": "{field} should be at least {length} characters long"
}
@@ -11,7 +11,7 @@
<div class="dev-warning">DEV MODE</div>
<div id="root"></div>
<script>
document.querySelector('body').classList.add(process.platform);
document.querySelector('body').classList.add(process.env.OS_PLATFORM ? process.env.OS_PLATFORM : process.platform);
const { isDevMode } = require('./environment');
if (isDevMode) {
@@ -4,7 +4,7 @@ import path from 'path';
import windowStateKeeper from 'electron-window-state';
import { isDevMode, isWindows } from './environment';
import { isDevMode, isMac, isWindows } from './environment';
import ipcApi from './electron/ipc-api';
import Tray from './lib/Tray';
import Settings from './electron/Settings';
@@ -72,9 +72,9 @@ const createWindow = () => {
height: mainWindowState.height,
minWidth: 600,
minHeight: 500,
titleBarStyle: 'hidden',
titleBarStyle: isMac ? 'hidden' : '',
frame: false,
backgroundColor: '#3498db',
autoHideMenuBar: true,
});
// Initialize System Tray
Oops, something went wrong.

0 comments on commit 9af5fd0

Please sign in to comment.