mirror of
https://github.com/TheGreyDiamond/Enlight.git
synced 2026-04-01 07:10:23 +02:00
init
This commit is contained in:
4
enlightApp/app/.testcafe-electron-rc
Normal file
4
enlightApp/app/.testcafe-electron-rc
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"mainWindowUrl": "./app.html",
|
||||
"appPath": "."
|
||||
}
|
||||
28
enlightApp/app/Routes.tsx
Normal file
28
enlightApp/app/Routes.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
/* eslint react/jsx-props-no-spreading: off */
|
||||
import React from 'react';
|
||||
import { Switch, Route } from 'react-router-dom';
|
||||
import routes from './constants/routes.json';
|
||||
import App from './containers/App';
|
||||
import HomePage from './containers/HomePage';
|
||||
|
||||
// Lazily load routes and code split with webpack
|
||||
const LazyCounterPage = React.lazy(() =>
|
||||
import(/* webpackChunkName: "CounterPage" */ './containers/CounterPage')
|
||||
);
|
||||
|
||||
const CounterPage = (props: Record<string, any>) => (
|
||||
<React.Suspense fallback={<h1>Loading...</h1>}>
|
||||
<LazyCounterPage {...props} />
|
||||
</React.Suspense>
|
||||
);
|
||||
|
||||
export default function Routes() {
|
||||
return (
|
||||
<App>
|
||||
<Switch>
|
||||
<Route path={routes.COUNTER} component={CounterPage} />
|
||||
<Route path={routes.HOME} component={HomePage} />
|
||||
</Switch>
|
||||
</App>
|
||||
);
|
||||
}
|
||||
47
enlightApp/app/app.global.css
Normal file
47
enlightApp/app/app.global.css
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* @NOTE: Prepend a `~` to css file paths that are in your node_modules
|
||||
* See https://github.com/webpack-contrib/sass-loader#imports
|
||||
*/
|
||||
@import '~@fortawesome/fontawesome-free/css/all.css';
|
||||
|
||||
body {
|
||||
position: relative;
|
||||
color: white;
|
||||
height: 100vh;
|
||||
background-color: #232c39;
|
||||
background-image: linear-gradient(
|
||||
45deg,
|
||||
rgba(0, 216, 255, 0.5) 10%,
|
||||
rgba(0, 1, 127, 0.7)
|
||||
);
|
||||
font-family: Arial, Helvetica, Helvetica Neue, serif;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
font-size: 2.25rem;
|
||||
font-weight: bold;
|
||||
letter-spacing: -0.025em;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
a {
|
||||
color: white;
|
||||
opacity: 0.75;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
opacity: 1;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
50
enlightApp/app/app.html
Normal file
50
enlightApp/app/app.html
Normal file
@@ -0,0 +1,50 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Hello Electron React!</title>
|
||||
<script>
|
||||
(() => {
|
||||
if (
|
||||
typeof process !== 'object' ||
|
||||
(typeof process === 'object' && !process.env.START_HOT)
|
||||
) {
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = './dist/style.css';
|
||||
// HACK: Writing the script path should be done with webpack
|
||||
document.getElementsByTagName('head')[0].appendChild(link);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script>
|
||||
if (typeof process === 'object') {
|
||||
const scripts = [];
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
// Dynamically insert the DLL script in development env in the
|
||||
// renderer process
|
||||
scripts.push('../dll/renderer.dev.dll.js');
|
||||
}
|
||||
if (process.env.START_HOT) {
|
||||
// Dynamically insert the bundled app script in the renderer process
|
||||
const port = process.env.PORT || 1212;
|
||||
scripts.push(`http://localhost:${port}/dist/renderer.dev.js`);
|
||||
} else {
|
||||
scripts.push('./dist/renderer.prod.js');
|
||||
}
|
||||
|
||||
if (scripts.length) {
|
||||
document.write(
|
||||
scripts
|
||||
.map(script => `<script defer src="${script}"><\/script>`)
|
||||
.join('')
|
||||
);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
BIN
enlightApp/app/app.icns
Normal file
BIN
enlightApp/app/app.icns
Normal file
Binary file not shown.
14
enlightApp/app/components/Home.css
Normal file
14
enlightApp/app/components/Home.css
Normal file
@@ -0,0 +1,14 @@
|
||||
.container {
|
||||
position: absolute;
|
||||
top: 30%;
|
||||
left: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.container h2 {
|
||||
font-size: 5rem;
|
||||
}
|
||||
|
||||
.container a {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
13
enlightApp/app/components/Home.tsx
Normal file
13
enlightApp/app/components/Home.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import routes from '../constants/routes.json';
|
||||
import styles from './Home.css';
|
||||
|
||||
export default function Home(): JSX.Element {
|
||||
return (
|
||||
<div className={styles.container} data-tid="container">
|
||||
<h2>Home</h2>
|
||||
<Link to={routes.COUNTER}>to Counter</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
9
enlightApp/app/components/css.d.ts
vendored
Normal file
9
enlightApp/app/components/css.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
declare module '*.scss' {
|
||||
const content: { [className: string]: string };
|
||||
export default content;
|
||||
}
|
||||
|
||||
declare module '*.css' {
|
||||
const content: { [className: string]: string };
|
||||
export default content;
|
||||
}
|
||||
4
enlightApp/app/constants/routes.json
Normal file
4
enlightApp/app/constants/routes.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"HOME": "/",
|
||||
"COUNTER": "/counter"
|
||||
}
|
||||
10
enlightApp/app/containers/App.tsx
Normal file
10
enlightApp/app/containers/App.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export default function App(props: Props) {
|
||||
const { children } = props;
|
||||
return <>{children}</>;
|
||||
}
|
||||
6
enlightApp/app/containers/CounterPage.tsx
Normal file
6
enlightApp/app/containers/CounterPage.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
import React from 'react';
|
||||
import Counter from '../features/counter/Counter';
|
||||
|
||||
export default function CounterPage() {
|
||||
return <Counter />;
|
||||
}
|
||||
6
enlightApp/app/containers/HomePage.tsx
Normal file
6
enlightApp/app/containers/HomePage.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
import React from 'react';
|
||||
import Home from '../components/Home';
|
||||
|
||||
export default function HomePage() {
|
||||
return <Home />;
|
||||
}
|
||||
22
enlightApp/app/containers/Root.tsx
Normal file
22
enlightApp/app/containers/Root.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { ConnectedRouter } from 'connected-react-router';
|
||||
import { hot } from 'react-hot-loader/root';
|
||||
import { History } from 'history';
|
||||
import { Store } from '../store';
|
||||
import Routes from '../Routes';
|
||||
|
||||
type Props = {
|
||||
store: Store;
|
||||
history: History;
|
||||
};
|
||||
|
||||
const Root = ({ store, history }: Props) => (
|
||||
<Provider store={store}>
|
||||
<ConnectedRouter history={history}>
|
||||
<Routes />
|
||||
</ConnectedRouter>
|
||||
</Provider>
|
||||
);
|
||||
|
||||
export default hot(Root);
|
||||
37
enlightApp/app/features/counter/Counter.css
Normal file
37
enlightApp/app/features/counter/Counter.css
Normal file
@@ -0,0 +1,37 @@
|
||||
.backButton {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.counter {
|
||||
position: absolute;
|
||||
top: 30%;
|
||||
left: 45%;
|
||||
font-size: 10rem;
|
||||
font-weight: bold;
|
||||
letter-spacing: -0.025em;
|
||||
}
|
||||
|
||||
.btnGroup {
|
||||
position: relative;
|
||||
top: 500px;
|
||||
width: 480px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.btn {
|
||||
font-size: 1.6rem;
|
||||
font-weight: bold;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
margin: 10px;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
opacity: 0.7;
|
||||
cursor: pointer;
|
||||
font-family: Arial, Helvetica, Helvetica Neue, sans-serif;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
color: white;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
71
enlightApp/app/features/counter/Counter.tsx
Normal file
71
enlightApp/app/features/counter/Counter.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import React from 'react';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import { Link } from 'react-router-dom';
|
||||
import styles from './Counter.css';
|
||||
import routes from '../../constants/routes.json';
|
||||
import {
|
||||
increment,
|
||||
decrement,
|
||||
incrementIfOdd,
|
||||
incrementAsync,
|
||||
selectCount,
|
||||
} from './counterSlice';
|
||||
|
||||
export default function Counter() {
|
||||
const dispatch = useDispatch();
|
||||
const value = useSelector(selectCount);
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.backButton} data-tid="backButton">
|
||||
<Link to={routes.HOME}>
|
||||
<i className="fa fa-arrow-left fa-3x" />
|
||||
</Link>
|
||||
</div>
|
||||
<div className={`counter ${styles.counter}`} data-tid="counter">
|
||||
{value}
|
||||
</div>
|
||||
<div className={styles.btnGroup}>
|
||||
<button
|
||||
className={styles.btn}
|
||||
onClick={() => {
|
||||
dispatch(increment());
|
||||
}}
|
||||
data-tclass="btn"
|
||||
type="button"
|
||||
>
|
||||
<i className="fa fa-plus" />
|
||||
</button>
|
||||
<button
|
||||
className={styles.btn}
|
||||
onClick={() => {
|
||||
dispatch(decrement());
|
||||
}}
|
||||
data-tclass="btn"
|
||||
type="button"
|
||||
>
|
||||
<i className="fa fa-minus" />
|
||||
</button>
|
||||
<button
|
||||
className={styles.btn}
|
||||
onClick={() => {
|
||||
dispatch(incrementIfOdd());
|
||||
}}
|
||||
data-tclass="btn"
|
||||
type="button"
|
||||
>
|
||||
odd
|
||||
</button>
|
||||
<button
|
||||
className={styles.btn}
|
||||
onClick={() => {
|
||||
dispatch(incrementAsync());
|
||||
}}
|
||||
data-tclass="btn"
|
||||
type="button"
|
||||
>
|
||||
async
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
38
enlightApp/app/features/counter/counterSlice.ts
Normal file
38
enlightApp/app/features/counter/counterSlice.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import { AppThunk, RootState } from '../../store';
|
||||
|
||||
const counterSlice = createSlice({
|
||||
name: 'counter',
|
||||
initialState: { value: 0 },
|
||||
reducers: {
|
||||
increment: (state) => {
|
||||
state.value += 1;
|
||||
},
|
||||
decrement: (state) => {
|
||||
state.value -= 1;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { increment, decrement } = counterSlice.actions;
|
||||
|
||||
export const incrementIfOdd = (): AppThunk => {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
if (state.counter.value % 2 === 0) {
|
||||
return;
|
||||
}
|
||||
dispatch(increment());
|
||||
};
|
||||
};
|
||||
|
||||
export const incrementAsync = (delay = 1000): AppThunk => (dispatch) => {
|
||||
setTimeout(() => {
|
||||
dispatch(increment());
|
||||
}, delay);
|
||||
};
|
||||
|
||||
export default counterSlice.reducer;
|
||||
|
||||
export const selectCount = (state: RootState) => state.counter.value;
|
||||
20
enlightApp/app/index.tsx
Normal file
20
enlightApp/app/index.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { AppContainer as ReactHotAppContainer } from 'react-hot-loader';
|
||||
import { history, configuredStore } from './store';
|
||||
import './app.global.css';
|
||||
|
||||
const store = configuredStore();
|
||||
|
||||
const AppContainer = process.env.PLAIN_HMR ? Fragment : ReactHotAppContainer;
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// eslint-disable-next-line global-require
|
||||
const Root = require('./containers/Root').default;
|
||||
render(
|
||||
<AppContainer>
|
||||
<Root store={store} history={history} />
|
||||
</AppContainer>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
});
|
||||
138
enlightApp/app/main.dev.ts
Normal file
138
enlightApp/app/main.dev.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
/* eslint global-require: off, no-console: off */
|
||||
|
||||
/**
|
||||
* This module executes inside of electron's main process. You can start
|
||||
* electron renderer process from here and communicate with the other processes
|
||||
* through IPC.
|
||||
*
|
||||
* When running `yarn build` or `yarn build-main`, this file is compiled to
|
||||
* `./app/main.prod.js` using webpack. This gives us some performance wins.
|
||||
*/
|
||||
import 'core-js/stable';
|
||||
import 'regenerator-runtime/runtime';
|
||||
import path from 'path';
|
||||
import { app, BrowserWindow } from 'electron';
|
||||
import { autoUpdater } from 'electron-updater';
|
||||
import log from 'electron-log';
|
||||
import MenuBuilder from './menu';
|
||||
|
||||
export default class AppUpdater {
|
||||
constructor() {
|
||||
log.transports.file.level = 'info';
|
||||
autoUpdater.logger = log;
|
||||
autoUpdater.checkForUpdatesAndNotify();
|
||||
}
|
||||
}
|
||||
|
||||
let mainWindow: BrowserWindow | null = null;
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
const sourceMapSupport = require('source-map-support');
|
||||
sourceMapSupport.install();
|
||||
}
|
||||
|
||||
if (
|
||||
process.env.NODE_ENV === 'development' ||
|
||||
process.env.DEBUG_PROD === 'true'
|
||||
) {
|
||||
require('electron-debug')();
|
||||
}
|
||||
|
||||
const installExtensions = async () => {
|
||||
const installer = require('electron-devtools-installer');
|
||||
const forceDownload = !!process.env.UPGRADE_EXTENSIONS;
|
||||
const extensions = ['REACT_DEVELOPER_TOOLS', 'REDUX_DEVTOOLS'];
|
||||
|
||||
return installer
|
||||
.default(
|
||||
extensions.map((name) => installer[name]),
|
||||
forceDownload
|
||||
)
|
||||
.catch(console.log);
|
||||
};
|
||||
|
||||
const createWindow = async () => {
|
||||
if (
|
||||
process.env.NODE_ENV === 'development' ||
|
||||
process.env.DEBUG_PROD === 'true'
|
||||
) {
|
||||
await installExtensions();
|
||||
}
|
||||
|
||||
const RESOURCES_PATH = app.isPackaged
|
||||
? path.join(process.resourcesPath, 'resources')
|
||||
: path.join(__dirname, '../resources');
|
||||
|
||||
const getAssetPath = (...paths: string[]): string => {
|
||||
return path.join(RESOURCES_PATH, ...paths);
|
||||
};
|
||||
|
||||
mainWindow = new BrowserWindow({
|
||||
show: false,
|
||||
width: 1024,
|
||||
height: 728,
|
||||
icon: getAssetPath('icon.png'),
|
||||
webPreferences:
|
||||
(process.env.NODE_ENV === 'development' ||
|
||||
process.env.E2E_BUILD === 'true') &&
|
||||
process.env.ERB_SECURE !== 'true'
|
||||
? {
|
||||
nodeIntegration: true,
|
||||
}
|
||||
: {
|
||||
preload: path.join(__dirname, 'dist/renderer.prod.js'),
|
||||
},
|
||||
});
|
||||
mainWindow.setMenuBarVisibility(false);
|
||||
mainWindow.loadURL(`file://${__dirname}/app.html`);
|
||||
|
||||
// @TODO: Use 'ready-to-show' event
|
||||
// https://github.com/electron/electron/blob/master/docs/api/browser-window.md#using-ready-to-show-event
|
||||
mainWindow.webContents.on('did-finish-load', () => {
|
||||
if (!mainWindow) {
|
||||
throw new Error('"mainWindow" is not defined');
|
||||
}
|
||||
if (process.env.START_MINIMIZED) {
|
||||
mainWindow.minimize();
|
||||
} else {
|
||||
mainWindow.show();
|
||||
mainWindow.focus();
|
||||
}
|
||||
});
|
||||
|
||||
mainWindow.on('closed', () => {
|
||||
mainWindow = null;
|
||||
});
|
||||
|
||||
const menuBuilder = new MenuBuilder(mainWindow);
|
||||
menuBuilder.buildMenu();
|
||||
|
||||
// Remove this if your app does not use auto updates
|
||||
// eslint-disable-next-line
|
||||
new AppUpdater();
|
||||
};
|
||||
|
||||
/**
|
||||
* Add event listeners...
|
||||
*/
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
// Respect the OSX convention of having the application in memory even
|
||||
// after all windows have been closed
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
if (process.env.E2E_BUILD === 'true') {
|
||||
// eslint-disable-next-line promise/catch-or-return
|
||||
app.whenReady().then(createWindow);
|
||||
} else {
|
||||
app.on('ready', createWindow);
|
||||
}
|
||||
|
||||
app.on('activate', () => {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) createWindow();
|
||||
});
|
||||
1
enlightApp/app/main.prod.js.LICENSE
Normal file
1
enlightApp/app/main.prod.js.LICENSE
Normal file
@@ -0,0 +1 @@
|
||||
/*! http://mths.be/fromcodepoint v0.1.0 by @mathias */
|
||||
1
enlightApp/app/main.prod.js.LICENSE.txt
Normal file
1
enlightApp/app/main.prod.js.LICENSE.txt
Normal file
@@ -0,0 +1 @@
|
||||
/*! http://mths.be/fromcodepoint v0.1.0 by @mathias */
|
||||
290
enlightApp/app/menu.ts
Normal file
290
enlightApp/app/menu.ts
Normal file
@@ -0,0 +1,290 @@
|
||||
import {
|
||||
app,
|
||||
Menu,
|
||||
shell,
|
||||
BrowserWindow,
|
||||
MenuItemConstructorOptions,
|
||||
} from 'electron';
|
||||
|
||||
interface DarwinMenuItemConstructorOptions extends MenuItemConstructorOptions {
|
||||
selector?: string;
|
||||
submenu?: DarwinMenuItemConstructorOptions[] | Menu;
|
||||
}
|
||||
|
||||
export default class MenuBuilder {
|
||||
mainWindow: BrowserWindow;
|
||||
|
||||
constructor(mainWindow: BrowserWindow) {
|
||||
this.mainWindow = mainWindow;
|
||||
}
|
||||
|
||||
buildMenu(): Menu {
|
||||
if (
|
||||
process.env.NODE_ENV === 'development' ||
|
||||
process.env.DEBUG_PROD === 'true'
|
||||
) {
|
||||
this.setupDevelopmentEnvironment();
|
||||
}
|
||||
|
||||
const template =
|
||||
process.platform === 'darwin'
|
||||
? this.buildDarwinTemplate()
|
||||
: this.buildDefaultTemplate();
|
||||
|
||||
const menu = Menu.buildFromTemplate(template);
|
||||
Menu.setApplicationMenu(menu);
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
setupDevelopmentEnvironment(): void {
|
||||
this.mainWindow.webContents.on('context-menu', (_, props) => {
|
||||
const { x, y } = props;
|
||||
|
||||
Menu.buildFromTemplate([
|
||||
{
|
||||
label: 'Inspect element',
|
||||
click: () => {
|
||||
this.mainWindow.webContents.inspectElement(x, y);
|
||||
},
|
||||
},
|
||||
]).popup({ window: this.mainWindow });
|
||||
});
|
||||
}
|
||||
|
||||
buildDarwinTemplate(): MenuItemConstructorOptions[] {
|
||||
const subMenuAbout: DarwinMenuItemConstructorOptions = {
|
||||
label: 'Electron',
|
||||
submenu: [
|
||||
{
|
||||
label: 'About ElectronReact',
|
||||
selector: 'orderFrontStandardAboutPanel:',
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{ label: 'Services', submenu: [] },
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Hide ElectronReact',
|
||||
accelerator: 'Command+H',
|
||||
selector: 'hide:',
|
||||
},
|
||||
{
|
||||
label: 'Hide Others',
|
||||
accelerator: 'Command+Shift+H',
|
||||
selector: 'hideOtherApplications:',
|
||||
},
|
||||
{ label: 'Show All', selector: 'unhideAllApplications:' },
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Quit',
|
||||
accelerator: 'Command+Q',
|
||||
click: () => {
|
||||
app.quit();
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
const subMenuEdit: DarwinMenuItemConstructorOptions = {
|
||||
label: 'Edit',
|
||||
submenu: [
|
||||
{ label: 'Undo', accelerator: 'Command+Z', selector: 'undo:' },
|
||||
{ label: 'Redo', accelerator: 'Shift+Command+Z', selector: 'redo:' },
|
||||
{ type: 'separator' },
|
||||
{ label: 'Cut', accelerator: 'Command+X', selector: 'cut:' },
|
||||
{ label: 'Copy', accelerator: 'Command+C', selector: 'copy:' },
|
||||
{ label: 'Paste', accelerator: 'Command+V', selector: 'paste:' },
|
||||
{
|
||||
label: 'Select All',
|
||||
accelerator: 'Command+A',
|
||||
selector: 'selectAll:',
|
||||
},
|
||||
],
|
||||
};
|
||||
const subMenuViewDev: MenuItemConstructorOptions = {
|
||||
label: 'View',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Reload',
|
||||
accelerator: 'Command+R',
|
||||
click: () => {
|
||||
this.mainWindow.webContents.reload();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Toggle Full Screen',
|
||||
accelerator: 'Ctrl+Command+F',
|
||||
click: () => {
|
||||
this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Toggle Developer Tools',
|
||||
accelerator: 'Alt+Command+I',
|
||||
click: () => {
|
||||
this.mainWindow.webContents.toggleDevTools();
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
const subMenuViewProd: MenuItemConstructorOptions = {
|
||||
label: 'View',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Toggle Full Screen',
|
||||
accelerator: 'Ctrl+Command+F',
|
||||
click: () => {
|
||||
this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
const subMenuWindow: DarwinMenuItemConstructorOptions = {
|
||||
label: 'Window',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Minimize',
|
||||
accelerator: 'Command+M',
|
||||
selector: 'performMiniaturize:',
|
||||
},
|
||||
{ label: 'Close', accelerator: 'Command+W', selector: 'performClose:' },
|
||||
{ type: 'separator' },
|
||||
{ label: 'Bring All to Front', selector: 'arrangeInFront:' },
|
||||
],
|
||||
};
|
||||
const subMenuHelp: MenuItemConstructorOptions = {
|
||||
label: 'Help',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Learn More',
|
||||
click() {
|
||||
shell.openExternal('https://electronjs.org');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Documentation',
|
||||
click() {
|
||||
shell.openExternal(
|
||||
'https://github.com/electron/electron/tree/master/docs#readme'
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Community Discussions',
|
||||
click() {
|
||||
shell.openExternal('https://www.electronjs.org/community');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Search Issues',
|
||||
click() {
|
||||
shell.openExternal('https://github.com/electron/electron/issues');
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const subMenuView =
|
||||
process.env.NODE_ENV === 'development' ||
|
||||
process.env.DEBUG_PROD === 'true'
|
||||
? subMenuViewDev
|
||||
: subMenuViewProd;
|
||||
|
||||
return [subMenuAbout, subMenuEdit, subMenuView, subMenuWindow, subMenuHelp];
|
||||
}
|
||||
|
||||
buildDefaultTemplate() {
|
||||
const templateDefault = [
|
||||
{
|
||||
label: '&File',
|
||||
submenu: [
|
||||
{
|
||||
label: '&Open',
|
||||
accelerator: 'Ctrl+O',
|
||||
},
|
||||
{
|
||||
label: '&Close',
|
||||
accelerator: 'Ctrl+W',
|
||||
click: () => {
|
||||
this.mainWindow.close();
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '&View',
|
||||
submenu:
|
||||
process.env.NODE_ENV === 'development' ||
|
||||
process.env.DEBUG_PROD === 'true'
|
||||
? [
|
||||
{
|
||||
label: '&Reload',
|
||||
accelerator: 'Ctrl+R',
|
||||
click: () => {
|
||||
this.mainWindow.webContents.reload();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Toggle &Full Screen',
|
||||
accelerator: 'F11',
|
||||
click: () => {
|
||||
this.mainWindow.setFullScreen(
|
||||
!this.mainWindow.isFullScreen()
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Toggle &Developer Tools',
|
||||
accelerator: 'Alt+Ctrl+I',
|
||||
click: () => {
|
||||
this.mainWindow.webContents.toggleDevTools();
|
||||
},
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
label: 'Toggle &Full Screen',
|
||||
accelerator: 'F11',
|
||||
click: () => {
|
||||
this.mainWindow.setFullScreen(
|
||||
!this.mainWindow.isFullScreen()
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Help',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Learn More',
|
||||
click() {
|
||||
shell.openExternal('https://electronjs.org');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Documentation',
|
||||
click() {
|
||||
shell.openExternal(
|
||||
'https://github.com/electron/electron/tree/master/docs#readme'
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Community Discussions',
|
||||
click() {
|
||||
shell.openExternal('https://www.electronjs.org/community');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Search Issues',
|
||||
click() {
|
||||
shell.openExternal('https://github.com/electron/electron/issues');
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
return templateDefault;
|
||||
}
|
||||
}
|
||||
18
enlightApp/app/package.json
Normal file
18
enlightApp/app/package.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "electron-react-boilerplate",
|
||||
"productName": "electron-react-boilerplate",
|
||||
"version": "1.4.0",
|
||||
"description": "Electron application boilerplate based on React, React Router, Webpack, React Hot Loader for rapid application development",
|
||||
"main": "./main.prod.js",
|
||||
"author": {
|
||||
"name": "Electron React Boilerplate Maintainers",
|
||||
"email": "electronreactboilerplate@gmail.com",
|
||||
"url": "https://github.com/electron-react-boilerplate"
|
||||
},
|
||||
"scripts": {
|
||||
"electron-rebuild": "node -r ../internals/scripts/BabelRegister.js ../internals/scripts/ElectronRebuild.js",
|
||||
"postinstall": "yarn electron-rebuild"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {}
|
||||
}
|
||||
12
enlightApp/app/rootReducer.ts
Normal file
12
enlightApp/app/rootReducer.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { combineReducers } from 'redux';
|
||||
import { connectRouter } from 'connected-react-router';
|
||||
import { History } from 'history';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import counterReducer from './features/counter/counterSlice';
|
||||
|
||||
export default function createRootReducer(history: History) {
|
||||
return combineReducers({
|
||||
router: connectRouter(history),
|
||||
counter: counterReducer,
|
||||
});
|
||||
}
|
||||
47
enlightApp/app/store.ts
Normal file
47
enlightApp/app/store.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { configureStore, getDefaultMiddleware, Action } from '@reduxjs/toolkit';
|
||||
import { createHashHistory } from 'history';
|
||||
import { routerMiddleware } from 'connected-react-router';
|
||||
import { createLogger } from 'redux-logger';
|
||||
import { ThunkAction } from 'redux-thunk';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import createRootReducer from './rootReducer';
|
||||
|
||||
export const history = createHashHistory();
|
||||
const rootReducer = createRootReducer(history);
|
||||
export type RootState = ReturnType<typeof rootReducer>;
|
||||
|
||||
const router = routerMiddleware(history);
|
||||
const middleware = [...getDefaultMiddleware(), router];
|
||||
|
||||
const excludeLoggerEnvs = ['test', 'production'];
|
||||
const shouldIncludeLogger = !excludeLoggerEnvs.includes(
|
||||
process.env.NODE_ENV || ''
|
||||
);
|
||||
|
||||
if (shouldIncludeLogger) {
|
||||
const logger = createLogger({
|
||||
level: 'info',
|
||||
collapsed: true,
|
||||
});
|
||||
middleware.push(logger);
|
||||
}
|
||||
|
||||
export const configuredStore = (initialState?: RootState) => {
|
||||
// Create Store
|
||||
const store = configureStore({
|
||||
reducer: rootReducer,
|
||||
middleware,
|
||||
preloadedState: initialState,
|
||||
});
|
||||
|
||||
if (process.env.NODE_ENV === 'development' && module.hot) {
|
||||
module.hot.accept(
|
||||
'./rootReducer',
|
||||
// eslint-disable-next-line global-require
|
||||
() => store.replaceReducer(require('./rootReducer').default)
|
||||
);
|
||||
}
|
||||
return store;
|
||||
};
|
||||
export type Store = ReturnType<typeof configuredStore>;
|
||||
export type AppThunk = ThunkAction<void, RootState, unknown, Action<string>>;
|
||||
0
enlightApp/app/utils/.gitkeep
Normal file
0
enlightApp/app/utils/.gitkeep
Normal file
4
enlightApp/app/yarn.lock
Normal file
4
enlightApp/app/yarn.lock
Normal file
@@ -0,0 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
Reference in New Issue
Block a user