mirror of
https://github.com/anidl/multi-downloader-nx.git
synced 2026-01-11 20:10:20 +00:00
Download options
This commit is contained in:
parent
71f7eb2a69
commit
df764a8620
14 changed files with 389 additions and 39 deletions
3
@types/messageHandler.d.ts
vendored
3
@types/messageHandler.d.ts
vendored
|
|
@ -5,7 +5,8 @@ import type { AvailableMuxer } from '../modules/module.args';
|
|||
export interface MessageHandler {
|
||||
auth: (data: AuthData) => Promise<AuthResponse>;
|
||||
checkToken: () => Promise<CheckTokenResponse>;
|
||||
search: (data: SearchData) => Promise<SearchResponse>
|
||||
search: (data: SearchData) => Promise<SearchResponse>,
|
||||
dubLangCodes: () => Promise<string[]>
|
||||
}
|
||||
|
||||
export type SearchResponse = ResponseBase<{
|
||||
|
|
|
|||
|
|
@ -16,4 +16,5 @@ export default () => {
|
|||
ipcMain.handle('auth', async (_, data) => handler?.auth(data));
|
||||
ipcMain.handle('checkToken', async () => handler?.checkToken());
|
||||
ipcMain.handle('search', async (_, data) => handler?.search(data));
|
||||
ipcMain.handle('dubLangCodes', async () => handler?.dubLangCodes());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,16 @@
|
|||
import { AuthData, CheckTokenResponse, MessageHandler, SearchData, SearchResponse } from "../../../../@types/messageHandler";
|
||||
import Funimation from '../../../../funi';
|
||||
import { dubLanguageCodes } from "../../../../modules/module.langsData";
|
||||
|
||||
class FunimationHandler implements MessageHandler {
|
||||
private funi: Funimation;
|
||||
constructor() {
|
||||
this.funi = new Funimation();
|
||||
}
|
||||
|
||||
public async dubLangCodes(): Promise<string[]> {
|
||||
return dubLanguageCodes;
|
||||
}
|
||||
|
||||
public async search(data: SearchData): Promise<SearchResponse> {
|
||||
const funiSearch = await this.funi.searchShow(false, data);
|
||||
|
|
|
|||
95
gui/react/package-lock.json
generated
95
gui/react/package-lock.json
generated
|
|
@ -20,6 +20,7 @@
|
|||
"@types/node": "^16.11.21",
|
||||
"@types/react": "^17.0.38",
|
||||
"@types/react-dom": "^17.0.11",
|
||||
"multi-downloader-nx": "file:../../",
|
||||
"notistack": "^2.0.3",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
|
|
@ -28,6 +29,53 @@
|
|||
"web-vitals": "^2.1.4"
|
||||
}
|
||||
},
|
||||
"../..": {
|
||||
"version": "2.0.18",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cheerio": "^1.0.0-rc.10",
|
||||
"dotenv": "^14.3.2",
|
||||
"electron-squirrel-startup": "^1.0.0",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"form-data": "^4.0.0",
|
||||
"fs-extra": "^10.0.0",
|
||||
"got": "^11.8.3",
|
||||
"hls-download": "^2.6.7",
|
||||
"iso-639": "^0.2.2",
|
||||
"lookpath": "^1.1.0",
|
||||
"m3u8-parsed": "^1.3.0",
|
||||
"sei-helper": "^3.3.0",
|
||||
"typescript-eslint": "^0.0.1-alpha.0",
|
||||
"yaml": "^1.10.0",
|
||||
"yargs": "^17.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-forge/cli": "^6.0.0-beta.61",
|
||||
"@electron-forge/maker-deb": "^6.0.0-beta.61",
|
||||
"@electron-forge/maker-rpm": "^6.0.0-beta.61",
|
||||
"@electron-forge/maker-squirrel": "^6.0.0-beta.61",
|
||||
"@electron-forge/maker-zip": "^6.0.0-beta.61",
|
||||
"@electron-forge/plugin-webpack": "^6.0.0-beta.61",
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/node": "^16.11.9",
|
||||
"@types/yargs": "^17.0.7",
|
||||
"@typescript-eslint/eslint-plugin": "^4.33.0",
|
||||
"@typescript-eslint/parser": "^4.33.0",
|
||||
"@vercel/webpack-asset-relocator-loader": "^1.7.0",
|
||||
"css-loader": "^6.5.1",
|
||||
"electron": "16.0.7",
|
||||
"eslint": "^7.30.0",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"fork-ts-checker-webpack-plugin": "^6.5.0",
|
||||
"node-loader": "^2.0.0",
|
||||
"pkg": "^5.4.1",
|
||||
"removeNPMAbsolutePaths": "^2.0.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"ts-loader": "^9.2.6",
|
||||
"ts-node": "^10.4.0",
|
||||
"typescript": "^4.5.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.16.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz",
|
||||
|
|
@ -11807,6 +11855,10 @@
|
|||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/multi-downloader-nx": {
|
||||
"resolved": "../..",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/multicast-dns": {
|
||||
"version": "6.2.3",
|
||||
"resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz",
|
||||
|
|
@ -25460,6 +25512,49 @@
|
|||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"multi-downloader-nx": {
|
||||
"version": "file:../..",
|
||||
"requires": {
|
||||
"@electron-forge/cli": "^6.0.0-beta.61",
|
||||
"@electron-forge/maker-deb": "^6.0.0-beta.61",
|
||||
"@electron-forge/maker-rpm": "^6.0.0-beta.61",
|
||||
"@electron-forge/maker-squirrel": "^6.0.0-beta.61",
|
||||
"@electron-forge/maker-zip": "^6.0.0-beta.61",
|
||||
"@electron-forge/plugin-webpack": "^6.0.0-beta.61",
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/node": "^16.11.9",
|
||||
"@types/yargs": "^17.0.7",
|
||||
"@typescript-eslint/eslint-plugin": "^4.33.0",
|
||||
"@typescript-eslint/parser": "^4.33.0",
|
||||
"@vercel/webpack-asset-relocator-loader": "^1.7.0",
|
||||
"cheerio": "^1.0.0-rc.10",
|
||||
"css-loader": "^6.5.1",
|
||||
"dotenv": "^14.3.2",
|
||||
"electron": "16.0.7",
|
||||
"electron-squirrel-startup": "^1.0.0",
|
||||
"eslint": "^7.30.0",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"fork-ts-checker-webpack-plugin": "^6.5.0",
|
||||
"form-data": "^4.0.0",
|
||||
"fs-extra": "^10.0.0",
|
||||
"got": "^11.8.3",
|
||||
"hls-download": "^2.6.7",
|
||||
"iso-639": "^0.2.2",
|
||||
"lookpath": "^1.1.0",
|
||||
"m3u8-parsed": "^1.3.0",
|
||||
"node-loader": "^2.0.0",
|
||||
"pkg": "^5.4.1",
|
||||
"removeNPMAbsolutePaths": "^2.0.0",
|
||||
"sei-helper": "^3.3.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"ts-loader": "^9.2.6",
|
||||
"ts-node": "^10.4.0",
|
||||
"typescript": "^4.5.5",
|
||||
"typescript-eslint": "^0.0.1-alpha.0",
|
||||
"yaml": "^1.10.0",
|
||||
"yargs": "^17.2.1"
|
||||
}
|
||||
},
|
||||
"multicast-dns": {
|
||||
"version": "6.2.3",
|
||||
"resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
import React from "react";
|
||||
import { Box, Button, Checkbox, Chip, FormControl, FormControlLabel, IconButton, InputLabel, MenuItem, OutlinedInput, Select, TextField } from "@mui/material";
|
||||
import useStore from "../../../hooks/useStore";
|
||||
import MultiSelect from "../../MultiSelect";
|
||||
import { messageChannelContext } from "../../../provider/MessageChannel";
|
||||
import { Check, Close } from "@mui/icons-material";
|
||||
|
||||
const DownloadSelector: React.FC = () => {
|
||||
const messageHandler = React.useContext(messageChannelContext);
|
||||
const [store, dispatch] = useStore();
|
||||
const [dubLangCodes, setDubLangCodes] = React.useState<string[]>([]);
|
||||
|
||||
React.useEffect(() => {
|
||||
(async () => {
|
||||
const codes = await messageHandler?.dubLangCodes();
|
||||
setDubLangCodes(codes ?? []);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
return <Box sx={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<Box sx={{ m: 2, gap: 1, display: 'flex', justifyContent: 'center', alignItems: 'center', flexWrap: 'wrap' }}>
|
||||
<TextField value={store.downloadOptions.id} required onChange={e => {
|
||||
dispatch({
|
||||
type: 'downloadOptions',
|
||||
payload: { ...store.downloadOptions, id: e.target.value }
|
||||
})
|
||||
}} label='Item ID' />
|
||||
<TextField type='number' value={store.downloadOptions.q} required onChange={e => {
|
||||
const parsed = parseInt(e.target.value);
|
||||
if (isNaN(parsed) || parsed < 0 || parsed > 10)
|
||||
return;
|
||||
dispatch({
|
||||
type: 'downloadOptions',
|
||||
payload: { ...store.downloadOptions, q: parsed }
|
||||
})
|
||||
}} label='Quality Level (0 for max)' />
|
||||
<TextField disabled={store.downloadOptions.all} value={store.downloadOptions.e} required onChange={e => {
|
||||
dispatch({
|
||||
type: 'downloadOptions',
|
||||
payload: { ...store.downloadOptions, e: e.target.value }
|
||||
})
|
||||
}} label='Episode Select' />
|
||||
<MultiSelect
|
||||
title='Dub Languages'
|
||||
values={dubLangCodes}
|
||||
selected={store.downloadOptions.dubLang}
|
||||
onChange={(e) => {
|
||||
dispatch({
|
||||
type: 'downloadOptions',
|
||||
payload: { ...store.downloadOptions, dubLang: e }
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<TextField value={store.downloadOptions.fileName} onChange={e => {
|
||||
dispatch({
|
||||
type: 'downloadOptions',
|
||||
payload: { ...store.downloadOptions, fileName: e.target.value }
|
||||
})
|
||||
}} sx={{ width: '50%' }} label='Filename' />
|
||||
<Button onClick={() => dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, all: !store.downloadOptions.all } })} variant={store.downloadOptions.all ? 'contained' : 'outlined'}>Download all</Button>
|
||||
<Button onClick={() => dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, but: !store.downloadOptions.but } })} variant={store.downloadOptions.but ? 'contained' : 'outlined'}>Download all but</Button>
|
||||
</Box>
|
||||
<Box sx={{ flex: 0, m: 1, mb: 3, display: 'flex', justifyContent: 'center' }}>
|
||||
<Button variant='contained'>Add to Queue</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
};
|
||||
|
||||
export default DownloadSelector;
|
||||
|
|
@ -1,12 +1,14 @@
|
|||
import { Box, Divider } from "@mui/material";
|
||||
import React from "react";
|
||||
import DownloadSelector from "./DownloadSelector/DownloadSelector";
|
||||
import './MainFrame.css';
|
||||
import SearchBox from "./SearchBox";
|
||||
import SearchBox from "./SearchBox/SearchBox";
|
||||
|
||||
const MainFrame: React.FC = () => {
|
||||
return <Box sx={{ border: '2px solid white', width: '75%' }}>
|
||||
<SearchBox />
|
||||
<Divider className="divider-width" light sx={{ color: 'text.primary'}}>Text</Divider>
|
||||
<Divider variant='middle' className="divider-width" light sx={{ color: 'text.primary', fontSize: '1.2rem' }}>Options</Divider>
|
||||
<DownloadSelector />
|
||||
</Box>
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,29 @@
|
|||
import { Image } from "@mui/icons-material";
|
||||
import { Box, Button, Divider, List, ListItem, ListItemText, Paper, Popover, TextField, Typography } from "@mui/material";
|
||||
import React from "react";
|
||||
import { SearchResponse } from "../../../../../@types/messageHandler";
|
||||
import { messageChannelContext } from "../../provider/MessageChannel";
|
||||
import Require from "../Require";
|
||||
import { Box, Divider, List, ListItem, Paper, TextField, Typography } from "@mui/material";
|
||||
import { SearchResponse } from "../../../../../../@types/messageHandler";
|
||||
import useStore from "../../../hooks/useStore";
|
||||
import { messageChannelContext } from "../../../provider/MessageChannel";
|
||||
import './SearchBox.css';
|
||||
|
||||
const SearchBox: React.FC = () => {
|
||||
const messageHandler = React.useContext(messageChannelContext);
|
||||
|
||||
const [store, dispatch] = useStore();
|
||||
const [search, setSearch] = React.useState('');
|
||||
|
||||
const [focus, setFocus] = React.useState(false);
|
||||
|
||||
const [searchResult, setSearchResult] = React.useState<undefined|SearchResponse>();
|
||||
const anchor = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
const selectItem = (id: string) => {
|
||||
window.alert(id); //TODO change
|
||||
console.log(id);
|
||||
dispatch({
|
||||
type: 'downloadOptions',
|
||||
payload: {
|
||||
...store.downloadOptions,
|
||||
id
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
|
|
@ -31,9 +39,10 @@ const SearchBox: React.FC = () => {
|
|||
|
||||
const anchorBounding = anchor.current?.getBoundingClientRect();
|
||||
|
||||
return <Box sx={{ mt: 2, mb: 2 }}>
|
||||
<TextField ref={anchor} value={search} onChange={e => setSearch(e.target.value)} variant='outlined' label='Search' sx={{ width: 'calc(100% - 50px)', left: 25 }} />
|
||||
{searchResult !== undefined && searchResult.isOk && searchResult.value.length > 0 &&
|
||||
|
||||
return <Box onBlurCapture={() => /* Delay to capture onclick */setTimeout(() => setFocus(false), 100) } onFocusCapture={() => setFocus(true)} sx={{ m: 2 }}>
|
||||
<TextField ref={anchor} value={search} onChange={e => setSearch(e.target.value)} variant='outlined' label='Search' fullWidth />
|
||||
{searchResult !== undefined && searchResult.isOk && searchResult.value.length > 0 && focus &&
|
||||
<Paper sx={{ position: 'absolute', maxHeight: '50%', width: `${anchorBounding?.width}px`,
|
||||
left: anchorBounding?.x, top: (anchorBounding?.y ?? 0) + (anchorBounding?.height ?? 0), zIndex: 99, overflowY: 'scroll'}}>
|
||||
<List>
|
||||
67
gui/react/src/components/MultiSelect.tsx
Normal file
67
gui/react/src/components/MultiSelect.tsx
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import React from "react";
|
||||
import { Box, Chip, FormControl, InputLabel, MenuItem, OutlinedInput, Select, Theme, useTheme } from "@mui/material";
|
||||
|
||||
export type MultiSelectProps = {
|
||||
values: string[],
|
||||
selected: string[],
|
||||
onChange: (values: string[]) => unknown,
|
||||
title: string
|
||||
}
|
||||
|
||||
const ITEM_HEIGHT = 48;
|
||||
const ITEM_PADDING_TOP = 8;
|
||||
const MenuProps = {
|
||||
PaperProps: {
|
||||
style: {
|
||||
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
|
||||
width: 250
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function getStyles(name: string, personName: readonly string[], theme: Theme) {
|
||||
return {
|
||||
fontWeight:
|
||||
personName.indexOf(name) === -1
|
||||
? theme.typography.fontWeightRegular
|
||||
: theme.typography.fontWeightMedium
|
||||
};
|
||||
}
|
||||
|
||||
const MultiSelect: React.FC<MultiSelectProps> = (props) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return <div>
|
||||
<FormControl sx={{ m: 1, width: 300 }}>
|
||||
<InputLabel id="multi-select-label">{props.title}</InputLabel>
|
||||
<Select
|
||||
labelId="multi-select-label"
|
||||
id="multi-select"
|
||||
multiple
|
||||
value={props.selected}
|
||||
onChange={e => props.onChange(typeof e.target.value === "string" ? e.target.value.split(",") : e.target.value)}
|
||||
input={<OutlinedInput id="select-multiple-chip" label={props.title} />}
|
||||
renderValue={(selected) => (
|
||||
<Box sx={{ display: "flex", flexWrap: "wrap", gap: 0.5 }}>
|
||||
{selected.map((value) => (
|
||||
<Chip key={value} label={value} />
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
MenuProps={MenuProps}
|
||||
>
|
||||
{props.values.map((name) => (
|
||||
<MenuItem
|
||||
key={name}
|
||||
value={name}
|
||||
style={getStyles(name, props.selected, theme)}
|
||||
>
|
||||
{name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</div>
|
||||
}
|
||||
|
||||
export default MultiSelect;
|
||||
12
gui/react/src/hooks/useStore.tsx
Normal file
12
gui/react/src/hooks/useStore.tsx
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import React from "react";
|
||||
import { StoreAction, StoreContext, StoreState } from "../provider/Store";
|
||||
|
||||
const useStore = () => {
|
||||
const context = React.useContext(StoreContext as unknown as React.Context<[StoreState, React.Dispatch<StoreAction<keyof StoreState>>]>);
|
||||
if (!context) {
|
||||
throw new Error('useStore must be used under Store');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
export default useStore;
|
||||
|
|
@ -7,6 +7,7 @@ import MessageChannel from './provider/MessageChannel';
|
|||
import { IconButton } from "@mui/material";
|
||||
import { CloseOutlined } from "@mui/icons-material";
|
||||
import { SnackbarProvider, SnackbarKey } from 'notistack';
|
||||
import Store from './provider/Store';
|
||||
|
||||
const notistackRef = React.createRef<SnackbarProvider>();
|
||||
const onClickDismiss = (key: SnackbarKey | undefined) => () => {
|
||||
|
|
@ -16,22 +17,24 @@ const onClickDismiss = (key: SnackbarKey | undefined) => () => {
|
|||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<SnackbarProvider
|
||||
ref={notistackRef}
|
||||
action={(key) => (
|
||||
<IconButton onClick={onClickDismiss(key)} color="inherit">
|
||||
<CloseOutlined />
|
||||
</IconButton>
|
||||
)}
|
||||
>
|
||||
<Style>
|
||||
<ServiceProvider>
|
||||
<MessageChannel>
|
||||
<App />
|
||||
</MessageChannel>
|
||||
</ServiceProvider>
|
||||
</Style>
|
||||
</SnackbarProvider>
|
||||
<Store>
|
||||
<SnackbarProvider
|
||||
ref={notistackRef}
|
||||
action={(key) => (
|
||||
<IconButton onClick={onClickDismiss(key)} color="inherit">
|
||||
<CloseOutlined />
|
||||
</IconButton>
|
||||
)}
|
||||
>
|
||||
<Style>
|
||||
<ServiceProvider>
|
||||
<MessageChannel>
|
||||
<App />
|
||||
</MessageChannel>
|
||||
</ServiceProvider>
|
||||
</Style>
|
||||
</SnackbarProvider>
|
||||
</Store>
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
|
|
@ -18,7 +18,8 @@ const MessageChannelProvider: React.FC = ({ children }) => {
|
|||
const messageHandler: MessageHandler = {
|
||||
auth: async (data) => await ipcRenderer.invoke('auth', data),
|
||||
checkToken: async () => await ipcRenderer.invoke('checkToken'),
|
||||
search: async (data) => await ipcRenderer.invoke('search', data)
|
||||
search: async (data) => await ipcRenderer.invoke('search', data),
|
||||
dubLangCodes: async () => await ipcRenderer.invoke('dubLangCodes')
|
||||
}
|
||||
|
||||
return <messageChannelContext.Provider value={messageHandler}>
|
||||
|
|
|
|||
81
gui/react/src/provider/Store.tsx
Normal file
81
gui/react/src/provider/Store.tsx
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
import React from 'react';
|
||||
import { dubLanguageCodes } from '../../../../modules/module.langsData';
|
||||
|
||||
export type QueueItem = {
|
||||
|
||||
}
|
||||
|
||||
export type DownloadOptions = {
|
||||
q: number,
|
||||
id: string,
|
||||
e: string,
|
||||
dubLang: typeof dubLanguageCodes,
|
||||
fileName: string,
|
||||
all: boolean,
|
||||
but: boolean
|
||||
}
|
||||
|
||||
export type StoreState = {
|
||||
queue: QueueItem[],
|
||||
downloadOptions: DownloadOptions
|
||||
}
|
||||
|
||||
export type StoreAction<T extends keyof StoreState> = {
|
||||
type: T,
|
||||
payload: StoreState[T]
|
||||
}
|
||||
|
||||
const Reducer = <T extends keyof StoreState,>(state: StoreState, action: StoreAction<T>): StoreState => {
|
||||
switch(action.type) {
|
||||
case "queue":
|
||||
return { ...state, queue: state.queue.concat(action.payload) };
|
||||
case "downloadOptions":
|
||||
return { ...state, downloadOptions: action.payload as DownloadOptions };
|
||||
}
|
||||
return state;
|
||||
};
|
||||
|
||||
const initialState: StoreState = {
|
||||
queue: [],
|
||||
downloadOptions: {
|
||||
id: '',
|
||||
q: 0,
|
||||
e: '',
|
||||
dubLang: [ 'jpn' ],
|
||||
fileName: '[${service}] ${showTitle} - S${season}E${episode} [${height}p]',
|
||||
all: false,
|
||||
but: false
|
||||
}
|
||||
};
|
||||
|
||||
const Store: React.FC = ({children}) => {
|
||||
const [state, dispatch] = React.useReducer(Reducer, initialState);
|
||||
/*React.useEffect(() => {
|
||||
if (!state.unsavedChanges.has)
|
||||
return;
|
||||
const unsavedChanges = (ev: BeforeUnloadEvent, lang: LanguageContextType) => {
|
||||
ev.preventDefault();
|
||||
ev.returnValue = lang.getLang('unsaved_changes');
|
||||
return lang.getLang('unsaved_changes');
|
||||
};
|
||||
|
||||
|
||||
const windowListener = (ev: BeforeUnloadEvent) => {
|
||||
return unsavedChanges(ev, state.lang);
|
||||
};
|
||||
|
||||
window.addEventListener('beforeunload', windowListener);
|
||||
|
||||
return () => window.removeEventListener('beforeunload', windowListener);
|
||||
}, [state.unsavedChanges.has]);*/
|
||||
|
||||
return (
|
||||
<StoreContext.Provider value={[state, dispatch]}>
|
||||
{children}
|
||||
</StoreContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
/* Importent Notice -- The 'queue' generic will be overriden */
|
||||
export const StoreContext = React.createContext<[StoreState, React.Dispatch<StoreAction<'queue'>>]>([initialState, undefined as any]);
|
||||
export default Store;
|
||||
22
tsc.ts
22
tsc.ts
|
|
@ -6,7 +6,9 @@ import { removeSync, copyFileSync } from 'fs-extra';
|
|||
const argv = process.argv.slice(2);
|
||||
let buildIgnore: string[] = [];
|
||||
|
||||
if (argv.length > 0 && argv[0] !== 'test')
|
||||
const isTest = !(argv.length > 0 && argv[0] !== 'test');
|
||||
|
||||
if (!isTest)
|
||||
buildIgnore = [
|
||||
'*/\\.env'
|
||||
];
|
||||
|
|
@ -47,15 +49,17 @@ export { ignore };
|
|||
const tsc = exec('npx tsc');
|
||||
|
||||
await waitForProcess(tsc);
|
||||
|
||||
process.stdout.write('✓\nBuilding react... ');
|
||||
const react = exec('npm run build', {
|
||||
cwd: path.join(__dirname, 'gui', 'react'),
|
||||
});
|
||||
|
||||
await waitForProcess(react);
|
||||
|
||||
if (!isTest) {
|
||||
process.stdout.write('✓\nBuilding react... ');
|
||||
const react = exec('npm run build', {
|
||||
cwd: path.join(__dirname, 'gui', 'react'),
|
||||
});
|
||||
|
||||
await waitForProcess(react);
|
||||
}
|
||||
|
||||
process.stdout.write('✓\nCopying files... ');
|
||||
|
||||
copyDir(path.join(__dirname, 'gui', 'react', 'build'), path.join(__dirname, 'lib', 'gui', 'electron', 'build'));
|
||||
|
||||
const files = readDir(__dirname);
|
||||
|
|
|
|||
Loading…
Reference in a new issue