diff --git a/app/components/Home.tsx b/app/components/Home.tsx
index 5b7f88e..fafb58c 100644
--- a/app/components/Home.tsx
+++ b/app/components/Home.tsx
@@ -1,19 +1,14 @@
-import React from 'react';
-import { useSelector } from 'react-redux';
-import { makeStyles } from '@material-ui/core/styles';
+import React, { useEffect } from 'react';
+import { useSelector, useDispatch } from 'react-redux';
+import { ThemeProvider } from '@material-ui/core/styles';
import { Typography } from '@material-ui/core';
import Box from '@material-ui/core/Box';
+import { ipcRenderer } from 'electron';
import WorkflowManager from '../features/workflow/WorkflowManager';
-import { selectWorkflow } from '../features/workflow/workflowSlice';
+import { selectWorkflow, setTheme } from '../features/workflow/workflowSlice';
import MatchesPanel from '../features/matches/MatchesPanel';
import SplitterPanel from '../features/splitter/SplitterPanel';
-
-const useStyles = makeStyles({
- steps: {
- height: '100%',
- maxHeight: 'fill-available',
- },
-});
+import getTheme from '../utils/Theme';
function handleWorkflow(step: number) {
switch (step) {
@@ -31,15 +26,21 @@ function handleWorkflow(step: number) {
}
export default function Home(): JSX.Element {
- const { currentStep } = useSelector(selectWorkflow);
- const classes = useStyles();
+ const dispatch = useDispatch();
+ const { currentStep, theme } = useSelector(selectWorkflow);
+
+ useEffect(() => {
+ ipcRenderer.on('set-theme', (_, arg: string) => {
+ dispatch(setTheme(arg[0]));
+ });
+ }, []);
return (
- <>
+
{handleWorkflow(currentStep)}
- >
+
);
}
diff --git a/app/features/matches/matchesSlice.tsx b/app/features/matches/matchesSlice.tsx
index b605f08..299219f 100644
--- a/app/features/matches/matchesSlice.tsx
+++ b/app/features/matches/matchesSlice.tsx
@@ -31,6 +31,9 @@ export const getMatches = createAsyncThunk(
(match) => {
return compLevelSort.indexOf(match.comp_level);
},
+ (match) => {
+ return match.match_number;
+ },
]);
const matches = sortedMatches.map((tbaMatch: TbaMatch) =>
@@ -114,7 +117,7 @@ const matchesSlice = createSlice({
},
},
extraReducers: (builder) => {
- builder.addCase(getMatches.pending, (state, action) => {
+ builder.addCase(getMatches.pending, (state) => {
state.loading = true;
});
builder.addCase(getMatches.fulfilled, (state, action) => {
diff --git a/app/features/workflow/workflowSlice.tsx b/app/features/workflow/workflowSlice.tsx
index 5fc1518..b12a1e5 100644
--- a/app/features/workflow/workflowSlice.tsx
+++ b/app/features/workflow/workflowSlice.tsx
@@ -1,15 +1,17 @@
-import { createSlice } from '@reduxjs/toolkit';
+import { createSlice, PayloadAction } from '@reduxjs/toolkit';
// eslint-disable-next-line import/no-cycle
-import { AppThunk, RootState } from '../../store';
+import { RootState } from '../../store';
export type WorkflowState = {
steps: string[];
currentStep: number;
+ theme: string;
};
const initialState: WorkflowState = {
- steps: ['Matches', 'Split', 'Upload'],
+ steps: ['Matches', 'Split'],
currentStep: 0,
+ theme: 'light',
};
const workflowSlice = createSlice({
@@ -18,17 +20,20 @@ const workflowSlice = createSlice({
reducers: {
nextStep: (state) => {
if (state.currentStep < state.steps.length) {
- state.currentStep++;
+ state.currentStep += 1;
}
},
prevStep: (state) => {
if (state.currentStep > 0) {
- state.currentStep--;
+ state.currentStep -= 1;
}
},
reset: (state) => {
state.currentStep = 0;
},
+ setTheme: (state, action: PayloadAction) => {
+ state.theme = action.payload;
+ },
},
});
@@ -36,6 +41,6 @@ export const selectWorkflow = (state: RootState) => {
return state.workflow;
};
-export const { nextStep, prevStep, reset } = workflowSlice.actions;
+export const { nextStep, prevStep, reset, setTheme } = workflowSlice.actions;
export default workflowSlice.reducer;
diff --git a/app/main.dev.ts b/app/main.dev.ts
index b192a18..4992124 100644
--- a/app/main.dev.ts
+++ b/app/main.dev.ts
@@ -14,7 +14,7 @@ import path from 'path';
import { app, BrowserWindow, ipcMain } from 'electron';
import { autoUpdater } from 'electron-updater';
import log from 'electron-log';
-import MenuBuilder from './menu';
+import MenuBuilder from './menu/menu';
import { split } from './ffmpeg/ffmpegCommands';
import { SplitDetails } from './features/splitter/splitterSlice';
@@ -131,10 +131,8 @@ app.on('activate', () => {
});
ipcMain.on('split', async (event, allDetails: SplitDetails[]) => {
- for (const details of allDetails) {
- await split(event, details).catch((error) =>
- event.reply('split-error', error)
- );
- }
+ allDetails.forEach((d) =>
+ split(event, d).catch((error) => event.reply('split-error', error))
+ );
event.reply('split-end', 'Done');
});
diff --git a/app/menu.ts b/app/menu/menu.ts
similarity index 65%
rename from app/menu.ts
rename to app/menu/menu.ts
index 8b23588..ea2caf7 100644
--- a/app/menu.ts
+++ b/app/menu/menu.ts
@@ -6,30 +6,42 @@ import {
MenuItemConstructorOptions,
} from 'electron';
+import MenuTemplateBuilder from './menuTemplateBuilder';
+
interface DarwinMenuItemConstructorOptions extends MenuItemConstructorOptions {
selector?: string;
submenu?: DarwinMenuItemConstructorOptions[] | Menu;
}
+function isDev(): boolean {
+ return (
+ process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true'
+ );
+}
+
export default class MenuBuilder {
mainWindow: BrowserWindow;
+ templateBuilder: MenuTemplateBuilder;
+
constructor(mainWindow: BrowserWindow) {
this.mainWindow = mainWindow;
+ this.templateBuilder = new MenuTemplateBuilder(
+ mainWindow,
+ process.platform,
+ isDev()
+ );
}
buildMenu(): Menu {
- if (
- process.env.NODE_ENV === 'development' ||
- process.env.DEBUG_PROD === 'true'
- ) {
+ if (isDev()) {
this.setupDevelopmentEnvironment();
}
const template =
process.platform === 'darwin'
? this.buildDarwinTemplate()
- : this.buildDefaultTemplate();
+ : this.templateBuilder.getMenuOptions();
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
@@ -189,98 +201,4 @@ export default class MenuBuilder {
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://github.com/tytremblay/frc-video-splitter-3'
- );
- },
- },
- {
- label: 'Documentation',
- click() {
- shell.openExternal(
- 'https://github.com/tytremblay/frc-video-splitter-3/blob/master/README.md'
- );
- },
- },
- {
- label: 'Submit Issues',
- click() {
- shell.openExternal(
- 'https://github.com/tytremblay/frc-video-splitter-3/issues'
- );
- },
- },
- ],
- },
- ];
-
- return templateDefault;
- }
}
diff --git a/app/menu/menuTemplateBuilder.ts b/app/menu/menuTemplateBuilder.ts
new file mode 100644
index 0000000..26fc6bb
--- /dev/null
+++ b/app/menu/menuTemplateBuilder.ts
@@ -0,0 +1,127 @@
+import { shell, BrowserWindow, MenuItemConstructorOptions } from 'electron';
+
+/* eslint class-methods-use-this: ["error", { "exceptMethods": ["getHelpMenu"] }] */
+
+export default class MenuTemplateBuilder {
+ platform: string;
+
+ isDev: boolean;
+
+ mainWindow: BrowserWindow;
+
+ constructor(mainWindow: BrowserWindow, platform: string, isDev: boolean) {
+ this.platform = platform;
+ this.isDev = isDev;
+ this.mainWindow = mainWindow;
+ }
+
+ public getMenuOptions(): MenuItemConstructorOptions[] {
+ const file = this.getFileMenu();
+ const view = this.getViewMenu();
+ const help = this.getHelpMenu();
+
+ return [file, view, help];
+ }
+
+ private getFileMenu(): MenuItemConstructorOptions {
+ return {
+ label: '&File',
+ submenu: [
+ {
+ label: '&Open',
+ accelerator: 'Ctrl+O',
+ },
+ {
+ label: '&Close',
+ accelerator: 'Ctrl+W',
+ click: () => {
+ this.mainWindow.close();
+ },
+ },
+ ],
+ };
+ }
+
+ private getViewMenu(): MenuItemConstructorOptions {
+ const prodOptions: MenuItemConstructorOptions[] = [
+ {
+ label: 'Toggle &Full Screen',
+ accelerator: 'F11',
+ click: () => {
+ this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
+ },
+ },
+ {
+ label: '&Theme',
+ submenu: [
+ {
+ label: '&Light',
+ click(_, focusedWindow) {
+ focusedWindow?.webContents.send('set-theme', ['light']);
+ },
+ },
+ {
+ label: '&Dark',
+ click(_, focusedWindow) {
+ focusedWindow?.webContents.send('set-theme', ['dark']);
+ },
+ },
+ ],
+ },
+ ];
+
+ const devOptions: MenuItemConstructorOptions[] = [
+ {
+ label: '&Reload',
+ accelerator: 'Ctrl+R',
+ click: () => {
+ this.mainWindow.webContents.reload();
+ },
+ },
+ {
+ label: 'Toggle &Developer Tools',
+ accelerator: 'Alt+Ctrl+I',
+ click: () => {
+ this.mainWindow.webContents.toggleDevTools();
+ },
+ },
+ ];
+
+ return {
+ label: '&View',
+ submenu: this.isDev ? prodOptions.concat(devOptions) : prodOptions,
+ };
+ }
+
+ private getHelpMenu(): MenuItemConstructorOptions {
+ return {
+ label: 'Help',
+ submenu: [
+ {
+ label: 'Learn More',
+ click() {
+ shell.openExternal(
+ 'https://github.com/tytremblay/frc-video-splitter-3'
+ );
+ },
+ },
+ {
+ label: 'Documentation',
+ click() {
+ shell.openExternal(
+ 'https://github.com/tytremblay/frc-video-splitter-3/blob/master/README.md'
+ );
+ },
+ },
+ {
+ label: 'Submit Issues',
+ click() {
+ shell.openExternal(
+ 'https://github.com/tytremblay/frc-video-splitter-3/issues'
+ );
+ },
+ },
+ ],
+ };
+ }
+}
diff --git a/app/package.json b/app/package.json
index 279997d..da25fca 100644
--- a/app/package.json
+++ b/app/package.json
@@ -1,7 +1,7 @@
{
"name": "frc-video-splitter-3",
"productName": "FRC Video Splitter",
- "version": "0.1.0",
+ "version": "0.1.1",
"description": "Automatically split FRC match videos from large recordings.",
"main": "./main.prod.js",
"author": {
diff --git a/app/utils/Theme.tsx b/app/utils/Theme.tsx
new file mode 100644
index 0000000..e47a974
--- /dev/null
+++ b/app/utils/Theme.tsx
@@ -0,0 +1,20 @@
+import { createMuiTheme, Theme } from '@material-ui/core/styles';
+
+const darkTheme: Theme = createMuiTheme({
+ palette: {
+ type: 'dark',
+ },
+});
+
+const lightTheme: Theme = createMuiTheme();
+
+export default function getTheme(theme: string): Theme {
+ switch (theme) {
+ case 'light':
+ return lightTheme;
+ case 'dark':
+ return darkTheme;
+ default:
+ return lightTheme;
+ }
+}
diff --git a/package.json b/package.json
index 7076757..e767a28 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "frc-video-splitter-3",
"productName": "FRC Video Splitter",
- "version": "0.1.0",
+ "version": "0.1.1",
"description": "Automatically split FRC match videos from large recordings.",
"scripts": {
"build": "yarn build-main && yarn build-renderer",