Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Builds WIP #20

Open
wants to merge 4 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 60 additions & 27 deletions src/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,39 @@ import { app, BrowserWindow, shell, ipcMain, globalShortcut } from 'electron';
import MenuBuilder from './menu';
import { resolveHtmlPath } from './util';
import { loadTevefData } from './load';
import { Prisma, PrismaClient } from '@prisma/client'
import { Prisma, PrismaClient } from '@prisma/client';
import { executeCommand } from './dirt/keyboard';
import { armFishing, stopFishingAndUnregisterHotkeys } from './services/fishing';
import {
armFishing,
stopFishingAndUnregisterHotkeys,
} from './services/fishing';
import { parseLastRun } from './lastrun';
import { createClassesService } from './services/classes';
import { createItemsService } from './services/items';
import { createSettingsService } from './services/settings';
import { createBuildsService } from './services/builds';
import { createDamageService } from './services/damage';

let mainWindow: BrowserWindow | null = null;

const DB_PATH = (process.env.NODE_ENV === 'development') ? 'file:./dev.db' : 'file:' + path.join(app.getAppPath(), "../prisma/dev.db");
const DB_PATH =
process.env.NODE_ENV === 'development'
? 'file:./dev.db'
: 'file:' + path.join(app.getAppPath(), '../prisma/dev.db');

export const prismaClient = new PrismaClient({
datasources: {
db: {
url: DB_PATH
}
}
})
url: DB_PATH,
},
},
});

const settingsService = createSettingsService(app.getPath.bind(app))
const settingsService = createSettingsService(app.getPath.bind(app));
const damageService = createDamageService(settingsService);
const classesService = createClassesService(prismaClient)
const itemsService = createItemsService(prismaClient)
const classesService = createClassesService(prismaClient);
const itemsService = createItemsService(prismaClient);
const buildsService = createBuildsService(app.getPath.bind(app));

ipcMain.on('loadData', async (event, arg) => {
const data = await loadTevefData(arg);
Expand All @@ -51,8 +59,8 @@ ipcMain.on('load', async (event, arg) => {
globalShortcut.unregister('A');
// eslint-disable-next-line no-restricted-syntax
for (const command of arg) {
// eslint-disable-next-line no-await-in-loop
await executeCommand(command);
// eslint-disable-next-line no-await-in-loop
await executeCommand(command);
}
});
});
Expand All @@ -66,35 +74,60 @@ ipcMain.on('fishing_disarm', async () => {
});

ipcMain.on('settings_read', async (event) => {
event.reply('settings_read', await settingsService.getSettings())
event.reply('settings_read', await settingsService.getSettings());
});

ipcMain.on(
'get_latest_damage_by_type',
async (event) => {
const res = await damageService.getLatestRunPerDamageType();
event.reply('get_latest_damage_by_type', res);
});
ipcMain.on('get_latest_damage_by_type', async (event) => {
const res = await damageService.getLatestRunPerDamageType();
event.reply('get_latest_damage_by_type', res);
});

ipcMain.on('get_all_classes', async (event) => {
event.reply('get_all_classes', await classesService.getAllClasses())
})
event.reply('get_all_classes', await classesService.getAllClasses());
});

ipcMain.on('get_all_items', async (event) => {
event.reply('get_all_items', await itemsService.getAllItemsDict());
})
});

ipcMain.on('request_last_run', async (event, arg) => {
const data = await parseLastRun(arg);
event.reply('last_run_info', data);
});

ipcMain.on('settings_write', async (event, arg) => {
const res = await settingsService.writeSettings(arg);
// we expect boolean on the other side, true means success.
// should change it probably
event.reply('settings_write', !res);
})
const res = await settingsService.writeSettings(arg);
// we expect boolean on the other side, true means success.
// should change it probably
event.reply('settings_write', !res);
});

ipcMain.on('get_all_builds', async (event) => {
event.reply('get_all_builds', await buildsService.getBuilds());
});

ipcMain.on('save_build', async (event, arg) => {
await buildsService.saveBuild(arg);
event.reply('get_all_builds', await buildsService.getBuilds());
});

ipcMain.on('delete_build', async (event, arg) => {
await buildsService.deleteBuild(arg);
event.reply('get_all_builds', await buildsService.getBuilds());
});

ipcMain.on('get_selected_builds', async (event) => {
event.reply('get_selected_builds', await buildsService.getSelectedBuilds());
});

ipcMain.on('save_selected_build', async (event, arg) => {
await buildsService.saveSelectedBuild(
arg.playerName,
arg.playerClass,
arg.buildId,
);
event.reply('get_selected_builds', await buildsService.getSelectedBuilds());
});

if (process.env.NODE_ENV === 'production') {
const sourceMapSupport = require('source-map-support');
Expand Down
98 changes: 98 additions & 0 deletions src/main/services/builds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import path from 'path';
import fs from 'fs/promises';

export function createBuildsService(
getPath: (type: 'userData' | 'documents') => string,
) {
const getDefaultBuilds = () => ({});
const getDefaultSelectedBuilds = () => ({});

const getBuildsFilePath = () => path.join(getPath('userData'), 'builds.json');
const getSelectedBuildsFilePath = () =>
path.join(getPath('userData'), 'selectedBuilds.json');
const getBuildIdFilePath = () =>
path.join(getPath('userData'), 'buildId.json');

const readFile = async (filePath: string, defaultValue: any) => {
try {
const data = await fs.readFile(filePath, 'utf-8');
return JSON.parse(data);
} catch (e) {
console.log(e);
return defaultValue;
}
};

const writeFile = async (filePath: string, data: any) => {
try {
await fs.writeFile(filePath, JSON.stringify(data));
} catch (e) {
console.log(e);
}
};

const readNextBuildId = async () => {
const buildIdData = await readFile(getBuildIdFilePath(), { nextId: 1 });
return buildIdData.nextId;
};

const incrementBuildId = async () => {
const buildIdData = await readFile(getBuildIdFilePath(), { nextId: 1 });
buildIdData.nextId += 1;
await writeFile(getBuildIdFilePath(), buildIdData);
};

return {
async getBuilds() {
return await readFile(getBuildsFilePath(), getDefaultBuilds());
},
async saveBuild(build: any) {
try {
const builds = await this.getBuilds();
if (!build.id) {
build.id = await readNextBuildId();
await incrementBuildId();
}
builds[build.id] = build;
await writeFile(getBuildsFilePath(), builds);
return 0;
} catch (e) {
console.log(e);
return 1;
}
},
async deleteBuild(buildId: number) {
try {
const builds = await this.getBuilds();
delete builds[buildId];
await writeFile(getBuildsFilePath(), builds);
return 0;
} catch (e) {
console.log(e);
return 1;
}
},
async getSelectedBuilds() {
return await readFile(
getSelectedBuildsFilePath(),
getDefaultSelectedBuilds(),
);
},
async saveSelectedBuild(
playerName: string,
playerClass: string,
buildId: number,
) {
try {
const selectedBuilds = (await this.getSelectedBuilds()) ?? {};
selectedBuilds[playerName] = selectedBuilds[playerName] ?? {};
selectedBuilds[playerName][playerClass] = buildId;
await writeFile(getSelectedBuildsFilePath(), selectedBuilds);
return 0;
} catch (e) {
console.log(e);
return 1;
}
},
};
}
42 changes: 28 additions & 14 deletions src/renderer/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,27 @@ import { useCharacterContext } from './contexts/characterContext';
import { ItemsPage } from './pages/ItemsPage';
import { CharacterPage } from './pages/CharacterPage';
import { LoaderPage } from './pages/LoaderPage';
import { DamagePage } from './feature/damage/DamagePage'
import { DamagePage } from './feature/damage/DamagePage';
import { ItemPage } from './pages/ItemPage';
import { FishingPage } from './pages/FishingPage';
import { LastRunInfoPage } from './pages/LastRunInfoPage';
import { useSettingsContext } from './contexts/settingsContext';
import { iconFromId } from './icons/icons';
import { BuildsPage } from './pages/BuildsPage';
import { BuildPage } from './pages/BuildPage';

export default function App() {
const { loadClasses } = useCharacterContext();
const { wc3path } = useSettingsContext();

const onRefreshClick = () => {
loadClasses()
loadClasses();
window.electron.ipcRenderer.sendMessage('request_last_run', wc3path);
window.electron.ipcRenderer.sendMessage('get_latest_damage_by_type');
}
};

return (
<Router initialEntries={[ '/characters' ]}>
<Router initialEntries={['/characters']}>
<ThemeProvider theme={theme}>
<Box sx={{ display: 'flex' }}>
<CssBaseline />
Expand Down Expand Up @@ -68,6 +70,9 @@ export default function App() {
<MenuItem component={Link} to="/items">
Items
</MenuItem>
<MenuItem component={Link} to="/builds">
Builds
</MenuItem>
<MenuItem component={Link} to="/fishing">
Fishing
</MenuItem>
Expand Down Expand Up @@ -97,16 +102,25 @@ export default function App() {
}}
>
<Box sx={{ maxWidth: '105ch' }}>
<Routes>
<Route path="/items" element={<ItemsPage/>}/>
<Route path="/item/:id" element={<ItemPage/>}/>
<Route path="/characters/:accountURL?" element={<LoaderPage />}/>
<Route path="/settings" element={<Settings />}/>
<Route path="/damagereport/:type" element={<DamagePage />}/>
<Route path="/fishing" element={<FishingPage />} />
<Route path="/lastruninfo" element={<LastRunInfoPage />}/>
<Route path="/character/:accountURL/:id" element={<CharacterPage />} />
</Routes>
<Routes>
<Route path="/items" element={<ItemsPage />} />
<Route path="/item/:id" element={<ItemPage />} />
<Route
path="/characters/:accountURL?"
element={<LoaderPage />}
/>
<Route path="/settings" element={<Settings />} />
<Route path="/damagereport/:type" element={<DamagePage />} />
<Route path="/fishing" element={<FishingPage />} />
<Route path="/lastruninfo" element={<LastRunInfoPage />} />
<Route
path="/character/:accountURL/:id"
element={<CharacterPage />}
/>
<Route path="/builds" element={<BuildsPage />} />
<Route path="/builds/new" element={<BuildPage />} />
<Route path="/builds/:id" element={<BuildPage />} />
</Routes>
</Box>
</Box>
</Box>
Expand Down
23 changes: 23 additions & 0 deletions src/renderer/components/BackButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { FC } from 'react';
import { IconButton, Typography, ButtonProps } from '@mui/material';
import { useNavigate } from 'react-router-dom';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';

interface BackButtonProps extends ButtonProps {
onClick?: () => void;
}

export const BackButton: FC<BackButtonProps> = ({ ...props }) => {
const { onClick } = props;
const navigate = useNavigate();
return (
<IconButton
{...props}
style={{ left: -10 }}
onClick={() => (onClick ? onClick() : navigate(-1))}
>
<ArrowBackIcon />
<Typography variant="caption" />
</IconButton>
);
};
29 changes: 18 additions & 11 deletions src/renderer/components/CompactItem.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FC } from 'react';
import { FC, useCallback } from 'react';
import Avatar from '@mui/material/Avatar';
import Tooltip from '@mui/material/Tooltip';
import { grey } from '@mui/material/colors';
Expand All @@ -8,14 +8,25 @@ import { useNavigate } from 'react-router-dom';
import { useItemContext } from '../contexts/itemsContext';

interface Props {
id?: string,
noTooltip?: boolean,
onClick?: () => void,
id?: string;
noTooltip?: boolean;
onClick?: () => void;
}

export const CompactItem: FC<Props> = ({ id, noTooltip, onClick }) => {
const { items } = useItemContext();
const navigate = useNavigate();
const handleClick = useCallback(
(event: React.MouseEvent) => {
event.stopPropagation();
if (onClick) {
onClick();
} else {
navigate(`/item/${id}`);
}
},
[onClick],
);
if (!id) {
return (
<Avatar src={iconFromId('EmptySlotIcon')} variant="rounded">
Expand All @@ -35,20 +46,16 @@ export const CompactItem: FC<Props> = ({ id, noTooltip, onClick }) => {
return (
<Tooltip
sx={{
boxShadow: 3
boxShadow: 3,
}}
title={
!noTooltip
? <ItemCard id={id} item={items[id]} />
: null
}
title={!noTooltip ? <ItemCard id={id} item={items[id]} /> : null}
placement="right-start"
>
<Avatar
sx={{ cursor: 'pointer' }}
variant="rounded"
src={iconFromId(items[id].icon)}
onClick={() => navigate(`/item/${id}`)}
onClick={handleClick}
/>
</Tooltip>
);
Expand Down
Loading