├── src ├── renderer │ ├── interfaces │ │ ├── misc.tsx │ │ ├── mainMessages.tsx │ │ ├── overview.tsx │ │ ├── store │ │ │ └── authReducerActionsInterfaces.tsx │ │ ├── filters.tsx │ │ └── items.tsx │ ├── views │ │ ├── login │ │ │ ├── types │ │ │ │ └── LoginMethod.ts │ │ │ ├── login.tsx │ │ │ ├── components │ │ │ │ └── LoginTabs.tsx │ │ │ └── HandleSuccess.tsx │ │ ├── overview │ │ │ ├── categoryDistribution │ │ │ │ └── categoriesRGB.tsx │ │ │ ├── rightGraph.tsx │ │ │ ├── leftGraph.tsx │ │ │ ├── EmptyField.tsx │ │ │ ├── overview.tsx │ │ │ ├── barChatMajors.tsx │ │ │ ├── barChart.tsx │ │ │ ├── sidebar │ │ │ │ └── sideBar.tsx │ │ │ ├── filter │ │ │ │ └── optionsDropdown.tsx │ │ │ ├── charts │ │ │ │ └── pieChart.tsx │ │ │ ├── radarChart copy.tsx │ │ │ └── overviewOptionsDropdown.tsx │ │ └── tradeUp │ │ │ ├── sidebar │ │ │ └── sideBar.tsx │ │ │ └── filter │ │ │ └── optionsDropdown.tsx │ ├── functionsClasses │ │ ├── createCSGOImage.tsx │ │ ├── reducerManager.tsx │ │ ├── filters │ │ │ ├── search.tsx │ │ │ └── custom.tsx │ │ ├── storageUnits │ │ │ ├── storageUnitsFunctions.tsx │ │ │ └── storageUnitsClass.tsx │ │ ├── downloadReport.tsx │ │ ├── rendererCommands │ │ │ └── admin.tsx │ │ └── prices.tsx │ ├── components │ │ └── content │ │ │ ├── shared │ │ │ ├── iconsLogo │ │ │ │ ├── minimize.tsx │ │ │ │ ├── restore.tsx │ │ │ │ ├── close.tsx │ │ │ │ ├── maximize.tsx │ │ │ │ ├── logo.tsx │ │ │ │ └── logo 2.tsx │ │ │ ├── animations.tsx │ │ │ ├── filters │ │ │ │ ├── transferAmount.tsx │ │ │ │ ├── inventoryAmount.tsx │ │ │ │ ├── accountAmount.tsx │ │ │ │ └── pricingAmount.tsx │ │ │ ├── rarities.tsx │ │ │ ├── emptyState.tsx │ │ │ ├── titleBarWindows.tsx │ │ │ ├── steamLogo.tsx │ │ │ ├── categories.tsx │ │ │ └── modals & notifcations │ │ │ │ ├── notification.tsx │ │ │ │ └── modalTradeResult.tsx │ │ │ ├── Inventory │ │ │ ├── inventoryRows │ │ │ │ ├── QTYRow.tsx │ │ │ │ ├── rarityRow.tsx │ │ │ │ ├── storageRow.tsx │ │ │ │ ├── tradeholdRow.tsx │ │ │ │ ├── floatRow.tsx │ │ │ │ ├── collectionsRow.tsx │ │ │ │ ├── functions.tsx │ │ │ │ ├── moveableRow.tsx │ │ │ │ ├── inventoryLinkRow.tsx │ │ │ │ ├── priceRow.tsx │ │ │ │ └── stickerPatchesRow.tsx │ │ │ ├── inventoryFilterSetup.tsx │ │ │ └── inventory.tsx │ │ │ ├── storageUnits │ │ │ ├── to │ │ │ │ └── toFilterSetup.tsx │ │ │ └── from │ │ │ │ ├── fromFilterSetup.tsx │ │ │ │ └── Content.tsx │ │ │ └── loadStorageUnitsButton.tsx │ ├── variables │ │ └── overviewOptions.tsx │ ├── context │ │ └── toMoveContext.tsx │ ├── store │ │ ├── helpers │ │ │ ├── globaloffensiveNotifications.tsx │ │ │ └── userStatusHelper.tsx │ │ ├── actions │ │ │ ├── modalTrade.tsx │ │ │ ├── userStatsActions.tsx │ │ │ ├── pricingActions.tsx │ │ │ ├── tradeUpActions.tsx │ │ │ ├── modalMove actions.tsx │ │ │ ├── settings.tsx │ │ │ ├── moveToActions.tsx │ │ │ ├── moveFromActions.tsx │ │ │ └── filtersInventoryActions.tsx │ │ ├── configureStore.tsx │ │ ├── inventory │ │ │ ├── inventoryInterfaces.tsx │ │ │ ├── inventoryActions.tsx │ │ │ ├── inventoryReducer.tsx │ │ │ └── inventoryClass.tsx │ │ ├── reducer │ │ │ ├── modalRename.tsx │ │ │ ├── index.tsx │ │ │ ├── modalTrade.tsx │ │ │ ├── userStatus.tsx │ │ │ ├── pricing.tsx │ │ │ ├── settings.tsx │ │ │ ├── modalMove.tsx │ │ │ ├── tradeupReducer.tsx │ │ │ ├── moveFromReducers.tsx │ │ │ └── moveToReducers.tsx │ │ └── handleMessage.tsx │ ├── index.tsx │ └── index.ejs ├── shared │ └── Interfaces.tsx │ │ ├── IPCReturn.tsx │ │ ├── login.tsx │ │ └── store.tsx ├── emitters.ts ├── main │ ├── helpers │ │ ├── eventEmitter.ts │ │ ├── classes │ │ │ ├── steam │ │ │ │ ├── inventoryManager.tsx │ │ │ │ ├── items │ │ │ │ │ ├── steam.js │ │ │ │ │ └── getCommands.js │ │ │ │ ├── currency.tsx │ │ │ │ └── backup │ │ │ │ │ └── currency.json │ │ │ └── IPCGenerators │ │ │ │ └── loginGenerator.tsx │ │ └── login │ │ │ ├── loginRegular.tsx │ │ │ └── flowLoginRegularQR.tsx │ ├── interfaces │ │ └── mainInterfaces.tsx │ ├── util.ts │ └── scripts │ │ └── versionHelper.tsx └── __tests__ │ └── App.test.tsx ├── .erb ├── mocks │ └── fileMock.js ├── img │ ├── erb-banner.png │ └── erb-logo.png ├── configs │ ├── .eslintrc │ ├── webpack.config.eslint.ts │ ├── postcss.config.js │ ├── webpack.config.base.ts │ ├── webpack.paths.ts │ ├── webpack.config.renderer.dev.dll.ts │ └── webpack.config.main.prod.ts └── scripts │ ├── .eslintrc │ ├── link-modules.js │ ├── delete-source-maps.js │ ├── check-node-env.js │ ├── clean.js │ ├── check-port-in-use.js │ ├── electron-rebuild.js │ ├── check-build-exists.ts │ ├── notarize.js │ └── check-native-dep.js ├── .erb.zip ├── assets ├── icon.ico ├── icon.png ├── icon.icns ├── casemove.png ├── casemove-preview.png ├── assets.d.ts ├── entitlements.mac.plist └── icon.svg ├── .editorconfig ├── .gitattributes ├── .eslintignore ├── tailwind.config.js ├── sign.js ├── release └── app │ └── package.json ├── tsconfig.json ├── .gitignore └── .eslintrc.js /src/renderer/interfaces/misc.tsx: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.erb/mocks/fileMock.js: -------------------------------------------------------------------------------- 1 | export default 'test-file-stub'; 2 | -------------------------------------------------------------------------------- /.erb.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udoless/casemove/main/.erb.zip -------------------------------------------------------------------------------- /assets/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udoless/casemove/main/assets/icon.ico -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udoless/casemove/main/assets/icon.png -------------------------------------------------------------------------------- /assets/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udoless/casemove/main/assets/icon.icns -------------------------------------------------------------------------------- /assets/casemove.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udoless/casemove/main/assets/casemove.png -------------------------------------------------------------------------------- /.erb/img/erb-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udoless/casemove/main/.erb/img/erb-banner.png -------------------------------------------------------------------------------- /.erb/img/erb-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udoless/casemove/main/.erb/img/erb-logo.png -------------------------------------------------------------------------------- /assets/casemove-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udoless/casemove/main/assets/casemove-preview.png -------------------------------------------------------------------------------- /src/renderer/views/login/types/LoginMethod.ts: -------------------------------------------------------------------------------- 1 | export type LoginMethod = 'QR' | 'WEBTOKEN' | 'REGULAR' 2 | -------------------------------------------------------------------------------- /src/shared/Interfaces.tsx/IPCReturn.tsx: -------------------------------------------------------------------------------- 1 | export interface CurrencyReturnValue { 2 | currency: string 3 | rate: number 4 | } 5 | -------------------------------------------------------------------------------- /src/renderer/functionsClasses/createCSGOImage.tsx: -------------------------------------------------------------------------------- 1 | export function createCSGOImage ( urlEndpath : string ) : string { 2 | return urlEndpath 3 | } 4 | -------------------------------------------------------------------------------- /.erb/configs/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-console": "off", 4 | "global-require": "off", 5 | "import/no-dynamic-require": "off" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/emitters.ts: -------------------------------------------------------------------------------- 1 | import EventEmitter from "events"; 2 | 3 | class MyEmitter extends EventEmitter {} 4 | const emitterAccount = new MyEmitter(); 5 | 6 | export {emitterAccount} 7 | -------------------------------------------------------------------------------- /.erb/configs/webpack.config.eslint.ts: -------------------------------------------------------------------------------- 1 | /* eslint import/no-unresolved: off, import/no-self-import: off */ 2 | 3 | module.exports = require('./webpack.config.renderer.dev').default; 4 | -------------------------------------------------------------------------------- /src/main/helpers/eventEmitter.ts: -------------------------------------------------------------------------------- 1 | import EventEmitter from "events"; 2 | 3 | class MyEmitter extends EventEmitter {} 4 | const emitterAccount = new MyEmitter(); 5 | 6 | export {emitterAccount} 7 | -------------------------------------------------------------------------------- /.erb/configs/postcss.config.js: -------------------------------------------------------------------------------- 1 | const tailwindcss = require('tailwindcss'); 2 | const autoprefixer = require('autoprefixer'); 3 | 4 | module.exports = { 5 | plugins: [tailwindcss, autoprefixer], 6 | }; 7 | -------------------------------------------------------------------------------- /.erb/scripts/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-console": "off", 4 | "global-require": "off", 5 | "import/no-dynamic-require": "off", 6 | "import/no-extraneous-dependencies": "off" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | *.exe binary 3 | *.png binary 4 | *.jpg binary 5 | *.jpeg binary 6 | *.ico binary 7 | *.icns binary 8 | *.eot binary 9 | *.otf binary 10 | *.ttf binary 11 | *.woff binary 12 | *.woff2 binary 13 | -------------------------------------------------------------------------------- /src/__tests__/App.test.tsx: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom'; 2 | import { render } from '@testing-library/react'; 3 | import App from '../renderer/App'; 4 | 5 | describe('App', () => { 6 | it('should render', () => { 7 | expect(render()).toBeTruthy(); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /src/renderer/interfaces/mainMessages.tsx: -------------------------------------------------------------------------------- 1 | import { ItemRow } from "./items" 2 | 3 | export interface MessageMain { 4 | command: number 5 | text: string 6 | returnValue: any 7 | } 8 | 9 | export interface MessageItems extends MessageMain { 10 | returnValue: ItemRow 11 | } 12 | 13 | -------------------------------------------------------------------------------- /src/renderer/interfaces/overview.tsx: -------------------------------------------------------------------------------- 1 | export interface OverviewOptionsBy { 2 | price: string 3 | volume: string 4 | 5 | } 6 | 7 | export interface OverviewOptionsLeftCharts { 8 | overall: string 9 | } 10 | export interface OverviewOptionsRightCharts { 11 | itemDistribution: string 12 | } -------------------------------------------------------------------------------- /src/main/interfaces/mainInterfaces.tsx: -------------------------------------------------------------------------------- 1 | 2 | // Need-update 3 | export interface GithubResponse { 4 | version: number 5 | downloadLink: string 6 | } 7 | export interface UpdateReply { 8 | requireUpdate: boolean 9 | currentVersion: number 10 | githubResponse: GithubResponse 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /src/renderer/interfaces/store/authReducerActionsInterfaces.tsx: -------------------------------------------------------------------------------- 1 | import { WalletInterface } from "../states"; 2 | 3 | export interface SignInActionPackage { 4 | displayName: string 5 | CSGOConnection: boolean 6 | userProfilePicture: string 7 | steamID: string 8 | wallet: WalletInterface 9 | } 10 | -------------------------------------------------------------------------------- /.erb/scripts/link-modules.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import { 3 | appNodeModulesPath, 4 | srcNodeModulesPath, 5 | } from '../configs/webpack.paths'; 6 | 7 | if (!fs.existsSync(srcNodeModulesPath) && fs.existsSync(appNodeModulesPath)) { 8 | fs.symlinkSync(appNodeModulesPath, srcNodeModulesPath, 'junction'); 9 | } 10 | -------------------------------------------------------------------------------- /.erb/scripts/delete-source-maps.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import rimraf from 'rimraf'; 3 | import webpackPaths from '../configs/webpack.paths'; 4 | 5 | export default function deleteSourceMaps() { 6 | rimraf.sync(path.join(webpackPaths.distMainPath, '*.js.map')); 7 | rimraf.sync(path.join(webpackPaths.distRendererPath, '*.js.map')); 8 | } 9 | -------------------------------------------------------------------------------- /assets/assets.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint @typescript-eslint/no-explicit-any: off */ 2 | 3 | declare module '*.svg' { 4 | const content: string; 5 | export default content; 6 | } 7 | 8 | declare module '*.png' { 9 | const content: string; 10 | export default content; 11 | } 12 | 13 | declare module '*.jpg' { 14 | const content: string; 15 | export default content; 16 | } 17 | -------------------------------------------------------------------------------- /assets/entitlements.mac.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.allow-unsigned-executable-memory 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/renderer/components/content/shared/iconsLogo/minimize.tsx: -------------------------------------------------------------------------------- 1 | const TitleBarMinimize = (props) => ( 2 | 10 | 11 | 12 | ) 13 | 14 | export default TitleBarMinimize -------------------------------------------------------------------------------- /src/renderer/components/content/shared/iconsLogo/restore.tsx: -------------------------------------------------------------------------------- 1 | const TitleBarRestore = (props) => ( 2 | 9 | 13 | 14 | ) 15 | 16 | export default TitleBarRestore -------------------------------------------------------------------------------- /.erb/scripts/check-node-env.js: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | 3 | export default function checkNodeEnv(expectedEnv) { 4 | if (!expectedEnv) { 5 | throw new Error('"expectedEnv" not set'); 6 | } 7 | 8 | if (process.env.NODE_ENV !== expectedEnv) { 9 | console.log( 10 | chalk.whiteBright.bgRed.bold( 11 | `"process.env.NODE_ENV" must be "${expectedEnv}" to use this webpack config` 12 | ) 13 | ); 14 | process.exit(2); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /assets/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/views/overview/categoryDistribution/categoriesRGB.tsx: -------------------------------------------------------------------------------- 1 | export const categoriesRGB = { 2 | characters: "rgba(255, 99, 132, 0.2)", 3 | status_icons: "rgba(153, 102, 255, 0.2)", 4 | weapon_cases: "rgba(54, 162, 235, 0.2)", 5 | patches: "rgba(99, 102, 241, 0.2)", 6 | music_kits: "rgba(107, 114, 128, 0.2)", 7 | default_generated: "rgba(75, 192, 192, 0.2)", 8 | stickers: "rgba(255, 206, 86, 0.2)", 9 | tools: "rgba(255, 159, 64, 0.2)" 10 | }; -------------------------------------------------------------------------------- /src/renderer/variables/overviewOptions.tsx: -------------------------------------------------------------------------------- 1 | import { OverviewOptionsBy, OverviewOptionsLeftCharts, OverviewOptionsRightCharts } from "renderer/interfaces/overview"; 2 | export const OveviewBy: OverviewOptionsBy = { 3 | price: 'Price', 4 | volume: 'Volume' 5 | } 6 | export const OverviewRightCharts: OverviewOptionsRightCharts = { 7 | itemDistribution: 'Category' 8 | } 9 | 10 | export const OverviewLeftCharts: OverviewOptionsLeftCharts = { 11 | overall: 'Overall' 12 | } -------------------------------------------------------------------------------- /.erb/scripts/clean.js: -------------------------------------------------------------------------------- 1 | import rimraf from 'rimraf'; 2 | import webpackPaths from '../configs/webpack.paths.ts'; 3 | import process from 'process'; 4 | 5 | const args = process.argv.slice(2); 6 | const commandMap = { 7 | dist: webpackPaths.distPath, 8 | release: webpackPaths.releasePath, 9 | dll: webpackPaths.dllPath, 10 | }; 11 | 12 | args.forEach((x) => { 13 | const pathToRemove = commandMap[x]; 14 | if (pathToRemove !== undefined) { 15 | rimraf.sync(pathToRemove); 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /src/renderer/components/content/shared/iconsLogo/close.tsx: -------------------------------------------------------------------------------- 1 | const TitleBarClose = (props) => ( 2 | 11 | 16 | 17 | ) 18 | 19 | export default TitleBarClose -------------------------------------------------------------------------------- /.erb/scripts/check-port-in-use.js: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import detectPort from 'detect-port'; 3 | 4 | const port = process.env.PORT || '1212'; 5 | 6 | detectPort(port, (err, availablePort) => { 7 | if (port !== String(availablePort)) { 8 | throw new Error( 9 | chalk.whiteBright.bgRed.bold( 10 | `Port "${port}" on "localhost" is already in use. Please use another port. ex: PORT=4343 npm start` 11 | ) 12 | ); 13 | } else { 14 | process.exit(0); 15 | } 16 | }); 17 | -------------------------------------------------------------------------------- /src/renderer/components/content/shared/iconsLogo/maximize.tsx: -------------------------------------------------------------------------------- 1 | const TitleBarMaximize = (props) => ( 2 | 12 | 14 | 15 | ) 16 | 17 | export default TitleBarMaximize 18 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | /* 10 | # Coverage directory used by tools like istanbul 11 | coverage 12 | .eslintcache 13 | 14 | # Dependency directory 15 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 16 | node_modules 17 | 18 | # OSX 19 | .DS_Store 20 | 21 | release/app/dist 22 | release/build 23 | .erb/dll 24 | 25 | .idea 26 | npm-debug.log.* 27 | *.css.d.ts 28 | *.sass.d.ts 29 | *.scss.d.ts 30 | -------------------------------------------------------------------------------- /src/renderer/components/content/shared/animations.tsx: -------------------------------------------------------------------------------- 1 | import { classNames } from "./filters/inventoryFunctions" 2 | 3 | export const LoadingButton = ({className=""}) => ( 4 | 5 | 10 | 11 | ); 12 | -------------------------------------------------------------------------------- /src/renderer/components/content/Inventory/inventoryRows/QTYRow.tsx: -------------------------------------------------------------------------------- 1 | 2 | export function RowQTY({ itemRow }) { 3 | 4 | return ( 5 | <> 6 | 7 |
8 |
9 | {itemRow.combined_QTY} 10 |
11 |
12 | 13 | 14 | ); 15 | } -------------------------------------------------------------------------------- /src/renderer/components/content/shared/filters/transferAmount.tsx: -------------------------------------------------------------------------------- 1 | import { SwitchHorizontalIcon } from '@heroicons/react/solid'; 2 | 3 | export default function TransferFilter({totalAmount}) { 4 | return ( 5 | 6 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /src/renderer/interfaces/filters.tsx: -------------------------------------------------------------------------------- 1 | 2 | export interface FilterRequirement { 3 | label: string 4 | valueToCheck: string 5 | commandType: 'checkBooleanVariable' | 'checkURL' | 'checkName' | 'checkMajor' | 'checkNameAndContainer' | 'checkCapsule' 6 | } 7 | export interface Filter extends FilterRequirement{ 8 | include: boolean 9 | } 10 | 11 | export interface Filters { 12 | [key: string]: Array 13 | } 14 | 15 | export interface ClassOptionFilter { 16 | [key: string]: FilterRequirement 17 | } 18 | 19 | export interface FiltersRequirement { 20 | [key: string]: Array 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/helpers/classes/steam/inventoryManager.tsx: -------------------------------------------------------------------------------- 1 | // Sub interfaces 2 | interface itemRow { 3 | item_name: string 4 | item_customname: string | null 5 | item_url: string 6 | item_id: string 7 | position: number 8 | item_wear_name?: string 9 | item_paint_wear?: number 10 | item_origin: number 11 | item_moveable: boolean 12 | item_has_stickers: boolean 13 | equipped_ct: boolean 14 | equipped_t: boolean 15 | def_index: number 16 | stickers: Array 17 | rarity?: number 18 | rarityName: string 19 | tradeUp: boolean 20 | stattrak: boolean 21 | tradeUpConfirmed: boolean 22 | collection?: string 23 | } 24 | -------------------------------------------------------------------------------- /src/renderer/components/content/shared/rarities.tsx: -------------------------------------------------------------------------------- 1 | const itemRarities = [ 2 | { 3 | value: 'Consumer Grade', 4 | bgColorClass: 'bg-blue-200', 5 | href: '#', 6 | }, 7 | { 8 | value: 'Industrial Grade', 9 | bgColorClass: 'bg-blue-400', 10 | href: '#', 11 | }, 12 | { 13 | value: 'Mil-Spec', 14 | bgColorClass: 'bg-blue-600', 15 | href: '#', 16 | }, 17 | { 18 | value: 'Restricted', 19 | bgColorClass: 'bg-purple-500', 20 | href: '#', 21 | }, 22 | { 23 | value: 'Classified', 24 | bgColorClass: 'bg-pink-500', 25 | href: '#', 26 | } 27 | ]; 28 | 29 | export default itemRarities; 30 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | import colors from 'tailwindcss/colors'; 2 | 3 | module.exports = { 4 | purge: [], 5 | darkMode: 'class', // or 'media' or 'class' 6 | theme: { 7 | fill: { 8 | current: 'currentColor', 9 | }, 10 | extend: { 11 | colors: { 12 | 'dark-level-one': "#121212", 13 | 'dark-level-two': "#181818", 14 | 'dark-level-three': "#282828", 15 | 'dark-level-four': "#404040", 16 | 'dark-level-five': "#2B303D", 17 | 'dark-white': "#d6d3cd", 18 | 'regal-blue': '#243c5a', 19 | } 20 | }, 21 | }, 22 | variants: { 23 | extend: {}, 24 | }, 25 | plugins: [], 26 | }; 27 | -------------------------------------------------------------------------------- /src/main/util.ts: -------------------------------------------------------------------------------- 1 | /* eslint import/prefer-default-export: off, import/no-mutable-exports: off */ 2 | import { URL } from 'url'; 3 | import path from 'path'; 4 | 5 | export let resolveHtmlPath: (htmlFileName: string) => string; 6 | 7 | if (process.env.NODE_ENV === 'development') { 8 | const port = process.env.PORT || 1212; 9 | resolveHtmlPath = (htmlFileName: string) => { 10 | const url = new URL(`http://localhost:${port}`); 11 | url.pathname = htmlFileName; 12 | return url.href; 13 | }; 14 | } else { 15 | resolveHtmlPath = (htmlFileName: string) => { 16 | return `file://${path.resolve(__dirname, '../renderer/', htmlFileName)}`; 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /src/renderer/components/content/Inventory/inventoryRows/rarityRow.tsx: -------------------------------------------------------------------------------- 1 | export function RowRarity({itemRow, settingsData}) { 2 | 3 | return ( 4 | <> 5 | 6 | {settingsData.columns.includes('Rarity') ? 7 | 8 |
9 |
10 | {itemRow.rarityName} 11 |
12 |
13 | : '' } 14 | 15 | 16 | ); 17 | } -------------------------------------------------------------------------------- /src/renderer/components/content/Inventory/inventoryRows/storageRow.tsx: -------------------------------------------------------------------------------- 1 | 2 | export function RowStorage({itemRow, settingsData}) { 3 | 4 | return ( 5 | <> 6 | 7 | {settingsData.columns.includes('Storage') ? 8 | 9 |
10 |
11 | {itemRow.storage_name} 12 |
13 |
14 | : '' } 15 | 16 | 17 | ); 18 | } -------------------------------------------------------------------------------- /src/renderer/components/content/Inventory/inventoryRows/tradeholdRow.tsx: -------------------------------------------------------------------------------- 1 | 2 | export function RowTradehold({itemRow, settingsData}) { 3 | const now = new Date(); 4 | 5 | return ( 6 | <> 7 | 8 | {settingsData.columns.includes('Tradehold') ? 9 | 10 | {itemRow.trade_unlock !== undefined 11 | ? Math.ceil( 12 | (itemRow.trade_unlock.getTime() - now.getTime()) / 13 | (1000 * 60 * 60 * 24) 14 | ) + ' days' 15 | : ''} 16 | : ''} 17 | 18 | 19 | ); 20 | } -------------------------------------------------------------------------------- /src/main/helpers/classes/steam/items/steam.js: -------------------------------------------------------------------------------- 1 | const { getUsername, setUsername } = require('../settings'); 2 | 3 | async function isLoggedInElsewhere(userSession) { 4 | return new Promise((resolve) => { 5 | userSession.requestRichPresence( 6 | 730, 7 | [userSession.steamID], 8 | 'english', 9 | function (err, items) { 10 | err; 11 | if (typeof items !== 'undefined') { 12 | if (Object.keys(items['users']).length > 0) { 13 | resolve(true); 14 | } else { 15 | resolve(false); 16 | } 17 | } 18 | } 19 | ); 20 | }); 21 | } 22 | 23 | module.exports = { 24 | isLoggedInElsewhere 25 | }; 26 | -------------------------------------------------------------------------------- /src/renderer/components/content/Inventory/inventoryRows/floatRow.tsx: -------------------------------------------------------------------------------- 1 | 2 | export function RowFloat({itemRow, settingsData}) { 3 | 4 | return ( 5 | <> 6 | 7 | {settingsData.columns.includes('Float') ? 8 | 9 |
10 |
11 | {itemRow.item_paint_wear?.toString()?.substr(0, 9)} 12 |
13 |
14 | : '' } 15 | 16 | 17 | ); 18 | } -------------------------------------------------------------------------------- /src/renderer/context/toMoveContext.tsx: -------------------------------------------------------------------------------- 1 | import { createContext, useContext } from 'react'; 2 | export type toMoveContext = { 3 | getToMoveContext: any; 4 | setToMoveContext: (c: any) => void; 5 | }; 6 | 7 | export const toMoveContext = createContext({ 8 | getToMoveContext: {}, 9 | setToMoveContext: () => {}, 10 | }); 11 | export const useToMoveContext = () => useContext(toMoveContext); 12 | 13 | export async function updateToMove(getToMoveContext, options) { 14 | let fromStorage = options.hasOwnProperty('fromStorage') 15 | ? options.fromStorage 16 | : getToMoveContext['fromStorage']; 17 | console.log(fromStorage); 18 | return { 19 | fromStorage: fromStorage, 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/helpers/classes/steam/items/getCommands.js: -------------------------------------------------------------------------------- 1 | var axios = require('axios'); 2 | var items = require('./index'); 3 | 4 | // RUN PROGRAMS 5 | class fetchItems { 6 | itemsClass = items; 7 | constructor() { 8 | this.itemsClass = new items(); 9 | } 10 | 11 | async convertInventory(inventory) { 12 | const responseFiltered = this.itemsClass.inventoryConverter( 13 | inventory, 14 | false 15 | ); 16 | return responseFiltered; 17 | } 18 | async convertStorageData(inventory) { 19 | const responseFiltered = this.itemsClass.inventoryConverter( 20 | inventory, 21 | true 22 | ); 23 | return responseFiltered; 24 | } 25 | } 26 | module.exports = { 27 | fetchItems, 28 | }; 29 | -------------------------------------------------------------------------------- /src/renderer/components/content/Inventory/inventoryRows/collectionsRow.tsx: -------------------------------------------------------------------------------- 1 | 2 | export function RowCollections({ itemRow, settingsData }) { 3 | return ( 4 | <> 5 | {settingsData.columns.includes('Collections') ? 6 | 7 |
8 | 9 | 10 | 11 | {itemRow?.collection?.replace('The ', '')?.replace(' Collection', '')} 12 | 13 | 14 | 15 | 16 |
17 | : ''} 18 | 19 | ); 20 | } -------------------------------------------------------------------------------- /src/renderer/components/content/shared/filters/inventoryAmount.tsx: -------------------------------------------------------------------------------- 1 | import { ArchiveIcon } from '@heroicons/react/solid'; 2 | 3 | export default function MoveLeft({totalAmount, textToWrite = "Left"}) { 4 | return ( 5 | 6 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/helpers/classes/IPCGenerators/loginGenerator.tsx: -------------------------------------------------------------------------------- 1 | import { LoginCommand, LoginCommandReturnPackage, LoginOptions } from "shared/Interfaces.tsx/store"; 2 | 3 | class LoginGenerator { 4 | returnValue: LoginCommand = { 5 | responseStatus: 'defaultError', 6 | returnPackage: {} 7 | } 8 | 9 | setResponseStatus(responseStatus: keyof LoginOptions) { 10 | this.returnValue.responseStatus = responseStatus 11 | } 12 | setEmptyPackage() { 13 | this.returnValue.returnPackage = {} 14 | } 15 | 16 | setPackage(returnPackage: LoginCommandReturnPackage) { 17 | this.returnValue.returnPackage = returnPackage 18 | } 19 | 20 | } 21 | 22 | module.exports = { 23 | LoginGenerator 24 | }; 25 | export { LoginGenerator }; 26 | -------------------------------------------------------------------------------- /src/renderer/components/content/shared/filters/accountAmount.tsx: -------------------------------------------------------------------------------- 1 | import { DatabaseIcon } from '@heroicons/react/solid'; 2 | 3 | export default function AccountAmount({totalAmount, textToWrite = "Left"}) { 4 | return ( 5 | 6 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /.erb/scripts/electron-rebuild.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import { execSync } from 'child_process'; 3 | import fs from 'fs'; 4 | import { dependencies } from '../../release/app/package.json'; 5 | import webpackPaths from '../configs/webpack.paths'; 6 | 7 | if ( 8 | Object.keys(dependencies || {}).length > 0 && 9 | fs.existsSync(webpackPaths.appNodeModulesPath) 10 | ) { 11 | const electronRebuildCmd = 12 | '../../node_modules/.bin/electron-rebuild --parallel --force --types prod,dev,optional --module-dir .'; 13 | const cmd = 14 | process.platform === 'win32' 15 | ? electronRebuildCmd.replace(/\//g, '\\') 16 | : electronRebuildCmd; 17 | execSync(cmd, { 18 | cwd: webpackPaths.appPath, 19 | stdio: 'inherit', 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /src/renderer/store/helpers/globaloffensiveNotifications.tsx: -------------------------------------------------------------------------------- 1 | export const GCConnectionStatus = { 2 | HAVE_SESSION: 0, 3 | GC_GOING_DOWN: 1, 4 | NO_SESSION: 2, 5 | NO_SESSION_IN_LOGON_QUEUE: 3, 6 | NO_STEAM: 4 7 | }; 8 | 9 | 10 | export const globalMessages = { 11 | NameItem: 1006, 12 | UnlockCrate: 1007, 13 | XRayItemReveal: 1008, 14 | XRayItemClaim: 1009, 15 | CasketTooFull: 1011, 16 | CasketContents: 1012, 17 | CasketAdded: 1013, 18 | CasketRemoved: 1014, 19 | CasketInvFull: 1015, 20 | NameBaseItem: 1019, 21 | RemoveItemName: 1030, 22 | RemoveSticker: 1053, 23 | ApplySticker: 1086, 24 | StatTrakSwap: 1088, 25 | ActivateFanToken: 9178, 26 | ActivateOperationCoin: 9179, 27 | GraffitiUnseal: 9185, 28 | GenerateSouvenir: 9204 29 | }; 30 | -------------------------------------------------------------------------------- /src/renderer/store/actions/modalTrade.tsx: -------------------------------------------------------------------------------- 1 | export const setTradeMove = () => { 2 | return { 3 | type: 'TRADE_MODAL_OPEN_CLOSE', 4 | } 5 | } 6 | export const setTradeConfirm = (inven) => { 7 | return { 8 | type: 'TRADE_MODAL_CONFIRM', 9 | payload: { 10 | inventory: inven 11 | } 12 | } 13 | } 14 | 15 | export const setTradeReset = () => { 16 | return { 17 | type: 'TRADE_MODAL_RESET' 18 | } 19 | } 20 | 21 | export const setTradeFoundMatch = (matchRow) => { 22 | return { 23 | type: 'TRADE_MODAL_MATCH_FOUND', 24 | payload: { 25 | matchRow: matchRow 26 | } 27 | } 28 | } 29 | export const setTradeMoveResult = () => { 30 | return { 31 | type: 'TRADE_MODAL_OPEN_RESULT', 32 | } 33 | } 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/renderer/components/content/Inventory/inventoryFilterSetup.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | characteristics, 3 | containers, 4 | FilterManager, 5 | } from 'renderer/functionsClasses/filters/filters'; 6 | 7 | export function InventoryGetFilterManager() { 8 | const ClassFilters = new FilterManager(); 9 | // Add characteristics 10 | Object.values(characteristics).forEach((filter) => { 11 | 12 | ClassFilters.addFilter('Include', filter, true); 13 | }); 14 | Object.values(characteristics).forEach((filter) => { 15 | 16 | ClassFilters.addFilter('Exclude', filter, false); 17 | }); 18 | 19 | // Add Containers 20 | Object.values(containers).forEach((filter) => { 21 | 22 | ClassFilters.addFilter('Containers', filter, true); 23 | }); 24 | 25 | return ClassFilters; 26 | } 27 | -------------------------------------------------------------------------------- /src/renderer/store/helpers/userStatusHelper.tsx: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | import { createCSGOImage } from "../../functionsClasses/createCSGOImage"; 3 | export async function getURL(steamID: string): Promise { 4 | let defaultReturnString = createCSGOImage("econ/characters/customplayer_tm_separatist") 5 | return new Promise((resolve) => { 6 | axios 7 | .get(`http://steamcommunity.com/profiles/${steamID}/?xml=1`) 8 | .then(function(response) { 9 | const parser = new DOMParser(); 10 | resolve(parser?.parseFromString(response.data,"text/xml")?.getElementsByTagName("profile")[0]?.getElementsByTagName("avatarMedium")[0]?.childNodes[0]?.nodeValue || defaultReturnString 11 | ) 12 | }) 13 | }).catch(error => console.log(error)); 14 | } 15 | -------------------------------------------------------------------------------- /src/renderer/index.tsx: -------------------------------------------------------------------------------- 1 | import { render } from "react-dom"; 2 | import { Provider } from "react-redux"; 3 | import App from "./App"; 4 | import { PersistGate } from 'redux-persist/integration/react' 5 | import returnVar from './store/configureStore' 6 | 7 | const myVar = returnVar() 8 | 9 | 10 | declare global { 11 | interface Window { 12 | electron: { 13 | store: { 14 | get: (key: string) => any; 15 | set: (key: string, val: any) => void; 16 | // any other methods you've defined... 17 | }, 18 | ipcRenderer: any 19 | } 20 | 21 | } 22 | } 23 | 24 | render( 25 | 26 | 27 | 28 | 29 | 30 | 31 | , document.getElementById("root")); -------------------------------------------------------------------------------- /.erb/scripts/check-build-exists.ts: -------------------------------------------------------------------------------- 1 | // Check if the renderer and main bundles are built 2 | import path from 'path'; 3 | import chalk from 'chalk'; 4 | import fs from 'fs'; 5 | import webpackPaths from '../configs/webpack.paths'; 6 | 7 | const mainPath = path.join(webpackPaths.distMainPath, 'main.js'); 8 | const rendererPath = path.join(webpackPaths.distRendererPath, 'renderer.js'); 9 | 10 | if (!fs.existsSync(mainPath)) { 11 | throw new Error( 12 | chalk.whiteBright.bgRed.bold( 13 | 'The main process is not built yet. Build it by running "npm run build:main"' 14 | ) 15 | ); 16 | } 17 | 18 | if (!fs.existsSync(rendererPath)) { 19 | throw new Error( 20 | chalk.whiteBright.bgRed.bold( 21 | 'The renderer process is not built yet. Build it by running "npm run build:renderer"' 22 | ) 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /sign.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | 3 | exports.default = async function(configuration) { 4 | // do not include passwords or other sensitive data in the file 5 | // rather create environment variables with sensitive data 6 | const TIMESTAMP = process.env.SIGNING_TIMESTAMP; 7 | const SIGNING_PATH = process.env.SIGNING_PATH; 8 | console.log("Signing with timestamp: " + TIMESTAMP); 9 | console.log("Signing path: " + SIGNING_PATH); 10 | 11 | require("child_process").execSync( 12 | // your commande here ! For exemple and with JSign : 13 | `signtool sign /tr ${TIMESTAMP} /td sha256 /fd sha256 /a "${configuration.path}"`, 14 | {cwd: SIGNING_PATH} 15 | ); 16 | 17 | // Sleep for 5 seconds to allow the signing to complete 18 | await new Promise(resolve => setTimeout(resolve, 5000)); 19 | }; -------------------------------------------------------------------------------- /src/renderer/components/content/shared/filters/pricingAmount.tsx: -------------------------------------------------------------------------------- 1 | import { CashIcon } from '@heroicons/react/solid'; 2 | import { classNames } from './inventoryFunctions'; 3 | 4 | export default function PricingAmount({totalAmount, pricingAmount = 0, IconToUse = CashIcon, colorOf = "text-yellow-500"}) { 5 | return ( 6 | 7 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /src/shared/Interfaces.tsx/login.tsx: -------------------------------------------------------------------------------- 1 | 2 | 3 | export interface DispatchStoreBuildingObject { 4 | name: string 5 | action: Function 6 | } 7 | export type DispatchStoreHandleBuildingOptionsClass = { 8 | [key in keyof DispatchStoresettingsOptions]: DispatchStoreBuildingObject; 9 | } 10 | 11 | export interface DispatchStoresettingsOptions { 12 | locale: string 13 | os: string 14 | columns: string 15 | devmode: string 16 | currency: string 17 | steamLoginShow: string 18 | } 19 | 20 | 21 | // Store 22 | export interface DispatchIPCBuildingObject { 23 | endpoint: Function 24 | action: Function 25 | } 26 | export type DispatchIPCHandleBuildingOptionsClass = { 27 | [key in keyof DispatchIPCsettingsOptions]: DispatchIPCBuildingObject; 28 | } 29 | 30 | export interface DispatchIPCsettingsOptions { 31 | currency: string 32 | } 33 | -------------------------------------------------------------------------------- /release/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "casemove", 3 | "productName": "casemove", 4 | "version": "2.3.7", 5 | "description": "Counter-Strike 2 Storage unit manager that helps you move stuff. ", 6 | "main": "./dist/main/main.js", 7 | "author": { 8 | "name": "Nombers", 9 | "email": "casemovenombers@gmail.com", 10 | "url": "https://github.com/nombersDev/casemove" 11 | }, 12 | "scripts": { 13 | "electron-rebuild": "node -r ts-node/register ../../.erb/scripts/electron-rebuild.js", 14 | "link-modules": "node -r ts-node/register ../../.erb/scripts/link-modules.js", 15 | "postinstall": "npm run electron-rebuild && npm run link-modules" 16 | }, 17 | "dependencies": { 18 | "globaloffensive": "^3.1.0", 19 | "steam-user": "^4.28.1", 20 | "steam-totp": "^2.1.1", 21 | "steam-session": "^1.1.1" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/renderer/store/configureStore.tsx: -------------------------------------------------------------------------------- 1 | // configureStore.js 2 | 3 | import { createStore } from 'redux' 4 | import { persistStore, persistReducer } from 'redux-persist' 5 | import storage from 'redux-persist/lib/storage' // defaults to localStorage for web 6 | import rootReducers from './reducer' 7 | 8 | 9 | const persistConfig = { 10 | key: 'root', 11 | storage, 12 | } 13 | 14 | const persistedReducer = persistReducer(persistConfig, rootReducers) 15 | 16 | export default () => { 17 | let reduxStore = createStore(persistedReducer) 18 | if (process.env.NODE_ENV === 'development') { 19 | reduxStore = createStore(persistedReducer, 20 | // @ts-ignore 21 | window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()) 22 | } 23 | 24 | let persistor = persistStore(reduxStore) 25 | return { reduxStore, persistor } 26 | } -------------------------------------------------------------------------------- /src/renderer/store/inventory/inventoryInterfaces.tsx: -------------------------------------------------------------------------------- 1 | import { ItemRow } from "renderer/interfaces/items"; 2 | 3 | // CONFIG 4 | export interface InventoryMethods { 5 | 'INVENTORY_SET_INVENTORY': string 6 | 'INVENTORY_STORAGES_ADD_TO': string 7 | 'INVENTORY_STORAGES_CLEAR_CASKET': string 8 | 'INVENTORY_STORAGES_SET_SORT_STORAGES': string 9 | 'INVENTORY_STORAGES_CLEAR_ALL': string 10 | 'MOVE_FROM_CLEAR': string 11 | 'SIGN_OUT': string 12 | } 13 | 14 | export type InventoryMatchingObject = { 15 | [reducerType in keyof InventoryMethods]: Function; 16 | }; 17 | 18 | export interface ActionInterface { 19 | type: keyof InventoryMethods 20 | } 21 | 22 | // ACTIONS 23 | export interface SetInventory extends ActionInterface { 24 | payload: { 25 | inventory: Array 26 | combinedInventory: Array 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2021", 4 | "module": "commonjs", 5 | "lib": ["dom", "esnext"], 6 | "declaration": true, 7 | "declarationMap": true, 8 | "jsx": "react-jsx", 9 | "strict": true, 10 | "pretty": true, 11 | "sourceMap": true, 12 | "baseUrl": "./src", 13 | /* Additional Checks */ 14 | "noUnusedLocals": true, 15 | "noUnusedParameters": true, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": true, 18 | /* Module Resolution Options */ 19 | "moduleResolution": "node", 20 | "esModuleInterop": true, 21 | "allowSyntheticDefaultImports": true, 22 | "resolveJsonModule": true, 23 | "allowJs": true, 24 | "outDir": "release/app/dist", 25 | "noImplicitAny": false 26 | }, 27 | "exclude": ["test", "release/build", "release/app/dist", ".erb/dll"] 28 | } 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Coverage directory used by tools like istanbul 11 | coverage 12 | .eslintcache 13 | 14 | # Dependency directory 15 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 16 | node_modules 17 | 18 | # OSX 19 | .DS_Store 20 | 21 | release/app/dist 22 | release/build 23 | .erb/dll 24 | .env 25 | 26 | .idea 27 | npm-debug.log.* 28 | *.css.d.ts 29 | *.sass.d.ts 30 | *.scss.d.ts 31 | .DS_Store 32 | .DS_Store 33 | src/main/helpers/classes/dev/* 34 | src/renderer/views/purchase/* 35 | src/renderer/store/reducer/modalPurchase.tsx 36 | src/renderer/store/reducer/purchase.tsx 37 | src/renderer/components/content/shared/modals & notifcations/modalPurchase.tsx 38 | src/renderer/store/actions/modalPurchase Actions.tsx 39 | src/renderer/store/actions/purchaseActions.tsx 40 | -------------------------------------------------------------------------------- /src/renderer/store/reducer/modalRename.tsx: -------------------------------------------------------------------------------- 1 | import { RenameModal } from "renderer/interfaces/states"; 2 | 3 | const initialState: RenameModal = { 4 | renameOpen: false, 5 | modalPayload: { 6 | itemID: '', 7 | itemName: '' 8 | }, 9 | }; 10 | 11 | const modalRenameReducer = (state = initialState, action) => { 12 | switch (action.type) { 13 | case 'SET_RENAME_MODAL': 14 | return { 15 | ...state, 16 | renameOpen: true, 17 | modalPayload: action.payload 18 | } 19 | case 'CLOSE_RENAME_MODAL': 20 | return { 21 | ...state, 22 | renameOpen: false 23 | } 24 | case 'SIGN_OUT': 25 | return { 26 | ...initialState 27 | } 28 | default: 29 | return {...state} 30 | 31 | } 32 | }; 33 | 34 | 35 | export default modalRenameReducer; 36 | -------------------------------------------------------------------------------- /src/renderer/components/content/storageUnits/to/toFilterSetup.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | characteristics, 3 | containers, 4 | FilterManager, 5 | } from 'renderer/functionsClasses/filters/filters'; 6 | 7 | export function toGetFilterManager() { 8 | const ClassFilters = new FilterManager(); 9 | // Add characteristics 10 | Object.values(characteristics).forEach((filter) => { 11 | if (filter.label != 'Storage moveable') { 12 | ClassFilters.addFilter('Include', filter, true); 13 | } 14 | }); 15 | 16 | // Add characteristics 17 | Object.values(characteristics).forEach((filter) => { 18 | if (filter.label != 'Storage moveable') { 19 | ClassFilters.addFilter('Exclude', filter, false); 20 | } 21 | }); 22 | // Add Containers 23 | Object.values(containers).forEach((filter) => { 24 | 25 | ClassFilters.addFilter('Containers', filter, true); 26 | }); 27 | return ClassFilters; 28 | } 29 | -------------------------------------------------------------------------------- /src/renderer/components/content/storageUnits/from/fromFilterSetup.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | characteristics, 3 | containers, 4 | FilterManager, 5 | } from 'renderer/functionsClasses/filters/filters'; 6 | 7 | export function fromGetFilterManager() { 8 | const ClassFilters = new FilterManager(); 9 | // Add characteristics 10 | Object.values(characteristics).forEach((filter) => { 11 | if (filter.label != 'Storage moveable') { 12 | ClassFilters.addFilter('Include', filter, true); 13 | } 14 | }); 15 | 16 | // Add characteristics 17 | Object.values(characteristics).forEach((filter) => { 18 | if (filter.label != 'Storage moveable') { 19 | ClassFilters.addFilter('Exclude', filter, false); 20 | } 21 | }); 22 | // Add Containers 23 | Object.values(containers).forEach((filter) => { 24 | 25 | ClassFilters.addFilter('Containers', filter, true); 26 | }); 27 | return ClassFilters; 28 | } 29 | -------------------------------------------------------------------------------- /src/renderer/components/content/storageUnits/from/Content.tsx: -------------------------------------------------------------------------------- 1 | import { HashRouter as Router, Route } from 'react-router-dom'; 2 | import FromMainComponent from './fromHolder'; 3 | 4 | function StorageUnits() { 5 | return ( 6 | <> 7 | {/* Page title & actions */} 8 |
9 |
10 |

11 | Transfer from storage units 12 |

13 |
14 |
15 | 16 | 17 | ); 18 | } 19 | 20 | export default function StorageUnitsComponent() { 21 | return ( 22 | 23 | 24 | 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /src/renderer/functionsClasses/reducerManager.tsx: -------------------------------------------------------------------------------- 1 | const names = { 2 | userdetails: 'authReducer', 3 | inventory: 'inventoryReducer', 4 | inventoryFilters: 'inventoryFiltersReducer', 5 | modalMove: 'modalMoveReducer', 6 | modalRename: 'modalRenameReducer', 7 | moveFrom: 'moveFromReducer', 8 | moveTo: 'moveToReducer', 9 | settings: 'settingsReducer', 10 | pricing: 'pricingReducer', 11 | tradeUp: 'tradeUpReducer', 12 | modalTrade: 'modalTradeReducer' 13 | } 14 | 15 | export class ReducerManager{ 16 | 17 | useSelector: Function 18 | names = names 19 | preExising: any = {} 20 | 21 | constructor(useSelector: Function) { 22 | this.useSelector = useSelector 23 | } 24 | 25 | getStorage(namesOption?: string) { 26 | 27 | if (namesOption == undefined) { 28 | return this.useSelector((state: any) => state); 29 | } 30 | return this.useSelector((state: any) => state[namesOption]); 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /src/renderer/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Casemove 7 | 8 | 27 | 28 |
29 | 30 | 38 | 39 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: 'erb', 3 | rules: { 4 | // A temporary hack related to IDE not resolving correct package.json 5 | 'import/no-extraneous-dependencies': 'off', 6 | // Since React 17 and typescript 4.1 you can safely disable the rule 7 | 'react/react-in-jsx-scope': 'off', 8 | }, 9 | parserOptions: { 10 | ecmaVersion: 2020, 11 | sourceType: 'module', 12 | project: './tsconfig.json', 13 | tsconfigRootDir: __dirname, 14 | createDefaultProgram: true, 15 | }, 16 | settings: { 17 | 'import/resolver': { 18 | // See https://github.com/benmosher/eslint-plugin-import/issues/1396#issuecomment-575727774 for line below 19 | node: {}, 20 | webpack: { 21 | config: require.resolve('./.erb/configs/webpack.config.eslint.ts'), 22 | }, 23 | }, 24 | 'import/parsers': { 25 | '@typescript-eslint/parser': ['.ts', '.tsx'], 26 | }, 27 | }, 28 | }; 29 | -------------------------------------------------------------------------------- /src/renderer/components/content/shared/emptyState.tsx: -------------------------------------------------------------------------------- 1 | export default function EmptyComponent() { 2 | return ( 3 |
4 | 19 |

Nothing here

20 |

21 | Expecting something here? Try the reload button or hide toogle. 22 |

23 |
24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /src/renderer/views/overview/rightGraph.tsx: -------------------------------------------------------------------------------- 1 | import { useSelector } from "react-redux"; 2 | import { ReducerManager } from "renderer/functionsClasses/reducerManager"; 3 | import { Settings } from "renderer/interfaces/states"; 4 | import EmptyField from "./EmptyField"; 5 | import ItemDistributionByVolume from "./categoryDistribution/categoryDistribution"; 6 | 7 | export default function RightGraph() { 8 | let ReducerClass = new ReducerManager(useSelector); 9 | let settingsData: Settings = ReducerClass.getStorage(ReducerClass.names.settings) 10 | 11 | let by = settingsData.overview.by 12 | let right = settingsData.overview.chartRight 13 | 14 | let returnObject = { 15 | itemDistribution: ItemDistributionByVolume 16 | } 17 | 18 | let Fitting = returnObject[right] 19 | if (Fitting == undefined) { 20 | Fitting = EmptyField 21 | } 22 | console.log(by) 23 | 24 | 25 | 26 | 27 | 28 | return ( 29 | <> 30 | 31 | 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/renderer/store/actions/userStatsActions.tsx: -------------------------------------------------------------------------------- 1 | import { SignInActionPackage } from "renderer/interfaces/store/authReducerActionsInterfaces" 2 | 3 | 4 | 5 | export const signIn = (forwardPackage: SignInActionPackage) => { 6 | return { 7 | type: 'SIGN_IN', 8 | payload: forwardPackage 9 | } 10 | } 11 | 12 | export const signOut = () => { 13 | return { 14 | type: 'SIGN_OUT' 15 | } 16 | } 17 | 18 | export const setConnection = (connectionStatus: boolean) => { 19 | return { 20 | type: 'SET_CONNECTION', 21 | payload: { 22 | hasConnection: connectionStatus 23 | } 24 | } 25 | } 26 | export const setWalletBalance = (walletBalance) => { 27 | return { 28 | type: 'SET_WALLET_BALANCE', 29 | payload: walletBalance 30 | } 31 | } 32 | export const setGC = (connectionStatus: boolean) => { 33 | return { 34 | type: 'SET_GC', 35 | payload: { 36 | CSGOConnection: connectionStatus 37 | } 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.erb/scripts/notarize.js: -------------------------------------------------------------------------------- 1 | const { notarize } = require('electron-notarize'); 2 | const { build } = require('../../package.json'); 3 | exports.default = async function notarizeMacos(context) { 4 | const { electronPlatformName, appOutDir } = context; 5 | if (electronPlatformName !== 'darwin') { 6 | return; 7 | } 8 | require('dotenv').config(); 9 | 10 | if (process.env.CI !== "true") { 11 | console.warn('Skipping notarizing step. Packaging is not running in CI'); 12 | return; 13 | } 14 | 15 | if (!('APPLE_ID' in process.env && 'APPLE_ID_PASS' in process.env)) { 16 | console.warn('Skipping notarizing step. APPLE_ID and APPLE_ID_PASS env variables must be set'); 17 | return; 18 | } 19 | 20 | const appName = context.packager.appInfo.productFilename; 21 | 22 | await notarize({ 23 | appBundleId: build.appId, 24 | appPath: `${appOutDir}/${appName}.app`, 25 | appleId: process.env.APPLE_ID, 26 | appleIdPassword: process.env.APPLE_ID_PASS, 27 | teamId: process.env.APPLE_TEAM_ID, 28 | }); 29 | }; 30 | -------------------------------------------------------------------------------- /src/renderer/interfaces/items.tsx: -------------------------------------------------------------------------------- 1 | 2 | export interface ItemRow { 3 | item_name: string 4 | item_customname: string | null 5 | item_url: string 6 | item_id: string 7 | position: number 8 | item_wear_name: string 9 | item_paint_wear?: number 10 | item_origin: number 11 | item_moveable: boolean 12 | item_has_stickers: boolean 13 | equipped_ct: boolean 14 | equipped_t: boolean 15 | def_index: number 16 | stickers: Array 17 | rarity: number 18 | rarityName: string 19 | tradeUp: boolean 20 | stattrak: boolean 21 | tradeUpConfirmed: boolean 22 | collection: string 23 | combined_ids: Array 24 | combined_QTY: number 25 | bgColorClass: string 26 | category: string 27 | major: string 28 | storage_id: string 29 | item_storage_total: number 30 | percentage: number 31 | } 32 | 33 | export interface ItemRowStorage extends ItemRow { 34 | storage_id: string 35 | item_storage_total: number 36 | storage_name: string 37 | } 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/renderer/views/overview/leftGraph.tsx: -------------------------------------------------------------------------------- 1 | import { useSelector } from "react-redux"; 2 | import { ReducerManager } from "renderer/functionsClasses/reducerManager"; 3 | import { Settings } from "renderer/interfaces/states"; 4 | import EmptyField from "./EmptyField"; 5 | import OverallVolume from "./leftGraph/barChartOverall"; 6 | 7 | export default function LeftGraph() { 8 | let ReducerClass = new ReducerManager(useSelector); 9 | let settingsData: Settings = ReducerClass.getStorage(ReducerClass.names.settings) 10 | 11 | let by = settingsData.overview.by 12 | let left = settingsData.overview.chartleft 13 | 14 | let returnObject = { 15 | overall: { 16 | volume: OverallVolume, 17 | price: OverallVolume 18 | } 19 | } 20 | 21 | let Fitting = returnObject[left][by] 22 | if (Fitting == undefined) { 23 | Fitting = EmptyField 24 | } 25 | console.log(Fitting) 26 | 27 | 28 | 29 | 30 | 31 | return ( 32 | <> 33 | 34 | 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /src/renderer/store/actions/pricingActions.tsx: -------------------------------------------------------------------------------- 1 | import { ItemRow } from "renderer/interfaces/items" 2 | 3 | export const pricing_addPrice = (itemRows) => { 4 | return { 5 | type: 'PRICING_ADD_TO', 6 | payload: { 7 | itemRows: itemRows 8 | } 9 | } 10 | } 11 | export const pricing_removePrice = (priceResult, itemName) => { 12 | return { 13 | type: 'PRICING_REMOVE', 14 | payload: { 15 | price: priceResult, 16 | itemName: itemName 17 | } 18 | } 19 | } 20 | export const pricing_add_storage_total = (amountToAdd) => { 21 | return { 22 | type: 'PRICING_ADD_STORAGE_TOTAL', 23 | payload: { 24 | storageAmount: amountToAdd 25 | } 26 | } 27 | } 28 | 29 | export const pricing_add_to_requested = (itemRows: Array) => { 30 | return { 31 | type: 'PRICING_ADD_TO_REQUESTED', 32 | payload: { 33 | itemRows: itemRows 34 | } 35 | } 36 | } 37 | export const pricing_clearAll = () => { 38 | return { 39 | type: 'PRICING_CLEAR' 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/renderer/components/content/Inventory/inventoryRows/functions.tsx: -------------------------------------------------------------------------------- 1 | 2 | 3 | import { State } from "renderer/interfaces/states"; 4 | import { inventorySetSortStorage } from "renderer/store/inventory/inventoryActions"; 5 | import { SetSortOption } from "renderer/store/actions/moveFromActions"; 6 | import { sortDataFunction } from "../../shared/filters/inventoryFunctions"; 7 | 8 | export async function onSortChange(dispatch: Function, sortValue: string, currentState: State) { 9 | dispatch(SetSortOption(sortValue)); 10 | const storageResult = await sortDataFunction( 11 | sortValue, 12 | currentState.inventoryReducer.storageInventory, 13 | currentState.pricingReducer.prices, currentState.settingsReducer?.source?.title 14 | ); 15 | const storageResultFiltered = await sortDataFunction( 16 | sortValue, 17 | currentState.inventoryFiltersReducer.storageFiltered, 18 | currentState.pricingReducer.prices, currentState.settingsReducer?.source?.title 19 | ); 20 | dispatch(inventorySetSortStorage(storageResult, storageResultFiltered)); 21 | } 22 | -------------------------------------------------------------------------------- /src/shared/Interfaces.tsx/store.tsx: -------------------------------------------------------------------------------- 1 | import { ItemRow } from "renderer/interfaces/items"; 2 | import { WalletInterface } from "renderer/interfaces/states"; 3 | 4 | export interface LoginCommandReturnPackage { 5 | steamID: string 6 | displayName: string 7 | haveGCSession: boolean 8 | csgoInventory: Array 9 | walletToSend: WalletInterface 10 | } 11 | export interface LoginCommand { 12 | responseStatus: keyof LoginOptions, 13 | returnPackage: {} | LoginCommandReturnPackage 14 | } 15 | 16 | export type HandleLoginObjectClass = { 17 | [key in keyof LoginOptions]: Function; 18 | } 19 | 20 | export interface LoginNotification { 21 | success: boolean 22 | title: string 23 | text: string 24 | } 25 | 26 | export interface LoginOptions { 27 | loggedIn: string, 28 | steamGuardError: string, 29 | steamGuardCodeIncorrect: string 30 | defaultError: string 31 | playingElsewhere: string 32 | wrongLoginToken: string 33 | webtokenNotJSON: string 34 | webtokenNotLoggedIn: string 35 | } 36 | 37 | export type LoginNotificationObject = { 38 | [key in keyof LoginOptions]: LoginNotification; 39 | }; 40 | -------------------------------------------------------------------------------- /src/renderer/components/content/Inventory/inventoryRows/moveableRow.tsx: -------------------------------------------------------------------------------- 1 | import { CheckCircleIcon } from "@heroicons/react/solid"; 2 | 3 | export function RowMoveable({itemRow, settingsData}) { 4 | 5 | return ( 6 | <> 7 | {settingsData.columns.includes('Moveable') ? ( 8 | 12 |
13 | {itemRow.item_moveable == true ? ( 14 |
22 | 23 | ) : ( 24 | '' 25 | )} 26 | 27 | 28 | ); 29 | } -------------------------------------------------------------------------------- /src/renderer/store/reducer/index.tsx: -------------------------------------------------------------------------------- 1 | import { combineReducers } from "redux"; 2 | // import inventoryReducer from "../inventory/inventoryReducer"; 3 | import authReducer from "./userStatus"; 4 | import inventoryFiltersReducer from './inventoryFiltersRed' 5 | import modalMoveReducer from './modalMove' 6 | import modalRenameReducer from './modalRename' 7 | import moveFromReducer from './moveFromReducers' 8 | import moveToReducer from './moveToReducers' 9 | import settingsReducer from "./settings"; 10 | import pricingReducer from "./pricing"; 11 | import tradeUpReducer from "./tradeupReducer"; 12 | import modalTradeReducer from './modalTrade' 13 | import { inventoryReducer } from "../inventory/inventoryClass"; 14 | 15 | const rootReducers = combineReducers({ 16 | authReducer, 17 | inventoryReducer, 18 | inventoryFiltersReducer, 19 | modalMoveReducer, 20 | modalRenameReducer, 21 | moveFromReducer, 22 | moveToReducer, 23 | settingsReducer, 24 | pricingReducer, 25 | tradeUpReducer, 26 | modalTradeReducer 27 | }) 28 | 29 | export default rootReducers; 30 | 31 | export type RootState = ReturnType 32 | -------------------------------------------------------------------------------- /src/renderer/views/overview/EmptyField.tsx: -------------------------------------------------------------------------------- 1 | /* This example requires Tailwind CSS v2.0+ */ 2 | export default function EmptyField() { 3 | return ( 4 | 25 | ) 26 | } -------------------------------------------------------------------------------- /.erb/configs/webpack.config.base.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Base webpack config used across other specific configs 3 | */ 4 | 5 | import webpack from 'webpack'; 6 | import webpackPaths from './webpack.paths'; 7 | import { dependencies as externals } from '../../release/app/package.json'; 8 | 9 | export default { 10 | externals: [...Object.keys(externals || {})], 11 | 12 | stats: 'errors-only', 13 | 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.[jt]sx?$/, 18 | exclude: /node_modules/, 19 | use: { 20 | loader: 'ts-loader', 21 | }, 22 | }, 23 | ], 24 | }, 25 | 26 | output: { 27 | path: webpackPaths.srcPath, 28 | // https://github.com/webpack/webpack/issues/1114 29 | library: { 30 | type: 'commonjs2', 31 | }, 32 | }, 33 | 34 | /** 35 | * Determine the array of extensions that should be used to resolve modules. 36 | */ 37 | resolve: { 38 | extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'], 39 | modules: [webpackPaths.srcPath, 'node_modules'], 40 | }, 41 | 42 | plugins: [ 43 | new webpack.EnvironmentPlugin({ 44 | NODE_ENV: 'production', 45 | }), 46 | ], 47 | }; 48 | -------------------------------------------------------------------------------- /.erb/configs/webpack.paths.ts: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const rootPath = path.join(__dirname, '../..'); 4 | 5 | const dllPath = path.join(__dirname, '../dll'); 6 | 7 | const srcPath = path.join(rootPath, 'src'); 8 | const srcMainPath = path.join(srcPath, 'main'); 9 | const srcRendererPath = path.join(srcPath, 'renderer'); 10 | 11 | const releasePath = path.join(rootPath, 'release'); 12 | const appPath = path.join(releasePath, 'app'); 13 | const appPackagePath = path.join(appPath, 'package.json'); 14 | const appNodeModulesPath = path.join(appPath, 'node_modules'); 15 | const srcNodeModulesPath = path.join(srcPath, 'node_modules'); 16 | 17 | const distPath = path.join(appPath, 'dist'); 18 | const distMainPath = path.join(distPath, 'main'); 19 | const distRendererPath = path.join(distPath, 'renderer'); 20 | 21 | const buildPath = path.join(releasePath, 'build'); 22 | 23 | export default { 24 | rootPath, 25 | dllPath, 26 | srcPath, 27 | srcMainPath, 28 | srcRendererPath, 29 | releasePath, 30 | appPath, 31 | appPackagePath, 32 | appNodeModulesPath, 33 | srcNodeModulesPath, 34 | distPath, 35 | distMainPath, 36 | distRendererPath, 37 | buildPath, 38 | }; 39 | -------------------------------------------------------------------------------- /src/renderer/components/content/Inventory/inventoryRows/inventoryLinkRow.tsx: -------------------------------------------------------------------------------- 1 | import { ExternalLinkIcon } from "@heroicons/react/solid"; 2 | import { Link } from "react-router-dom"; 3 | 4 | export function RowLinkInventory({ itemRow, settingsData, userDetails }) { 5 | 6 | return ( 7 | <> 8 | {settingsData.columns.includes('Inventory link') ? ( 9 | 13 |
14 | 20 |
26 | 27 | ) : ( 28 | '' 29 | )} 30 | 31 | 32 | ); 33 | } -------------------------------------------------------------------------------- /src/renderer/store/actions/tradeUpActions.tsx: -------------------------------------------------------------------------------- 1 | 2 | export const tradeUpAddRemove = (productRow) => { 3 | return { 4 | type: 'TRADEUP_ADD_REMOVE', 5 | payload: productRow 6 | } 7 | } 8 | 9 | export const tradeUpSetPossible = (productRow) => { 10 | return { 11 | type: 'TRADEUP_SET_POSSIBLE', 12 | payload: productRow 13 | } 14 | } 15 | 16 | export const tradeUpResetPossible = () => { 17 | return { 18 | type: 'TRADEUP_RESET' 19 | } 20 | } 21 | 22 | export const tradeUpSetSearch = (searchField) => { 23 | return { 24 | type: 'TRADEUP_SET_SEARCH', 25 | payload: { 26 | searchField: searchField 27 | } 28 | } 29 | } 30 | 31 | export const tradeUpSetMin = (min) => { 32 | return { 33 | type: 'TRADEUP_SET_MIN', 34 | payload: min 35 | } 36 | } 37 | export const tradeUpSetMax = (max) => { 38 | return { 39 | type: 'TRADEUP_SET_MAX', 40 | payload: max 41 | } 42 | } 43 | export const tradeUpCollectionsAddRemove = (collection) => { 44 | return { 45 | type: 'TRADEUP_ADDREMOVE_COLLECTION', 46 | payload: collection 47 | } 48 | } 49 | 50 | export const tradeUpOptionsAddRemove = (option) => { 51 | return { 52 | type: 'TRADEUP_ADDREMOVE_OPTION', 53 | payload: option 54 | } 55 | } -------------------------------------------------------------------------------- /src/renderer/store/inventory/inventoryActions.tsx: -------------------------------------------------------------------------------- 1 | import { SetInventory } from "./inventoryInterfaces" 2 | 3 | export const setInventoryAction = (forwardPackage: any): SetInventory => { 4 | return { 5 | type: 'INVENTORY_SET_INVENTORY', 6 | payload: { 7 | inventory: forwardPackage.inventory, 8 | combinedInventory: forwardPackage.combinedInventory 9 | } 10 | } 11 | } 12 | 13 | export const addStorageInventoryData = (storageRowsRaw, storageData, casketID, sortValue) => { 14 | return { 15 | type: 'INVENTORY_STORAGES_ADD_TO', 16 | payload: { 17 | storageRowsRaw: storageRowsRaw, 18 | storageData: storageData, 19 | casketID: casketID, 20 | sortValue: sortValue 21 | } 22 | } 23 | } 24 | 25 | export const inventorySetSortStorage = (storageData, storageFiltered) => { 26 | return { 27 | type: 'INVENTORY_STORAGES_SET_SORT_STORAGES', 28 | payload: { 29 | storageData, 30 | storageFiltered 31 | } 32 | } 33 | } 34 | 35 | export const clearStorageIDData = (casketID) => { 36 | return { 37 | type: 'INVENTORY_STORAGES_CLEAR_CASKET', 38 | payload: { 39 | casketID: casketID 40 | } 41 | } 42 | } 43 | 44 | export const clearStorage = () => { 45 | return { 46 | type: 'INVENTORY_STORAGES_CLEAR_ALL' 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/renderer/store/actions/modalMove actions.tsx: -------------------------------------------------------------------------------- 1 | export const setRenameModal = (itemID, itemName) => { 2 | return { 3 | type: 'SET_RENAME_MODAL', 4 | payload: { 5 | itemID: itemID, 6 | itemName: itemName 7 | } 8 | } 9 | } 10 | 11 | export const closeRenameModal = () => { 12 | return { 13 | type: 'CLOSE_RENAME_MODAL' 14 | } 15 | } 16 | 17 | 18 | export const moveModalQuerySet = (queryList) => { 19 | return { 20 | type: 'MOVE_MODAL_QUERY_SET', 21 | payload: { 22 | query: queryList 23 | } 24 | } 25 | } 26 | export const modalResetStorageIdsToClearFrom = () => { 27 | return { 28 | type: 'MODAL_RESET_STORAGE_IDS_TO_CLEAR_FROM' 29 | } 30 | } 31 | export const closeMoveModal = () => { 32 | return { 33 | type: 'CLOSE_MOVE_MODAL' 34 | } 35 | } 36 | 37 | export const cancelModal = (key) => { 38 | return { 39 | type: 'MOVE_MODAL_CANCEL', 40 | payload: { 41 | doCancel: key 42 | } 43 | } 44 | } 45 | 46 | export const moveModalAddToFail = () => { 47 | return { 48 | type: 'MODAL_ADD_TO_FAILED', 49 | } 50 | } 51 | 52 | 53 | export const moveModalUpdate = () => { 54 | return { 55 | type: 'MOVE_MODAL_UPDATE' 56 | } 57 | } 58 | 59 | export const moveModalResetPayload = () => { 60 | return { 61 | type: 'MOVE_MODAL_RESET_PAYLOAD' 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /src/renderer/components/content/Inventory/inventoryRows/priceRow.tsx: -------------------------------------------------------------------------------- 1 | 2 | import { ConvertPricesFormatted } from "renderer/functionsClasses/prices"; 3 | 4 | 5 | export function RowPrice({itemRow, settingsData, pricesReducer}) { 6 | const PricesClass = new ConvertPricesFormatted(settingsData, pricesReducer) 7 | const price = PricesClass.getPrice(itemRow) 8 | const formattedPrice = PricesClass.getFormattedPrice(itemRow) 9 | const formattedPriceCombined = PricesClass.getFormattedPriceCombined(itemRow) 10 | 11 | return ( 12 | <> 13 | 14 | {settingsData.columns.includes('Price') ? 15 | 16 |
17 |
18 | {formattedPriceCombined} 19 |
20 |
21 |
22 |
23 | {!price 24 | ? '' 25 | : itemRow.combined_QTY == 1 26 | ? '' 27 | : formattedPrice} 28 |
29 |
30 | : '' } 31 | 32 | 33 | ); 34 | } -------------------------------------------------------------------------------- /src/renderer/store/handleMessage.tsx: -------------------------------------------------------------------------------- 1 | 2 | // 1: Inventory changed 3 | // 2: User log in / log out 4 | // 3: CSGO Connection 5 | 6 | import combineInventory from "renderer/components/content/shared/filters/inventoryFunctions" 7 | import { setInventoryAction } from "./inventory/inventoryActions" 8 | import { setConnection, setGC, signOut, setWalletBalance } from "./actions/userStatsActions" 9 | 10 | export async function handleLogonSuccess(message) { 11 | console.log(message) 12 | } 13 | 14 | export async function handleUserEvent(message, settings) { 15 | const statusCode = message[0] 16 | const description = message[1] 17 | switch (statusCode) { 18 | case 1: 19 | const subMessage = message[2] 20 | return setInventoryAction({inventory: subMessage[1], combinedInventory: await combineInventory(subMessage[1], settings)}) 21 | 22 | case 2: 23 | if (description == 'disconnected') { 24 | return setConnection(false) 25 | } 26 | if (description == 'reconnected') { 27 | return setConnection(true) 28 | } 29 | if (description == 'fatalError') { 30 | return signOut() 31 | } 32 | return 33 | 34 | case 3: 35 | if (description == 'disconnectedFromGC') { 36 | return setGC(false) 37 | } else { 38 | return setGC(true) 39 | } 40 | case 4: 41 | return setWalletBalance(description) 42 | default: 43 | return 44 | 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /src/renderer/store/reducer/modalTrade.tsx: -------------------------------------------------------------------------------- 1 | 2 | import { ModalTrade } from "renderer/interfaces/states"; 3 | 4 | const initialState: ModalTrade = { 5 | moveOpen: false, 6 | openResult: false, 7 | inventoryFirst: [], 8 | rowToMatch: {} 9 | }; 10 | 11 | const modalTradeReducer = (state = initialState, action) => { 12 | switch (action.type) { 13 | case 'TRADE_MODAL_OPEN_CLOSE': 14 | return { 15 | ...state, 16 | moveOpen: !state.moveOpen 17 | } 18 | case 'TRADE_MODAL_CONFIRM': 19 | return { 20 | ...state, 21 | moveOpen: false, 22 | inventoryFirst: action.payload.inventory 23 | } 24 | 25 | case 'TRADE_MODAL_MATCH_FOUND': 26 | return { 27 | ...state, 28 | openResult: true, 29 | inventoryFirst: initialState.inventoryFirst, 30 | rowToMatch: action.payload.matchRow 31 | } 32 | case 'TRADE_MODAL_RESET': 33 | return { 34 | ...initialState 35 | } 36 | case 'TRADE_MODAL_OPEN_RESULT': 37 | if (state.moveOpen == true) { 38 | return { 39 | ...state, 40 | moveOpen: false, 41 | openResult: !state.openResult 42 | } 43 | } 44 | return { 45 | ...state, 46 | openResult: !state.openResult 47 | } 48 | 49 | 50 | 51 | case 'SIGN_OUT': 52 | return { 53 | ...initialState 54 | } 55 | default: 56 | return {...state} 57 | 58 | } 59 | }; 60 | 61 | 62 | export default modalTradeReducer; 63 | -------------------------------------------------------------------------------- /src/renderer/store/actions/settings.tsx: -------------------------------------------------------------------------------- 1 | import { Overview } from "renderer/interfaces/states" 2 | import { CurrencyReturnValue } from "shared/Interfaces.tsx/IPCReturn" 3 | 4 | export const setColumns = (valueToSet) => { 5 | return { 6 | type: 'SETTINGS_SET_COLUMNS', 7 | payload: valueToSet 8 | } 9 | } 10 | export const setCurrencyValue = (valueToSet) => { 11 | return { 12 | type: 'SETTINGS_SET_CURRENCY', 13 | payload: valueToSet 14 | } 15 | } 16 | export const setLocale = (valueToSet) => { 17 | return { 18 | type: 'SETTINGS_SET_LOCALE', 19 | payload: valueToSet 20 | } 21 | } 22 | export const setSourceValue = (valueToSet) => { 23 | return { 24 | type: 'SETTINGS_SET_SOURCE', 25 | payload: valueToSet 26 | } 27 | } 28 | export const setCurrencyRate = (returnPackage: CurrencyReturnValue) => { 29 | return { 30 | type: 'SETTINGS_ADD_CURRENCYPRICE', 31 | payload: { 32 | currency: returnPackage.currency, 33 | rate: returnPackage.rate 34 | } 35 | } 36 | } 37 | export const setOS = (os) => { 38 | return { 39 | type: 'SETTINGS_SET_OS', 40 | payload: os 41 | } 42 | } 43 | export const setSteamLoginShow = (loginShow) => { 44 | return { 45 | type: 'SETTINGS_SET_STEAMLOGINSHOW', 46 | payload: loginShow 47 | } 48 | } 49 | export const setDevmode = (devmode) => { 50 | return { 51 | type: 'SETTINGS_SET_DEVMODE', 52 | payload: devmode 53 | } 54 | } 55 | export const setOverview = (newObject: Overview) => { 56 | return { 57 | type: 'SETTINGS_SET_OVERVIEW', 58 | payload: newObject 59 | } 60 | } -------------------------------------------------------------------------------- /src/main/helpers/login/loginRegular.tsx: -------------------------------------------------------------------------------- 1 | import { EAuthTokenPlatformType, LoginSession } from 'steam-session'; 2 | import { StartLoginSessionWithCredentialsDetails } from 'steam-session/dist/interfaces-external'; 3 | import { LoginOptions } from '../../../shared/Interfaces.tsx/store'; 4 | import { storeRefreshToken } from '../classes/steam/settings'; 5 | 6 | export async function flowLoginRegular( 7 | loginDetails: StartLoginSessionWithCredentialsDetails, 8 | doStoreLogin: boolean 9 | ): Promise<{ 10 | responseStatus: keyof LoginOptions; 11 | refreshToken?: string; 12 | }> { 13 | return new Promise(async (resolve) => { 14 | let session = new LoginSession(EAuthTokenPlatformType.SteamClient); 15 | session.on('authenticated', async () => { 16 | console.log( 17 | `Logged into Steam as authenticated - ${session.accountName}` 18 | ); 19 | 20 | if (doStoreLogin) { 21 | storeRefreshToken(session.accountName, session.refreshToken); 22 | } 23 | 24 | resolve({ 25 | responseStatus: 'loggedIn', 26 | refreshToken: session.refreshToken, 27 | }); 28 | }); 29 | 30 | session.once('timeout', () => { 31 | resolve({ 32 | responseStatus: 'defaultError', 33 | }); 34 | }); 35 | 36 | session.once('error', (err) => { 37 | console.log('Error', err); 38 | resolve({ 39 | responseStatus: 'defaultError', 40 | }); 41 | }); 42 | try { 43 | await session.startWithCredentials(loginDetails); 44 | } catch (e) { 45 | console.log(e); 46 | resolve({ 47 | responseStatus: 'defaultError', 48 | }); 49 | } 50 | }); 51 | } 52 | -------------------------------------------------------------------------------- /src/renderer/functionsClasses/filters/search.tsx: -------------------------------------------------------------------------------- 1 | 2 | import { ItemRow } from "renderer/interfaces/items"; 3 | import { InventoryFilters, MoveFromReducer, MoveToReducer } from "renderer/interfaces/states"; 4 | 5 | 6 | export function searchFilter(itemsArray: Array, inventoryFilters: InventoryFilters, chosenReducer: InventoryFilters | MoveFromReducer | MoveToReducer | undefined): Array { 7 | let searchString: string = '' 8 | if (chosenReducer != undefined) { 9 | searchString = chosenReducer.searchInput 10 | } 11 | return itemsArray.filter(function (row) { 12 | 13 | if ( 14 | inventoryFilters.categoryFilter.length != 0 ) { 15 | if (!inventoryFilters.categoryFilter?.includes(row.bgColorClass as string)) { 16 | return false 17 | } 18 | } 19 | if ( 20 | row.item_name 21 | ?.toLowerCase() 22 | .trim() 23 | .includes(searchString?.toLowerCase().trim()) 24 | ) { 25 | return true; // skip 26 | } 27 | if ( 28 | row.item_wear_name 29 | ?.toLowerCase() 30 | .trim() 31 | .includes(searchString?.toLowerCase().trim()) 32 | ) { 33 | return true; // skip 34 | } 35 | if ( 36 | row.item_customname 37 | ?.toLowerCase() 38 | .trim() 39 | .includes(searchString?.toLowerCase().trim()) 40 | ) { 41 | return true; // skip 42 | } 43 | if (searchString == undefined || searchString == '' ) { 44 | return true; // skip 45 | } 46 | return false; 47 | }); 48 | } 49 | -------------------------------------------------------------------------------- /src/renderer/store/reducer/userStatus.tsx: -------------------------------------------------------------------------------- 1 | import { AuthReducer } from "renderer/interfaces/states"; 2 | 3 | const initialState: AuthReducer = { 4 | displayName: null , 5 | CSGOConnection: false, 6 | userProfilePicture: null , 7 | steamID: null, 8 | isLoggedIn: false , 9 | hasConnection: false, 10 | walletBalance: { 11 | hasWallet: false, 12 | currency: '', 13 | balance: 0 14 | } 15 | }; 16 | 17 | const authReducer = (state = initialState, action) => { 18 | switch (action.type) { 19 | case 'SIGN_IN': 20 | return { 21 | ...state, 22 | displayName: action.payload.displayName, 23 | CSGOConnection: action.payload.CSGOConnection, 24 | userProfilePicture: action.payload.userProfilePicture, 25 | steamID: action.payload.steamID, 26 | isLoggedIn: true, 27 | hasConnection: true, 28 | walletBalance: action.payload.wallet 29 | } 30 | case 'SIGN_OUT': 31 | return { 32 | ...initialState 33 | } 34 | 35 | case 'SET_CONNECTION': 36 | return { 37 | ...state, 38 | hasConnection: action.payload.hasConnection 39 | } 40 | case 'SET_WALLET_BALANCE': 41 | return { 42 | ...state, 43 | walletBalance: action.payload 44 | } 45 | 46 | 47 | case 'SET_GC': 48 | return { 49 | ...state, 50 | CSGOConnection: action.payload.CSGOConnection 51 | } 52 | case 'LOGOUT': 53 | return { 54 | ...initialState 55 | } 56 | default: 57 | return state 58 | 59 | } 60 | }; 61 | 62 | export default authReducer; 63 | -------------------------------------------------------------------------------- /src/renderer/views/login/login.tsx: -------------------------------------------------------------------------------- 1 | import { Disclosure } from '@headlessui/react' 2 | import { useState } from 'react' 3 | import LoginForm from './loginForm' 4 | import UserGrid from './userManagement' 5 | import { BrowserRouter as Router, Route } from 'react-router-dom'; 6 | Disclosure 7 | function LoginPageContent() { 8 | const [getLock, setLock] = useState(['']) 9 | const [deleteUser, setdeleteUser] = useState('') 10 | 11 | return ( 12 | <> 13 | {/* 14 | This example requires updating your template: 15 | 16 | ``` 17 | 18 | 19 | ``` 20 | */} 21 |
22 | 23 | 24 | {/* Account switcher */} 25 |
26 | 27 | 28 | setLock(username)} runDeleteUser={() => setdeleteUser('')} deleteUser={deleteUser} /> 29 |
30 | 31 | {/* Login */} 32 |
36 |
37 | setLock([''])} runDeleteUser={(username) => setdeleteUser(username)}/> 38 |
39 |
40 |
41 | 42 | ) 43 | } 44 | 45 | export default function LoginPage() { 46 | return ( 47 | 48 | 49 | 50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /src/main/helpers/login/flowLoginRegularQR.tsx: -------------------------------------------------------------------------------- 1 | import { EAuthTokenPlatformType, LoginSession } from 'steam-session'; 2 | import { LoginOptions } from '../../../shared/Interfaces.tsx/store'; 3 | import { storeRefreshToken } from '../classes/steam/settings'; 4 | import { emitterAccount } from '../../../emitters'; 5 | 6 | export async function flowLoginRegularQR(doStoreLogin: boolean): Promise<{ 7 | responseStatus: keyof LoginOptions; 8 | session?: LoginSession; 9 | }> { 10 | return new Promise(async (resolve) => { 11 | let session = new LoginSession(EAuthTokenPlatformType.SteamClient); 12 | console.log('Start with QR'); 13 | 14 | session.on('authenticated', async () => { 15 | console.log(`Logged into Steam as ${session.accountName}`); 16 | 17 | if (doStoreLogin) { 18 | storeRefreshToken(session.accountName, session.refreshToken); 19 | } 20 | 21 | resolve({ 22 | responseStatus: 'loggedIn', 23 | session, 24 | }); 25 | }); 26 | 27 | session.once('timeout', () => { 28 | resolve({ 29 | responseStatus: 'defaultError', 30 | }); 31 | }); 32 | 33 | session.once('error', (_err) => { 34 | console.log('Error'); 35 | resolve({ 36 | responseStatus: 'defaultError', 37 | }); 38 | }); 39 | try { 40 | emitterAccount.once('qrLogin:cancel', () => { 41 | session.removeAllListeners('authenticated'); 42 | session.removeAllListeners('timeout'); 43 | session.removeAllListeners('error'); 44 | session.cancelLoginAttempt(); 45 | }); 46 | const { qrChallengeUrl } = await session.startWithQR(); 47 | emitterAccount.emit('qrLogin:show', qrChallengeUrl); 48 | } catch { 49 | resolve({ 50 | responseStatus: 'defaultError', 51 | }); 52 | } 53 | }); 54 | } 55 | -------------------------------------------------------------------------------- /src/renderer/views/overview/overview.tsx: -------------------------------------------------------------------------------- 1 | 2 | import { useSelector } from 'react-redux'; 3 | import { BrowserRouter as Router, Route } from 'react-router-dom'; 4 | import RunOverview from './runOverview'; 5 | 6 | function overviewContent() { 7 | const tradeUpData = useSelector((state: any) => state.tradeUpReducer); 8 | const pricesResult = useSelector((state: any) => state.pricingReducer); 9 | const settingsData = useSelector((state: any) => state.settingsReducer); 10 | let totalFloat = 0; 11 | let totalPrice = 0; 12 | tradeUpData.tradeUpProducts.forEach((element) => { 13 | totalFloat += element.item_paint_wear; 14 | totalPrice += 15 | pricesResult.prices[element.item_name + element.item_wear_name || '']?.['steam_listing'] * 16 | settingsData.currencyPrice[settingsData.currency]; 17 | }); 18 | totalFloat = totalFloat / tradeUpData.tradeUpProducts.length; 19 | let totalEV = 0; 20 | tradeUpData.possibleOutcomes.forEach((element) => { 21 | let individualPrice = 22 | pricesResult?.prices[element.item_name + element.item_wear_name || '']?.['steam_listing'] * settingsData.currencyPrice[settingsData.currency]; 23 | totalEV += individualPrice * (element.percentage / 100); 24 | console.log( 25 | element, 26 | element.percentage, 27 | individualPrice * (element.percentage / 100) 28 | ); 29 | }); 30 | totalEV 31 | totalPrice 32 | 33 | return ( 34 | <> 35 |
36 |
37 |
40 | 41 | 42 |
43 |
44 |
45 | 46 | ); 47 | } 48 | export default function OverviewPage() { 49 | return ( 50 | 51 | 52 | 53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /src/renderer/store/actions/moveToActions.tsx: -------------------------------------------------------------------------------- 1 | export const moveToSetHide = () => { 2 | return { 3 | type: 'MOVE_TO_SET_HIDE' 4 | } 5 | } 6 | export const moveToSetFull = () => { 7 | return { 8 | type: 'MOVE_TO_SET_FULL' 9 | } 10 | } 11 | export const moveToClearAll = () => { 12 | return { 13 | type: 'MOVE_TO_CLEAR_ALL' 14 | } 15 | } 16 | export const doCancel = (doCancel) => { 17 | return { 18 | type: 'DO_CANCEL', 19 | payload: { 20 | doCancel: doCancel 21 | } 22 | } 23 | } 24 | export const moveTosetSearchField = (searchField) => { 25 | return { 26 | type: 'MOVE_TO_SET_SEARCH', 27 | payload: { 28 | searchField: searchField 29 | } 30 | } 31 | } 32 | export const moveTosetSearchFieldStorage = (searchField) => { 33 | return { 34 | type: 'MOVE_TO_SET_SEARCH_STORAGE', 35 | payload: { 36 | searchField: searchField 37 | } 38 | } 39 | } 40 | 41 | export const moveToAddCasketToStorages = (casketID, casketVolume) => { 42 | return { 43 | type: 'MOVE_TO_ADD_TO', 44 | payload: { 45 | casketID: casketID, 46 | casketVolume: casketVolume 47 | } 48 | } 49 | } 50 | 51 | export const moveToSetStorageAmount = (storageAmount) => { 52 | return { 53 | type: 'SET_STORAGE_AMOUNT', 54 | payload: { 55 | storageAmount: storageAmount 56 | } 57 | } 58 | } 59 | export const moveToAddRemove = (casketID, itemID, totalItems, itemName) => { 60 | return { 61 | type: 'MOVE_TO_TOTAL_TO_ADD', 62 | payload: { 63 | casketID: casketID, 64 | toMove: totalItems, 65 | itemID: itemID, 66 | itemName:itemName 67 | 68 | } 69 | } 70 | } 71 | 72 | 73 | -------------------------------------------------------------------------------- /src/main/scripts/versionHelper.tsx: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { GithubResponse } from 'main/interfaces/mainInterfaces'; 3 | 4 | 5 | async function getGithubVersion(platform: string): Promise { 6 | return new Promise((resolve) => { 7 | axios 8 | .get('https://api.github.com/repos/nombersDev/casemove/releases') 9 | .then((response) => { 10 | const responseData: JSON = response.data; 11 | 12 | for (const [_key, value] of Object.entries(responseData)) { 13 | if (value.prerelease == false) { 14 | console.log('githubVersion', value.tag_name.replaceAll('.', '')); 15 | let downloadLink: string = value['html_url']; 16 | console.log('Platform: ', platform) 17 | 18 | // Find the relevant download link 19 | switch (platform) { 20 | case 'win32': 21 | value.assets.forEach((element) => { 22 | if (element.name.includes('.exe') && !element.name?.toLowerCase()?.includes('blockmap')) { 23 | downloadLink = element.browser_download_url; 24 | } 25 | }); 26 | break; 27 | 28 | case 'linux': 29 | value.assets.forEach((element) => { 30 | if (element.name.includes('.dmg')) { 31 | downloadLink = element.browser_download_url; 32 | } 33 | }); 34 | break; 35 | 36 | default: 37 | break 38 | } 39 | 40 | resolve({ 41 | version: parseInt(value.tag_name.replaceAll('.', '').replaceAll('v', '')), 42 | downloadLink: downloadLink, 43 | }); 44 | break; 45 | } 46 | } 47 | }); 48 | }); 49 | } 50 | 51 | export { getGithubVersion }; 52 | -------------------------------------------------------------------------------- /src/renderer/components/content/shared/titleBarWindows.tsx: -------------------------------------------------------------------------------- 1 | import TitleBarClose from "./iconsLogo/close"; 2 | import TitleBarMaximize from "./iconsLogo/maximize"; 3 | import TitleBarMinimize from "./iconsLogo/minimize"; 4 | 5 | export default function TitleBarWindows() { 6 | 7 | async function sendAction(whichOption) { 8 | window.electron.ipcRenderer.handleWindowsActions(whichOption) 9 | } 10 | return ( 11 | <> 12 | {/* Page title & actions */} 13 |
14 |
15 | 23 |
24 |
25 | 32 |
33 |
34 | 41 |
42 |
43 | 44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /src/renderer/store/actions/moveFromActions.tsx: -------------------------------------------------------------------------------- 1 | export const moveFromSetFull = () => { 2 | return { 3 | type: 'MOVE_FROM_SET_FULL' 4 | } 5 | } 6 | 7 | export const moveFromSetSortBack = () => { 8 | return { 9 | type: 'MOVE_FROM_SET_SORT_BACK' 10 | } 11 | } 12 | export const moveFromClearAll = () => { 13 | return { 14 | type: 'MOVE_FROM_CLEAR_ALL' 15 | } 16 | } 17 | export const moveFromReset = () => { 18 | return { 19 | type: 'MOVE_FROM_CLEAR' 20 | } 21 | } 22 | export const moveFromsetSearchField = (searchField) => { 23 | return { 24 | type: 'MOVE_FROM_SET_SEARCH', 25 | payload: { 26 | searchField: searchField 27 | } 28 | } 29 | } 30 | export const moveFromsetSearchFieldStorage = (searchField) => { 31 | return { 32 | type: 'MOVE_FROM_SET_SEARCH_STORAGE', 33 | payload: { 34 | searchField: searchField 35 | } 36 | } 37 | } 38 | export const SetSortOption = (sortValue) => { 39 | return { 40 | type: 'SET_SORT', 41 | payload: { 42 | sortValue: sortValue 43 | } 44 | } 45 | } 46 | 47 | export const moveFromAddCasketToStorages = (casketID) => { 48 | return { 49 | type: 'MOVE_FROM_ADD_TO', 50 | payload: { 51 | casketID: casketID 52 | } 53 | } 54 | } 55 | 56 | export const moveFromAddRemove = (casketID: string, itemID: string, totalItems: Array, itemName: string) => { 57 | return { 58 | type: 'MOVE_FROM_TOTAL_TO_ADD', 59 | payload: { 60 | casketID: casketID, 61 | toMove: totalItems, 62 | itemID: itemID, 63 | itemName:itemName 64 | 65 | } 66 | } 67 | } 68 | 69 | export const moveFromRemoveCasket = (casketID) => { 70 | return { 71 | type: 'MOVE_FROM_ALL_CASKET_RESULTS', 72 | payload: { 73 | casketID: casketID 74 | 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /.erb/configs/webpack.config.renderer.dev.dll.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Builds the DLL for development electron renderer process 3 | */ 4 | 5 | import webpack from 'webpack'; 6 | import path from 'path'; 7 | import { merge } from 'webpack-merge'; 8 | import baseConfig from './webpack.config.base'; 9 | import webpackPaths from './webpack.paths'; 10 | import { dependencies } from '../../package.json'; 11 | import checkNodeEnv from '../scripts/check-node-env'; 12 | 13 | checkNodeEnv('development'); 14 | 15 | const dist = webpackPaths.dllPath; 16 | 17 | export default merge(baseConfig, { 18 | context: webpackPaths.rootPath, 19 | 20 | devtool: 'eval', 21 | 22 | mode: 'development', 23 | 24 | target: 'electron-renderer', 25 | 26 | externals: ['fsevents', 'crypto-browserify'], 27 | 28 | /** 29 | * Use `module` from `webpack.config.renderer.dev.js` 30 | */ 31 | module: require('./webpack.config.renderer.dev').default.module, 32 | 33 | entry: { 34 | renderer: Object.keys(dependencies || {}), 35 | }, 36 | 37 | output: { 38 | path: dist, 39 | filename: '[name].dev.dll.js', 40 | library: { 41 | name: 'renderer', 42 | type: 'var', 43 | }, 44 | }, 45 | 46 | plugins: [ 47 | new webpack.DllPlugin({ 48 | path: path.join(dist, '[name].json'), 49 | name: '[name]', 50 | }), 51 | 52 | /** 53 | * Create global constants which can be configured at compile time. 54 | * 55 | * Useful for allowing different behaviour between development builds and 56 | * release builds 57 | * 58 | * NODE_ENV should be production so that modules do not perform certain 59 | * development checks 60 | */ 61 | new webpack.EnvironmentPlugin({ 62 | NODE_ENV: 'development', 63 | }), 64 | 65 | new webpack.LoaderOptionsPlugin({ 66 | debug: true, 67 | options: { 68 | context: webpackPaths.srcPath, 69 | output: { 70 | path: webpackPaths.dllPath, 71 | }, 72 | }, 73 | }), 74 | ], 75 | }); 76 | -------------------------------------------------------------------------------- /src/renderer/store/reducer/pricing.tsx: -------------------------------------------------------------------------------- 1 | import { Prices } from "renderer/interfaces/states"; 2 | 3 | const initialState: Prices = { 4 | prices: {}, 5 | storageAmount: 0, 6 | productsRequested: [], 7 | }; 8 | 9 | const pricingReducer = (state = initialState, action) => { 10 | switch (action.type) { 11 | 12 | 13 | case 'PRICING_ADD_STORAGE_TOTAL': 14 | console.log(action.payload.storageAmount) 15 | return { 16 | ...state, 17 | storageAmount: state.storageAmount + action.payload.storageAmount, 18 | }; 19 | case 'PRICING_ADD_TO': 20 | let currentPrices = state.prices; 21 | action.payload.itemRows.forEach(element => { 22 | currentPrices[element.item_name + element.item_wear_name || ''] = element.pricing; 23 | }); 24 | console.log(currentPrices) 25 | return { 26 | ...state, 27 | prices: currentPrices, 28 | }; 29 | 30 | case 'PRICING_ADD_TO_REQUESTED': 31 | let currentRequested = state.productsRequested; 32 | action.payload.itemRows.forEach(element => { 33 | let nameToPuse = element.item_name + element.item_wear_name || '' 34 | currentRequested.push(nameToPuse) 35 | }); 36 | return { 37 | ...state, 38 | productsRequested: currentRequested, 39 | }; 40 | case 'PRICING_REMOVE': 41 | let removeCurrentPrices = state.prices; 42 | 43 | if (removeCurrentPrices[action.payload.itemName] !== undefined) { 44 | delete removeCurrentPrices[action.payload.itemName]; 45 | } 46 | return { 47 | ...state, 48 | prices: removeCurrentPrices, 49 | }; 50 | case 'PRICING_CLEAR': 51 | return { 52 | ...initialState, 53 | }; 54 | case 'MOVE_FROM_CLEAR': 55 | return { 56 | ...state, 57 | storageAmount: initialState.storageAmount 58 | } 59 | 60 | case 'SIGN_OUT': 61 | return { 62 | ...initialState, 63 | }; 64 | default: 65 | return { ...state }; 66 | } 67 | }; 68 | 69 | export default pricingReducer; 70 | -------------------------------------------------------------------------------- /.erb/scripts/check-native-dep.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import chalk from 'chalk'; 3 | import { execSync } from 'child_process'; 4 | import { dependencies } from '../../package.json'; 5 | 6 | if (dependencies) { 7 | const dependenciesKeys = Object.keys(dependencies); 8 | const nativeDeps = fs 9 | .readdirSync('node_modules') 10 | .filter((folder) => fs.existsSync(`node_modules/${folder}/binding.gyp`)); 11 | if (nativeDeps.length === 0) { 12 | process.exit(0); 13 | } 14 | try { 15 | // Find the reason for why the dependency is installed. If it is installed 16 | // because of a devDependency then that is okay. Warn when it is installed 17 | // because of a dependency 18 | const { dependencies: dependenciesObject } = JSON.parse( 19 | execSync(`npm ls ${nativeDeps.join(' ')} --json`).toString() 20 | ); 21 | const rootDependencies = Object.keys(dependenciesObject); 22 | const filteredRootDependencies = rootDependencies.filter((rootDependency) => 23 | dependenciesKeys.includes(rootDependency) 24 | ); 25 | if (filteredRootDependencies.length > 0) { 26 | const plural = filteredRootDependencies.length > 1; 27 | console.log(` 28 | ${chalk.whiteBright.bgYellow.bold( 29 | 'Webpack does not work with native dependencies.' 30 | )} 31 | ${chalk.bold(filteredRootDependencies.join(', '))} ${ 32 | plural ? 'are native dependencies' : 'is a native dependency' 33 | } and should be installed inside of the "./release/app" folder. 34 | First, uninstall the packages from "./package.json": 35 | ${chalk.whiteBright.bgGreen.bold('npm uninstall your-package')} 36 | ${chalk.bold( 37 | 'Then, instead of installing the package to the root "./package.json":' 38 | )} 39 | ${chalk.whiteBright.bgRed.bold('npm install your-package')} 40 | ${chalk.bold('Install the package to "./release/app/package.json"')} 41 | ${chalk.whiteBright.bgGreen.bold('cd ./release/app && npm install your-package')} 42 | Read more about native dependencies at: 43 | ${chalk.bold( 44 | 'https://electron-react-boilerplate.js.org/docs/adding-dependencies/#module-structure' 45 | )} 46 | `); 47 | process.exit(1); 48 | } 49 | } catch (e) { 50 | console.log('Native dependencies could not be checked'); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/renderer/components/content/loadStorageUnitsButton.tsx: -------------------------------------------------------------------------------- 1 | import { CollectionIcon } from "@heroicons/react/solid"; 2 | import { useState } from "react"; 3 | import { useDispatch, useSelector } from "react-redux"; 4 | import { ReducerManager } from "renderer/functionsClasses/reducerManager"; 5 | import { getAllStorages } from "renderer/functionsClasses/storageUnits/storageUnitsFunctions"; 6 | import { State } from "renderer/interfaces/states"; 7 | import { LoadingButton } from "./shared/animations"; 8 | import { classNames } from "./shared/filters/inventoryFunctions"; 9 | 10 | export function LoadButton() { 11 | let ReducerClass = new ReducerManager(useSelector); 12 | let currentState: State = ReducerClass.getStorage(); 13 | const dispatch = useDispatch(); 14 | // Get all storage unit data 15 | async function getAllStor() { 16 | setLoadingButton(true) 17 | getAllStorages(dispatch, currentState).then(() => { 18 | setLoadingButton(false) 19 | }) 20 | } 21 | 22 | const [getLoadingButton, setLoadingButton] = useState(false); 23 | return ( 24 | <> 25 | 45 | 46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /.erb/configs/webpack.config.main.prod.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Webpack config for production electron main process 3 | */ 4 | 5 | import path from 'path'; 6 | import webpack from 'webpack'; 7 | import { merge } from 'webpack-merge'; 8 | import TerserPlugin from 'terser-webpack-plugin'; 9 | import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; 10 | import baseConfig from './webpack.config.base'; 11 | import webpackPaths from './webpack.paths'; 12 | import checkNodeEnv from '../scripts/check-node-env'; 13 | import deleteSourceMaps from '../scripts/delete-source-maps'; 14 | 15 | checkNodeEnv('production'); 16 | deleteSourceMaps(); 17 | 18 | const devtoolsConfig = 19 | process.env.DEBUG_PROD === 'true' 20 | ? { 21 | devtool: 'source-map', 22 | } 23 | : {}; 24 | 25 | export default merge(baseConfig, { 26 | ...devtoolsConfig, 27 | 28 | mode: 'production', 29 | 30 | target: 'electron-main', 31 | 32 | entry: { 33 | main: path.join(webpackPaths.srcMainPath, 'main.ts'), 34 | preload: path.join(webpackPaths.srcMainPath, 'preload.js'), 35 | }, 36 | 37 | output: { 38 | path: webpackPaths.distMainPath, 39 | filename: '[name].js', 40 | }, 41 | 42 | optimization: { 43 | minimizer: [ 44 | new TerserPlugin({ 45 | parallel: true, 46 | }), 47 | ], 48 | }, 49 | 50 | plugins: [ 51 | new BundleAnalyzerPlugin({ 52 | analyzerMode: 53 | process.env.OPEN_ANALYZER === 'true' ? 'server' : 'disabled', 54 | openAnalyzer: process.env.OPEN_ANALYZER === 'true', 55 | }), 56 | 57 | /** 58 | * Create global constants which can be configured at compile time. 59 | * 60 | * Useful for allowing different behaviour between development builds and 61 | * release builds 62 | * 63 | * NODE_ENV should be production so that modules do not perform certain 64 | * development checks 65 | */ 66 | new webpack.EnvironmentPlugin({ 67 | NODE_ENV: 'production', 68 | DEBUG_PROD: false, 69 | START_MINIMIZED: false, 70 | }), 71 | ], 72 | 73 | /** 74 | * Disables webpack processing of __dirname and __filename. 75 | * If you run the bundle in node.js it falls back to these values of node.js. 76 | * https://github.com/webpack/webpack/issues/2010 77 | */ 78 | node: { 79 | __dirname: false, 80 | __filename: false, 81 | }, 82 | }); 83 | -------------------------------------------------------------------------------- /src/renderer/components/content/Inventory/inventoryRows/stickerPatchesRow.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { Link } from "react-router-dom"; 3 | import { createCSGOImage } from "../../../../functionsClasses/createCSGOImage"; 4 | import { classNames } from "../../shared/filters/inventoryFunctions"; 5 | 6 | export function RowStickersPatches({itemRow, settingsData}) { 7 | const [stickerHover, setStickerHover] = useState(''); 8 | 9 | return ( 10 | <> 11 | 12 | {settingsData.columns.includes('Stickers/patches') ? 13 | 14 |
15 |
16 | {itemRow.stickers?.map((sticker, index) => ( 17 | 23 | 26 | setStickerHover(index + itemRow.item_id) 27 | } 28 | onMouseLeave={() => setStickerHover('')} 29 | className={classNames( 30 | stickerHover == index + itemRow.item_id 31 | ? 'transform-gpu hover:-translate-y-1 hover:scale-110' 32 | : '', 33 | 'max-w-none h-8 w-8 rounded-full hover:shadow-sm text-black hover:bg-gray-50 transition duration-500 ease-in-out hover:text-white hover:bg-green-600 ring-2 object-cover ring-transparent bg-gradient-to-t from-gray-100 to-gray-300 dark:from-gray-300 dark:to-gray-400' 34 | )} 35 | src={ 36 | createCSGOImage(sticker.sticker_url) 37 | } 38 | alt={sticker.sticker_name} 39 | title={sticker.sticker_name} 40 | /> 41 | 42 | ))} 43 |
44 |
45 | : '' } 46 | 47 | 48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /src/renderer/functionsClasses/storageUnits/storageUnitsFunctions.tsx: -------------------------------------------------------------------------------- 1 | 2 | import { ItemRow, ItemRowStorage } from "renderer/interfaces/items"; 3 | import { State } from "renderer/interfaces/states"; 4 | import { HandleStorageData } from "./storageUnitsClass"; 5 | 6 | function sorting(valueOne, valueTwo) { 7 | if (valueOne < valueTwo) { 8 | return -1; 9 | } 10 | if (valueOne > valueTwo) { 11 | return 1; 12 | } 13 | return 0; 14 | } 15 | class Sort { 16 | itemArray: Array 17 | constructor(itemArray: Array) { 18 | this.itemArray = itemArray 19 | } 20 | 21 | async item_customname() { 22 | return this.itemArray.sort(function(a, b) { 23 | return sorting(a.item_customname || '0000', b.item_customname || '0000') 24 | }) 25 | 26 | } 27 | } 28 | 29 | export async function getAllStorages( 30 | dispatch: Function, 31 | state: State 32 | ) { 33 | 34 | // Filter the storage inventory 35 | const casketResults = await state.inventoryReducer.inventory.filter(function (row) { 36 | if (!row.item_url.includes('casket')) { 37 | return false; // skip 38 | } 39 | if (row.item_storage_total == 0) { 40 | return false; // skip 41 | } 42 | if ( 43 | state.moveFromReducer.searchInputStorage != '' && 44 | !row?.item_customname?.toLowerCase()?.includes(state.moveFromReducer.searchInputStorage) 45 | ) { 46 | return false; // skip 47 | } 48 | if (row.item_storage_total == 1000 && state.moveFromReducer.hideFull) { 49 | return false; // skip 50 | } 51 | return true; 52 | }); 53 | 54 | async function sendArrayAddStorage(returnValue: Array) { 55 | let StorageClass = new HandleStorageData(dispatch, state) 56 | let addArray: Array = [] 57 | for (const [_key, project] of Object.entries(returnValue)) { 58 | if (!state.moveFromReducer.activeStorages.includes(project.item_id)) { 59 | addArray = [...addArray, ...await StorageClass.addStorage( 60 | project as ItemRowStorage, 61 | addArray 62 | 63 | )] 64 | } 65 | } 66 | return 67 | } 68 | 69 | // Handle storage data 70 | let SortingClass = new Sort(casketResults) 71 | return SortingClass.item_customname().then((returnValue) => { 72 | return sendArrayAddStorage(returnValue) 73 | }) 74 | 75 | 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/renderer/store/reducer/settings.tsx: -------------------------------------------------------------------------------- 1 | import { Settings } from 'renderer/interfaces/states'; 2 | 3 | const initialState: Settings = { 4 | fastMove: false, 5 | currency: 'USD', 6 | locale: 'EN-GB', 7 | os: '', 8 | steamLoginShow: true, 9 | devmode: false, 10 | columns: [ 11 | 'Price', 12 | 'Stickers/patches', 13 | 'Storage', 14 | 'Tradehold', 15 | 'Moveable', 16 | 'Inventory link', 17 | ], 18 | currencyPrice: {}, 19 | source: { 20 | title: 'steam_listing', 21 | name: 'Steam Community Market', 22 | avatar: 'https://steamcommunity.com/favicon.ico', 23 | }, 24 | overview: { 25 | by: 'price', 26 | chartleft: 'overall', 27 | chartRight: 'itemDistribution', 28 | }, 29 | }; 30 | 31 | const settingsReducer = (state = initialState, action) => { 32 | switch (action.type) { 33 | case 'SETTINGS_SET_FASTMOVE': 34 | return { 35 | ...state, 36 | fastMove: action.payload, 37 | }; 38 | case 'SETTINGS_SET_COLUMNS': 39 | return { 40 | ...state, 41 | columns: action.payload, 42 | }; 43 | case 'SETTINGS_SET_CURRENCY': 44 | if (action.payload == true) { 45 | return { 46 | ...state, 47 | }; 48 | } 49 | return { 50 | ...state, 51 | currency: action.payload, 52 | }; 53 | 54 | case 'SETTINGS_SET_STEAMLOGINSHOW': 55 | return { 56 | ...state, 57 | steamLoginShow: action.payload, 58 | }; 59 | 60 | case 'SETTINGS_SET_LOCALE': 61 | return { 62 | ...state, 63 | locale: action.payload, 64 | }; 65 | case 'SETTINGS_SET_OS': 66 | return { 67 | ...state, 68 | os: action.payload, 69 | }; 70 | case 'SETTINGS_SET_DEVMODE': 71 | return { 72 | ...state, 73 | devmode: action.payload, 74 | }; 75 | case 'SETTINGS_SET_OVERVIEW': 76 | return { 77 | ...state, 78 | overview: action.payload, 79 | }; 80 | case 'SETTINGS_ADD_CURRENCYPRICE': 81 | let currencyDict = state.currencyPrice; 82 | currencyDict[action.payload.currency] = action.payload.rate; 83 | return { 84 | ...state, 85 | currency: action.payload.currency, 86 | currencyPrice: currencyDict, 87 | }; 88 | 89 | default: 90 | return { ...state }; 91 | } 92 | }; 93 | 94 | export default settingsReducer; 95 | -------------------------------------------------------------------------------- /src/renderer/views/overview/barChatMajors.tsx: -------------------------------------------------------------------------------- 1 | 2 | import { Bar } from 'react-chartjs-2'; 3 | import { useSelector } from 'react-redux'; 4 | import {itemSubCategories} from 'renderer/components/content/shared/categories'; 5 | import Chart from 'chart.js/auto'; 6 | Chart 7 | 8 | 9 | export default function BarAppMajor() { 10 | 11 | // Bar options 12 | // @ts-ignore 13 | const options = { 14 | plugins: { 15 | legend: { 16 | labels: { 17 | color: '#d6d3cd', 18 | }, 19 | 20 | }, 21 | title: { 22 | display: true, 23 | text: 'Majors', 24 | color: '#d6d3cd' 25 | } 26 | } 27 | }; 28 | 29 | // Go through inventory and find matching categories 30 | const inventory = useSelector((state: any) => state.inventoryReducer); 31 | 32 | // Convert inventory to chart data 33 | let inventoryDataToUse: Array = []; 34 | let storageUnitDataToUse: Array = []; 35 | 36 | Object.keys(itemSubCategories.majors).forEach(category => { 37 | const inventoryResult = inventory.combinedInventory.filter(itemRow => itemRow.major == category); 38 | const storageResult = inventory.storageInventory.filter(itemRow => itemRow.major == category); 39 | let categoryCounter = 0 40 | inventoryResult.forEach(element => { 41 | categoryCounter = categoryCounter + element.combined_QTY 42 | }); 43 | let storageCounter = 0 44 | storageResult.forEach(element => { 45 | storageCounter = storageCounter + element.combined_QTY 46 | }); 47 | 48 | inventoryDataToUse.push(categoryCounter) 49 | storageUnitDataToUse.push(storageCounter) 50 | }); 51 | console.log(storageUnitDataToUse) 52 | 53 | 54 | const data = { 55 | labels: Object.keys(itemSubCategories.majors), 56 | 57 | 58 | datasets: [ 59 | { 60 | label: 'Inventory', 61 | data: inventoryDataToUse, 62 | backgroundColor: 'rgba(255, 99, 132, 0.2)', 63 | borderColor: 'rgba(255, 99, 132, 1)', 64 | borderWidth: 1, 65 | }, 66 | { 67 | label: 'Storage Units', 68 | data: storageUnitDataToUse, 69 | backgroundColor: 'rgb(50, 91, 136, 0.2)', 70 | borderColor: 'rgb(50, 91, 136, 1)', 71 | borderWidth: 1, 72 | }, 73 | ], 74 | }; 75 | // @ts-ignore 76 | return ( 77 | <> 78 | 79 | 80 | 81 | ); 82 | } 83 | -------------------------------------------------------------------------------- /src/renderer/functionsClasses/downloadReport.tsx: -------------------------------------------------------------------------------- 1 | import { ItemRow, ItemRowStorage } from "renderer/interfaces/items"; 2 | import { Prices, Settings } from "renderer/interfaces/states"; 3 | import { ConvertPricesFormatted } from "./prices"; 4 | 5 | async function handleDownload(storageData) { 6 | let csvContent = 7 | 'Item Name,Item Custom Name,Price,Price Combined,Item Moveable,Storage Name,Tradehold,Category,Combined QTY,Item Wear Name,Item Paint Wear,Item Has Stickers/Patches,Stickers\n'; 8 | var csv = storageData 9 | .map(function (d) { 10 | let storageName = d.storage_name; 11 | if (storageName == undefined) { 12 | storageName = '#Inventory'; 13 | } 14 | 15 | let stickersData = d.stickers; 16 | // @ts-ignore 17 | if (stickersData != []) { 18 | let newStickers = [] as any; 19 | stickersData.forEach((element) => { 20 | newStickers.push(element.sticker_name); 21 | }); 22 | stickersData = newStickers.join(';'); 23 | } 24 | const returnDict = { 25 | item_name: d.item_name, 26 | item_customname: d.item_customname, 27 | price: d.item_price, 28 | price_combined: d.item_price_combined, 29 | item_moveable: d.item_moveable, 30 | storage_name: storageName, 31 | trade_unlock: d.trade_unlock, 32 | category: d.category, 33 | combined_QTY: d.combined_QTY, 34 | item_wear_name: d.item_wear_name, 35 | item_paint_wear: d.item_paint_wear, 36 | item_has_stickers: d.item_has_stickers, 37 | item_stickers: stickersData, 38 | }; 39 | return JSON.stringify(Object.values(returnDict)); 40 | }) 41 | .join('\n') 42 | .replaceAll('null', '') 43 | .replace(/(^\[)|(\]$)/gm, ''); 44 | csv = csvContent + csv; 45 | window.electron.ipcRenderer.downloadFile(csv); 46 | } 47 | 48 | export async function downloadReport(settingsData: Settings, pricesReducer: Prices, itemArray: Array ) { 49 | const PricesClassFormatted = new ConvertPricesFormatted(settingsData, pricesReducer) 50 | itemArray.forEach((element: any) => { 51 | element.item_price = PricesClassFormatted.getFormattedPrice(element); 52 | element.item_price_combined = PricesClassFormatted.getFormattedPriceCombined(element); 53 | }); 54 | 55 | handleDownload(itemArray); 56 | } 57 | -------------------------------------------------------------------------------- /src/renderer/components/content/shared/iconsLogo/logo.tsx: -------------------------------------------------------------------------------- 1 | const Logo = (props) => ( 2 | 10 | 11 | 12 | 13 | 16 | 20 | 21 | ); 22 | 23 | export default Logo; 24 | -------------------------------------------------------------------------------- /src/renderer/components/content/shared/steamLogo.tsx: -------------------------------------------------------------------------------- 1 | const SteamLogo = (props) => ( 2 | 9 | 10 | 11 | 12 | 13 | 14 | ) 15 | 16 | export default SteamLogo 17 | 18 | -------------------------------------------------------------------------------- /src/renderer/components/content/shared/iconsLogo/logo 2.tsx: -------------------------------------------------------------------------------- 1 | const Logo = (props) => ( 2 | 10 | 14 | 18 | 19 | ); 20 | 21 | export default Logo; 22 | -------------------------------------------------------------------------------- /src/main/helpers/classes/steam/currency.tsx: -------------------------------------------------------------------------------- 1 | const CC = require('currency-converter-lt'); 2 | 3 | async function setBackUp(currencyClass) { 4 | let rates = require('./backup/currency.json') 5 | currencyClass.setRates(rates.rates) 6 | } 7 | 8 | async function getLiveRates(currencyClass) { 9 | console.log('here') 10 | let currencyConverter = new CC({isDecimalComma:true}); 11 | currencyConverter.from('USD').to('EUR').amount(100).convert().then((response) => { 12 | console.log(response) 13 | 14 | let secondConverter = new CC(); 15 | secondConverter.from('USD').to('EUR').amount(100).convert().then((secondResponse) => { 16 | if (response < secondResponse) { 17 | currencyClass.setCurrencyClass(currencyConverter) 18 | } else { 19 | currencyClass.setCurrencyClass(secondConverter) 20 | } 21 | }) 22 | }).catch(_error => { 23 | console.log('Error initilizing') 24 | } ) 25 | console.log('here 2') 26 | } 27 | 28 | class currency { 29 | rates = {}; 30 | currencyConverter 31 | seenRates = {} 32 | 33 | constructor() { 34 | setBackUp(this) 35 | getLiveRates(this) 36 | } 37 | 38 | // Setup backup 39 | setRates(rates) { 40 | this.rates = rates 41 | } 42 | 43 | // Setup for live rates 44 | setCurrencyClass(converter) { 45 | this.currencyConverter = converter 46 | } 47 | 48 | 49 | getRate(exchangeTo) { 50 | return new Promise((resolve) => { 51 | if (this.seenRates[exchangeTo] != undefined) { 52 | resolve(this.seenRates[exchangeTo]) 53 | } 54 | if (this.currencyConverter == undefined) { 55 | resolve(this.rates[exchangeTo]) 56 | } 57 | this.currencyConverter.from('USD').to(exchangeTo).amount(100).convert().then((response) => { 58 | let rate = response / 100 59 | if (typeof rate === 'number' && !Number.isNaN(rate)) { 60 | this.seenRates[exchangeTo] = rate 61 | resolve(rate) 62 | } else { 63 | resolve(this.rates[exchangeTo]) 64 | } 65 | }).catch(error => { 66 | console.log('error occurred', error) 67 | resolve(this.rates[exchangeTo]) 68 | }) 69 | }); 70 | } 71 | } 72 | 73 | // const currencyClass = new currency() 74 | 75 | // sleep time expects milliseconds 76 | //function sleep (time) { 77 | // return new Promise((resolve) => setTimeout(resolve, time)); 78 | //} 79 | 80 | // Usage! 81 | //sleep(5000).then(() => { 82 | // currencyClass.getRate('EUR').then((returnValue) => { 83 | // console.log(returnValue) 84 | // }) 85 | //}); 86 | 87 | 88 | module.exports = { 89 | currency 90 | }; 91 | export { currency }; 92 | 93 | -------------------------------------------------------------------------------- /src/renderer/components/content/Inventory/inventory.tsx: -------------------------------------------------------------------------------- 1 | import { BrowserRouter as Router, Route } from 'react-router-dom'; 2 | import InventoryFilters from './filterHeader'; 3 | import InventoryRowsComponent from './inventoryRows'; 4 | import { useState } from 'react'; 5 | import { LoadingButton } from '../shared/animations'; 6 | import { RefreshIcon } from '@heroicons/react/solid'; 7 | 8 | function content() { 9 | const [getLoadingButton, setLoadingButton] = useState(false); 10 | setLoadingButton; 11 | 12 | // Get the inventory 13 | async function refreshInventory() { 14 | window.electron.ipcRenderer.refreshInventory(); 15 | } 16 | 17 | return ( 18 | <> 19 | {/* Page title & actions */} 20 |
21 |
22 |

23 | Inventory 24 |

25 |
26 |
27 | 42 |
43 |
44 | {/* Pinned projects */} 45 | 46 | 47 | {/* Projects list (only on smallest breakpoint) */} 48 |
49 |
50 |

51 | Storages 52 |

53 |
54 |
55 | 56 | {/* Projects table (small breakpoint and up) */} 57 |
58 |
59 | 60 |
61 |
62 | 63 | ); 64 | } 65 | export default function inventoryContent() { 66 | return ( 67 | 68 | 69 | 70 | ); 71 | } 72 | -------------------------------------------------------------------------------- /src/renderer/functionsClasses/rendererCommands/admin.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | setColumns, 3 | setCurrencyRate, 4 | setCurrencyValue, 5 | setDevmode, 6 | setLocale, 7 | setOS, 8 | setSteamLoginShow 9 | } from 'renderer/store/actions/settings'; 10 | import { 11 | DispatchIPCBuildingObject, 12 | DispatchIPCHandleBuildingOptionsClass, 13 | DispatchStoreBuildingObject, 14 | DispatchStoreHandleBuildingOptionsClass, 15 | } from 'shared/Interfaces.tsx/login'; 16 | 17 | export class IPCCommunication { 18 | ipc = window.electron.ipcRenderer; 19 | store = window.electron.store; 20 | 21 | async get(command: Function) { 22 | return await command().then((returnValue) => { 23 | return returnValue; 24 | }); 25 | } 26 | async storeGet(settingToGet: string) { 27 | return await this.store.get(settingToGet).then((returnValue) => { 28 | return returnValue; 29 | }); 30 | } 31 | } 32 | 33 | // Dispatch Store 34 | export class DispatchStore extends IPCCommunication { 35 | dispatch: Function; 36 | buildingObject: DispatchStoreHandleBuildingOptionsClass = { 37 | 38 | locale: { 39 | name: 'locale', 40 | action: setLocale 41 | }, 42 | os: { 43 | name: 'os', 44 | action: setOS 45 | }, 46 | columns: { 47 | name: 'columns', 48 | action: setColumns 49 | }, 50 | devmode: { 51 | name: 'devmode.value', 52 | action: setDevmode 53 | }, 54 | currency: { 55 | name: 'currency', 56 | action: setCurrencyValue 57 | }, 58 | steamLoginShow: { 59 | name: 'steamLogin', 60 | action: setSteamLoginShow 61 | } 62 | }; 63 | constructor(dispatch: Function) { 64 | super(); 65 | this.dispatch = dispatch; 66 | } 67 | 68 | async run(buildingObject: DispatchStoreBuildingObject) { 69 | this.storeGet(buildingObject.name).then((returnValue) => { 70 | if (returnValue != undefined) { 71 | this.dispatch(buildingObject.action(returnValue)); 72 | } 73 | }); 74 | } 75 | } 76 | 77 | // Dispatch IPC 78 | export class DispatchIPC extends IPCCommunication { 79 | dispatch: Function; 80 | buildingObject: DispatchIPCHandleBuildingOptionsClass = { 81 | currency: { 82 | endpoint: this.ipc.getCurrencyRate, 83 | action: setCurrencyRate, 84 | }, 85 | }; 86 | 87 | constructor(dispatch: Function) { 88 | super(); 89 | this.dispatch = dispatch; 90 | } 91 | 92 | async run(buildingObject: DispatchIPCBuildingObject) { 93 | this.get(buildingObject.endpoint).then((returnValue) => { 94 | if (returnValue != undefined) { 95 | this.dispatch(buildingObject.action(returnValue)); 96 | } 97 | }); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/renderer/store/inventory/inventoryReducer.tsx: -------------------------------------------------------------------------------- 1 | 2 | import { Inventory } from "renderer/interfaces/states"; 3 | 4 | const initialState: Inventory = { 5 | inventory: [], 6 | combinedInventory: [], 7 | storageInventory: [], 8 | storageInventoryRaw: [], 9 | totalAccountItems: 0, 10 | itemsLookUp: {} 11 | }; 12 | 13 | const inventoryReducer = (state = initialState, action) => { 14 | switch (action.type) { 15 | case 'INVENTORY_SET_INVENTORY': 16 | let storageTotal = 0 17 | action.payload.inventory.forEach(element => { 18 | storageTotal += 1 19 | if (element.item_url == "econ/tools/casket") { 20 | storageTotal += element.item_storage_total 21 | } 22 | }); 23 | 24 | 25 | return { 26 | ...state, 27 | inventory: action.payload.inventory, 28 | combinedInventory: action.payload.combinedInventory, 29 | totalAccountItems: storageTotal 30 | } 31 | case 'INVENTORY_STORAGES_ADD_TO': 32 | console.log(state) 33 | const add_to_filtered = state.storageInventory?.filter(id => id.storage_id != action.payload.casketID) || [] 34 | const add_to_filtered_raw = state.storageInventoryRaw?.filter(id => id.storage_id != action.pay) || [] 35 | action.payload.storageData.forEach(storageRow => add_to_filtered.push(storageRow)) 36 | action.payload.storageRowsRaw.forEach(storageRow => add_to_filtered_raw.push(storageRow)) 37 | 38 | return { 39 | ...state, 40 | storageInventory: add_to_filtered, 41 | storageInventoryRaw: add_to_filtered_raw 42 | } 43 | case 'INVENTORY_STORAGES_CLEAR_CASKET': 44 | const AddToFiltered = state.storageInventory.filter(id => id.storage_id != action.payload.casketID) 45 | const AddToFilteredRaw = state.storageInventoryRaw.filter(id => id.storage_id != action.payload.casketID) 46 | 47 | return { 48 | ...state, 49 | storageInventory: AddToFiltered, 50 | storageInventoryRaw: AddToFilteredRaw 51 | } 52 | case 'INVENTORY_STORAGES_SET_SORT_STORAGES': 53 | return { 54 | ...state, 55 | storageInventory: action.payload.storageData 56 | } 57 | case 'INVENTORY_STORAGES_CLEAR_ALL': 58 | return { 59 | ...state, 60 | storageInventory: initialState.storageInventory, 61 | storageInventoryRaw: initialState.storageInventoryRaw 62 | } 63 | case 'MOVE_FROM_CLEAR': 64 | return { 65 | ...state 66 | } 67 | case 'MOVE_FROM_RESET': 68 | return { 69 | ...state, 70 | storageInventory: initialState.storageInventory, 71 | storageInventoryRaw: initialState.storageInventoryRaw 72 | 73 | } 74 | case 'SIGN_OUT': 75 | return { 76 | ...initialState 77 | } 78 | default: 79 | return { ...state } 80 | 81 | } 82 | }; 83 | 84 | export default inventoryReducer; 85 | -------------------------------------------------------------------------------- /src/renderer/functionsClasses/filters/custom.tsx: -------------------------------------------------------------------------------- 1 | import { Filter } from "renderer/interfaces/filters"; 2 | import { ItemRow } from "renderer/interfaces/items"; 3 | 4 | 5 | class CheckFilter { 6 | itemRow: ItemRow 7 | filter: Filter 8 | 9 | constructor(itemRow: ItemRow, filter: Filter) { 10 | this.itemRow = itemRow 11 | this.filter = filter 12 | } 13 | 14 | // Check if string is in URL 15 | CheckVariableIncludes(variableName: string): boolean { 16 | return this.itemRow?.[variableName]?.includes(this.filter.valueToCheck) 17 | } 18 | 19 | // Check if variable exists and boolean 20 | checkBooleanVariable(): boolean { 21 | return this.itemRow?.[this.filter.valueToCheck] || false 22 | } 23 | 24 | // Check if Container and sub value 25 | checkContainerSubValue(variableName: string): boolean { 26 | return this.itemRow.category == 'Containers' && this.CheckVariableIncludes(variableName) || false 27 | } 28 | 29 | } 30 | 31 | 32 | 33 | function filterLogic(itemRow: ItemRow, IndividualFilter: Filter): boolean { 34 | const FilterClass = new CheckFilter(itemRow, IndividualFilter) 35 | let returnValue: boolean = false; 36 | switch (IndividualFilter.commandType) { 37 | case 'checkBooleanVariable': 38 | returnValue = FilterClass.checkBooleanVariable() 39 | break 40 | 41 | case 'checkName': 42 | returnValue = FilterClass.CheckVariableIncludes('item_name') 43 | break 44 | 45 | case 'checkURL': 46 | returnValue = FilterClass.CheckVariableIncludes('item_url') 47 | break 48 | 49 | case 'checkMajor': 50 | returnValue = FilterClass.CheckVariableIncludes('major') 51 | break 52 | 53 | case 'checkNameAndContainer': 54 | returnValue = FilterClass.checkContainerSubValue('item_name') 55 | break 56 | 57 | case 'checkCapsule': 58 | returnValue = FilterClass.checkContainerSubValue('item_name') 59 | if (itemRow.item_name.includes('Challengers') || itemRow.item_name.includes('Legends') || itemRow.item_name.includes('Contenders')) { 60 | if (!itemRow.item_name.includes('Patch')) { 61 | returnValue = true; 62 | 63 | } 64 | } 65 | 66 | break; 67 | 68 | default: 69 | break 70 | } 71 | if (IndividualFilter.include) { 72 | return returnValue 73 | } else { 74 | return !returnValue 75 | } 76 | } 77 | 78 | export async function filterItemRows(arrayToFilter: Array, filters: Array): Promise> { 79 | let returnArray = arrayToFilter; 80 | 81 | filters.forEach(filt => { 82 | 83 | returnArray = returnArray.filter(itemRow => { 84 | return filterLogic(itemRow, filt) 85 | }); 86 | }); 87 | 88 | return returnArray 89 | } -------------------------------------------------------------------------------- /src/renderer/store/reducer/modalMove.tsx: -------------------------------------------------------------------------------- 1 | import { ModalMove } from "renderer/interfaces/states"; 2 | 3 | const initialState: ModalMove = { 4 | moveOpen: false, 5 | notifcationOpen: false, 6 | storageIdsToClearFrom: [], 7 | modalPayload: { 8 | number: 0, 9 | itemID: '', 10 | isLast: false 11 | }, 12 | doCancel: [], 13 | query: [], 14 | totalFailed: 0 15 | }; 16 | 17 | const modalMoveReducer = (state = initialState, action) => { 18 | switch (action.type) { 19 | case 'MOVE_MODAL_QUERY_SET': 20 | let queryData = [...action.payload.query] 21 | queryData.shift() 22 | return { 23 | ...state, 24 | moveOpen: true, 25 | modalPayload: action.payload.query[0].payload, 26 | query: queryData 27 | } 28 | 29 | case 'MOVE_MODAL_UPDATE': 30 | if (state.query.length == 0) { 31 | return { 32 | ...state, 33 | modalPayload: initialState.modalPayload, 34 | moveOpen: false 35 | } 36 | } 37 | let initialStoragesToClear = state.storageIdsToClearFrom 38 | if (!initialStoragesToClear.includes(state.query[0].payload.storageID)) { 39 | initialStoragesToClear.push(state.query[0].payload.storageID) 40 | } 41 | if (state.doCancel.includes(state.query[0].payload.key)) { 42 | return { 43 | ...state 44 | } 45 | } 46 | let newQuery = [...state.query] 47 | newQuery.shift() 48 | return { 49 | ...state, 50 | moveOpen: true, 51 | modalPayload: state.query[0].payload, 52 | storageIdsToClearFrom: initialStoragesToClear, 53 | query: newQuery 54 | } 55 | case 'CLOSE_MOVE_MODAL': 56 | return { 57 | ...state, 58 | moveOpen: false, 59 | totalFailed: initialState.totalFailed 60 | } 61 | case 'MOVE_MODAL_RESET_PAYLOAD': 62 | return { 63 | ...state, 64 | query: initialState.query 65 | } 66 | case 'MOVE_MODAL_CANCEL': 67 | return { 68 | ...state, 69 | doCancel: [...state.doCancel, action.payload.doCancel], 70 | totalFailed: initialState.totalFailed 71 | } 72 | case 'MODAL_RESET_STORAGE_IDS_TO_CLEAR_FROM': 73 | return { 74 | ...state, 75 | storageIdsToClearFrom: initialState.storageIdsToClearFrom 76 | } 77 | case 'MODAL_ADD_TO_FAILED': 78 | return { 79 | ...state, 80 | totalFailed: state.totalFailed + 1 81 | } 82 | case 'SIGN_OUT': 83 | return { 84 | ...initialState 85 | } 86 | default: 87 | return {...state} 88 | 89 | } 90 | }; 91 | 92 | 93 | export default modalMoveReducer; 94 | -------------------------------------------------------------------------------- /src/renderer/views/login/components/LoginTabs.tsx: -------------------------------------------------------------------------------- 1 | import { LockClosedIcon, QrcodeIcon, WifiIcon } from '@heroicons/react/solid'; 2 | import { LoginMethod } from '../types/LoginMethod'; 3 | import { classNames } from '../../../components/content/shared/filters/inventoryFunctions'; 4 | 5 | interface TabProps { 6 | name: string; 7 | icon: any; 8 | key: LoginMethod; 9 | } 10 | 11 | const tabs: TabProps[] = [ 12 | { name: 'QR', icon: QrcodeIcon, key: 'QR' }, 13 | { name: 'Webtoken', icon: WifiIcon, key: 'WEBTOKEN' }, 14 | { name: 'Regular', icon: LockClosedIcon, key: 'REGULAR' }, 15 | ]; 16 | 17 | type LoginTabsProps = { 18 | selectedTab: LoginMethod; 19 | setSelectedTab: (tab: LoginMethod) => void; 20 | }; 21 | 22 | export default function LoginTabs({ 23 | selectedTab, 24 | setSelectedTab, 25 | }: LoginTabsProps) { 26 | const defaultValue: LoginMethod = 'REGULAR'; 27 | return ( 28 |
29 |
30 |
31 | 34 | {/* Use an "onChange" listener to redirect the user to the selected tab URL. */} 35 | 45 |
46 |
47 | 69 |
70 |
71 |
72 | ); 73 | } 74 | -------------------------------------------------------------------------------- /src/renderer/views/login/HandleSuccess.tsx: -------------------------------------------------------------------------------- 1 | import combineInventory, { sortDataFunctionTwo } from "renderer/components/content/shared/filters/inventoryFunctions"; 2 | import { filterItemRows } from "renderer/functionsClasses/filters/custom"; 3 | import { DispatchIPC, DispatchStore } from "renderer/functionsClasses/rendererCommands/admin" 4 | import { State } from "renderer/interfaces/states"; 5 | import { SignInActionPackage } from "renderer/interfaces/store/authReducerActionsInterfaces" 6 | import { inventorySetFilter } from "renderer/store/actions/filtersInventoryActions"; 7 | import { setInventoryAction } from "renderer/store/inventory/inventoryActions"; 8 | import { signIn } from "renderer/store/actions/userStatsActions"; 9 | import { getURL } from "renderer/store/helpers/userStatusHelper"; 10 | import { LoginCommandReturnPackage } from "shared/Interfaces.tsx/store" 11 | import { createCSGOImage } from "../../functionsClasses/createCSGOImage"; 12 | async function getProfilePicture(steamID: string): Promise { 13 | try { 14 | const profilePicture = await getURL(steamID); 15 | return profilePicture as string; 16 | } catch (error) { 17 | return createCSGOImage("econ/characters/customplayer_tm_separatist"); 18 | } 19 | } 20 | export async function handleSuccess(returnSuccessPackage: LoginCommandReturnPackage, dispatch: Function, currentState: State) { 21 | // Get Redux values 22 | const StoreClass = new DispatchStore(dispatch) 23 | const IPCClass = new DispatchIPC(dispatch) 24 | 25 | // Locale 26 | StoreClass.run(StoreClass.buildingObject.locale) 27 | 28 | // Currency 29 | IPCClass.run(IPCClass.buildingObject.currency) 30 | await new Promise((r) => setTimeout(r, 2500)); 31 | 32 | 33 | // Create a store object 34 | let signInPackage: SignInActionPackage = { 35 | userProfilePicture: await getProfilePicture(returnSuccessPackage.steamID), 36 | displayName: returnSuccessPackage.displayName, 37 | CSGOConnection: returnSuccessPackage.haveGCSession, 38 | steamID: returnSuccessPackage.steamID, 39 | wallet: returnSuccessPackage.walletToSend 40 | } 41 | 42 | // Get the profile picture 43 | 44 | dispatch(signIn(signInPackage)) 45 | 46 | // Inventory 47 | let combinedInventory = await combineInventory( 48 | returnSuccessPackage.csgoInventory, 49 | currentState.settingsReducer 50 | ) 51 | dispatch( 52 | setInventoryAction({ 53 | inventory: returnSuccessPackage.csgoInventory, 54 | combinedInventory 55 | }) 56 | ); 57 | 58 | // Filtered inventory 59 | let filteredInv = await filterItemRows( 60 | combinedInventory, 61 | currentState.inventoryFiltersReducer.inventoryFilter 62 | ); 63 | filteredInv = await sortDataFunctionTwo( 64 | currentState.inventoryFiltersReducer.sortValue, 65 | filteredInv, 66 | currentState.pricingReducer.prices, 67 | currentState.settingsReducer?.source?.title 68 | ); 69 | 70 | dispatch( 71 | inventorySetFilter( 72 | currentState.inventoryFiltersReducer.inventoryFilter, 73 | currentState.inventoryFiltersReducer.sortValue, 74 | filteredInv 75 | ) 76 | ); 77 | } 78 | -------------------------------------------------------------------------------- /src/renderer/views/overview/barChart.tsx: -------------------------------------------------------------------------------- 1 | 2 | import { Bar } from 'react-chartjs-2'; 3 | import { useSelector } from 'react-redux'; 4 | import {itemCategories} from 'renderer/components/content/shared/categories'; 5 | import Chart from 'chart.js/auto'; 6 | Chart 7 | 8 | 9 | export default function BarApp() { 10 | let categoriesFixed: Array = []; 11 | 12 | let resultingData = {} as any; 13 | itemCategories.forEach((element) => { 14 | categoriesFixed.push(element.name); 15 | resultingData[element.name] = { 16 | inventory: 0, 17 | storageUnits: 0 18 | } 19 | }); 20 | 21 | // Radar options 22 | const options = { 23 | parsing: { 24 | key: 'nested.value', 25 | }, 26 | legend: { 27 | position: 'top', 28 | labels: { 29 | fontColor: 'white', 30 | }, 31 | }, 32 | plugins: { 33 | title: { 34 | display: true, 35 | text: 'Items distribution' 36 | } 37 | }, 38 | scales: { 39 | r: { 40 | angleLines: { 41 | color: 'rgba(255, 255, 255, 0.2)', 42 | }, 43 | 44 | grid: { 45 | color: 'rgba(255, 255, 255, 0.2)', 46 | }, 47 | ticks: { 48 | color: 'white', 49 | showLabelBackdrop: false, 50 | }, 51 | }, 52 | }, 53 | }; 54 | 55 | // Go through inventory and find matching categories 56 | const inventory = useSelector((state: any) => state.inventoryReducer); 57 | inventory.combinedInventory.forEach(element => { 58 | if (resultingData[element.category]) { 59 | resultingData[element.category].inventory = resultingData?.[element.category]?.inventory + element.combined_QTY 60 | } 61 | }); 62 | 63 | // Go through Storage Units 64 | inventory.storageInventory.forEach(element => { 65 | console.log(element) 66 | if (resultingData[element.category]) { 67 | resultingData[element.category].storageUnits = resultingData?.[element.category]?.storageUnits + element.combined_QTY 68 | } 69 | }); 70 | 71 | // Convert inventory to chart data 72 | let inventoryDataToUse: Array = []; 73 | let storageUnitDataToUse: Array = []; 74 | 75 | categoriesFixed.forEach(category => { 76 | inventoryDataToUse.push(resultingData[category].inventory) 77 | storageUnitDataToUse.push(resultingData[category].storageUnits) 78 | }); 79 | console.log(storageUnitDataToUse) 80 | 81 | 82 | const data = { 83 | labels: categoriesFixed, 84 | 85 | datasets: [ 86 | { 87 | label: 'Inventory', 88 | data: inventoryDataToUse, 89 | backgroundColor: 'rgba(255, 99, 132, 0.2)', 90 | borderColor: 'rgba(255, 99, 132, 1)', 91 | borderWidth: 1, 92 | }, 93 | { 94 | label: 'Storage Units', 95 | data: storageUnitDataToUse, 96 | backgroundColor: 'rgb(50, 91, 136, 0.2)', 97 | borderColor: 'rgb(50, 91, 136, 1)', 98 | borderWidth: 1, 99 | }, 100 | ], 101 | }; 102 | 103 | return ( 104 | <> 105 | 106 | 107 | ); 108 | } 109 | -------------------------------------------------------------------------------- /src/renderer/functionsClasses/prices.tsx: -------------------------------------------------------------------------------- 1 | import { ItemRow } from 'renderer/interfaces/items'; 2 | import { Prices, Settings } from 'renderer/interfaces/states'; 3 | import { pricing_add_to_requested } from 'renderer/store/actions/pricingActions'; 4 | 5 | export class ConvertPrices { 6 | settingsData: Settings; 7 | prices: Prices; 8 | 9 | constructor(settingsData: Settings, prices: Prices) { 10 | this.settingsData = settingsData; 11 | this.prices = prices; 12 | } 13 | 14 | _getName(itemRow: ItemRow) { 15 | return itemRow.item_name + itemRow.item_wear_name || ''; 16 | } 17 | 18 | getPrice(itemRow:ItemRow, nanToZero=false) { 19 | let itemPrice = 20 | this.prices.prices[this._getName(itemRow)]?.[ 21 | this.settingsData.source.title 22 | ] * this.settingsData.currencyPrice[this.settingsData.currency]; 23 | 24 | if (nanToZero && isNaN(itemPrice)) { 25 | return 0 26 | } 27 | 28 | return itemPrice 29 | } 30 | } 31 | 32 | export class ConvertPricesFormatted extends ConvertPrices { 33 | constructor(settingsData: Settings, prices: Prices) { 34 | super(settingsData, prices); 35 | } 36 | 37 | formatPrice(price: number) { 38 | return new Intl.NumberFormat(this.settingsData.locale, { 39 | style: 'currency', 40 | currency: this.settingsData.currency, 41 | }).format(price); 42 | } 43 | 44 | getFormattedPrice(itemRow: ItemRow) { 45 | return this.formatPrice(this.getPrice(itemRow)); 46 | } 47 | getFormattedPriceCombined(itemRow: ItemRow) { 48 | let comQty = itemRow?.combined_QTY as number; 49 | return new Intl.NumberFormat(this.settingsData.locale, { 50 | style: 'currency', 51 | currency: this.settingsData.currency, 52 | }).format(comQty * this.getPrice(itemRow)); 53 | } 54 | } 55 | 56 | async function requestPrice(priceToGet: Array) { 57 | window.electron.ipcRenderer.getPrice(priceToGet); 58 | } 59 | 60 | async function dispatchRequested( 61 | dispatch: Function, 62 | rowsToGet: Array 63 | ) { 64 | dispatch(pricing_add_to_requested(rowsToGet)); 65 | } 66 | 67 | export class RequestPrices extends ConvertPrices { 68 | dispatch: Function; 69 | constructor(dispatch: Function, settingsData: Settings, prices: Prices) { 70 | super(settingsData, prices); 71 | this.dispatch = dispatch; 72 | } 73 | 74 | _checkRequested(itemRow: ItemRow): boolean { 75 | return ( 76 | this.prices.productsRequested.includes(this._getName(itemRow)) == false 77 | ); 78 | } 79 | 80 | handleRequested(itemRow: ItemRow): void { 81 | if (isNaN(this.getPrice(itemRow)) == true && this._checkRequested(itemRow)) { 82 | let rowsToSend = [itemRow]; 83 | requestPrice(rowsToSend); 84 | dispatchRequested(this.dispatch, rowsToSend); 85 | } 86 | } 87 | 88 | handleRequestArray(itemRows: Array): void { 89 | let rowsToSend = [] as Array 90 | itemRows.forEach((itemRow) => { 91 | if (isNaN(this.getPrice(itemRow)) == true && this._checkRequested(itemRow)) { 92 | rowsToSend.push(itemRow) 93 | } 94 | }); 95 | if (rowsToSend.length > 0) { 96 | requestPrice(rowsToSend); 97 | dispatchRequested(this.dispatch, rowsToSend); 98 | 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/renderer/views/overview/sidebar/sideBar.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { useDispatch, useSelector } from 'react-redux'; 3 | import { classNames } from 'renderer/components/content/shared/filters/inventoryFunctions'; 4 | import { tradeUpAddRemove } from 'renderer/store/actions/tradeUpActions'; 5 | import { createCSGOImage } from '../../../functionsClasses/createCSGOImage'; 6 | import PossibleOutcomes from './possibleOutcomes'; 7 | 8 | export default function TradeUpSideBar() { 9 | const tradeUpData = useSelector((state: any) => state.tradeUpReducer); 10 | const [itemHover, setItemHover] = useState(''); 11 | const dispatch = useDispatch(); 12 | 13 | let totalFloat = 0; 14 | tradeUpData.tradeUpProducts.forEach((element) => { 15 | totalFloat += element.item_paint_wear; 16 | }); 17 | totalFloat = totalFloat / tradeUpData.tradeUpProducts.length; 18 | 19 | 20 | let productsToUse = [...tradeUpData.tradeUpProducts]; 21 | 22 | while (true) { 23 | if (productsToUse.length != 10) { 24 | productsToUse.push({ item_name: 'EMPTY' }); 25 | } else { 26 | break; 27 | } 28 | } 29 | 30 | return ( 31 |
32 |
33 |
34 |
35 |
36 | {productsToUse.map((projectRow) => ( 37 |
41 | {projectRow.item_name == 'EMPTY' ? ( 42 |
48 | ) : ( 49 | 67 | )} 68 |
69 | ))} 70 |
71 |
72 |
73 |
74 |
75 | 76 |
77 |
78 | ); 79 | } 80 | -------------------------------------------------------------------------------- /src/renderer/views/tradeUp/sidebar/sideBar.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { useDispatch, useSelector } from 'react-redux'; 3 | import { classNames } from 'renderer/components/content/shared/filters/inventoryFunctions'; 4 | import { tradeUpAddRemove } from 'renderer/store/actions/tradeUpActions'; 5 | import { createCSGOImage } from '../../../functionsClasses/createCSGOImage'; 6 | import PossibleOutcomes from './possibleOutcomes'; 7 | 8 | export default function TradeUpSideBar() { 9 | const tradeUpData = useSelector((state: any) => state.tradeUpReducer); 10 | const [itemHover, setItemHover] = useState(''); 11 | const dispatch = useDispatch(); 12 | 13 | let totalFloat = 0; 14 | tradeUpData.tradeUpProducts.forEach((element) => { 15 | totalFloat += element.item_paint_wear; 16 | }); 17 | totalFloat = totalFloat / tradeUpData.tradeUpProducts.length; 18 | 19 | 20 | let productsToUse = [...tradeUpData.tradeUpProducts]; 21 | 22 | while (true) { 23 | if (productsToUse.length != 10) { 24 | productsToUse.push({ item_name: 'EMPTY' }); 25 | } else { 26 | break; 27 | } 28 | } 29 | 30 | return ( 31 |
32 |
33 |
34 |
35 |
36 | {productsToUse.map((projectRow) => ( 37 |
41 | {projectRow.item_name == 'EMPTY' ? ( 42 |
48 | ) : ( 49 | 67 | )} 68 |
69 | ))} 70 |
71 |
72 |
73 |
74 |
75 | 76 |
77 |
78 | ); 79 | } 80 | -------------------------------------------------------------------------------- /src/renderer/components/content/shared/categories.tsx: -------------------------------------------------------------------------------- 1 | export const itemCategories = [ 2 | { 3 | value: 'characters', 4 | name: 'Agents', 5 | bgColorClass: 'bg-pink-500', 6 | href: '#', 7 | }, 8 | { 9 | value: 'status_icons', 10 | name: 'Collectibles & Passes', 11 | bgColorClass: 'bg-red-500', 12 | href: '#', 13 | }, 14 | { 15 | value: 'weapon_cases', 16 | name: 'Containers', 17 | bgColorClass: 'bg-indigo-500', 18 | href: '#', 19 | }, 20 | { 21 | value: 'patches', 22 | name: 'Patches', 23 | bgColorClass: 'bg-indigo-200', 24 | href: '#', 25 | }, 26 | { 27 | value: 'music_kits', 28 | name: 'Music kits', 29 | bgColorClass: 'bg-gray-500', 30 | href: '#', 31 | }, 32 | { 33 | value: 'default_generated', 34 | name: 'Skins & Knives', 35 | bgColorClass: 'bg-green-500', 36 | href: '#', 37 | }, 38 | { 39 | value: 'stickers', 40 | name: 'Stickers', 41 | bgColorClass: 'bg-yellow-500', 42 | href: '#', 43 | }, 44 | { value: 'tools', name: 'Tools', bgColorClass: 'bg-yellow-800', href: '#' }, 45 | ]; 46 | 47 | 48 | 49 | export const itemSubCategories = { 50 | majors: { 51 | "Antwerp 2022": { 52 | name: "Antwerp 2022", 53 | key: "Antwerp 2022", 54 | year: 2021 55 | }, 56 | "Stockholm 2021": { 57 | name: "Stockholm 2021", 58 | key: "Stockholm 2021", 59 | year: 2021 60 | }, 61 | "2020 RMR": { 62 | name: "2020 RMR", 63 | key: "2020 RMR", 64 | year: 2020 65 | }, 66 | "Berlin 2019": { 67 | name: "Berlin 2019", 68 | key: "Berlin 2019", 69 | year: 2019 70 | }, 71 | "Katowice 2019": { 72 | name: "Katowice 2019", 73 | key: "Katowice 2019", 74 | year: 2019 75 | }, 76 | "London 2018": { 77 | name: "London 2018", 78 | key: "London 2018", 79 | year: 2018 80 | }, 81 | "Boston 2018": { 82 | name: "Boston 2018", 83 | key: "Boston 2018", 84 | year: 2018 85 | }, 86 | "Krakow 2017": { 87 | name: "Krakow 2017", 88 | key: "Krakow 2017", 89 | year: 2017 90 | }, 91 | "Atlanta 2017": { 92 | name: "Atlanta 2017", 93 | key: "Atlanta 2017", 94 | year: 2018 95 | }, 96 | "Cologne 2016": { 97 | name: "Cologne 2016", 98 | key: "Cologne 2016", 99 | year: 2016 100 | }, 101 | "Columbus 2016": { 102 | name: "Columbus 2016", 103 | key: "Columbus 2016", 104 | year: 2016 105 | }, 106 | "Cluj-Napoca 2015": { 107 | name: "Cluj-Napoca 2015", 108 | key: "Cluj-Napoca 2015", 109 | year: 2015 110 | }, 111 | "Cologne 2015": { 112 | name: "Cologne 2015", 113 | key: "Cologne 2015", 114 | year: 2015 115 | }, 116 | "Katowice 2015": { 117 | name: "Katowice 2015", 118 | key: "Katowice 2015", 119 | year: 2015 120 | }, 121 | "DreamHack 2014": { 122 | name: "DreamHack 2014", 123 | key: "DreamHack 2014", 124 | year: 2014 125 | }, 126 | "Cologne 2014": { 127 | name: "Cologne 2014", 128 | key: "Cologne 2014", 129 | year: 2014 130 | }, 131 | "Katowice 2014": { 132 | name: "Katowice 2014", 133 | key: "Katowice 2014", 134 | year: 2014 135 | } 136 | } 137 | } -------------------------------------------------------------------------------- /src/renderer/store/reducer/tradeupReducer.tsx: -------------------------------------------------------------------------------- 1 | import { TradeUpActions } from "renderer/interfaces/states"; 2 | 3 | const initialState: TradeUpActions = { 4 | tradeUpProducts: [], 5 | tradeUpProductsIDS: [], 6 | possibleOutcomes: [], 7 | searchInput: '', 8 | MinFloat: 0, 9 | MaxFloat: 1, 10 | collections: [], 11 | options: ["Hide equipped"], 12 | }; 13 | 14 | const tradeUpReducer = (state = initialState, action) => { 15 | switch (action.type) { 16 | case 'TRADEUP_ADD_REMOVE': 17 | let toMoveAlreadyExists = state.tradeUpProducts.filter(row => row.item_id != action.payload.item_id) 18 | console.log(action.payload.item_id, toMoveAlreadyExists) 19 | if (toMoveAlreadyExists.length == state.tradeUpProducts.length) { 20 | toMoveAlreadyExists.push(action.payload) 21 | } 22 | let newTradeUpIDS = [] as any 23 | toMoveAlreadyExists.forEach(element => { 24 | newTradeUpIDS.push(element.item_id) 25 | 26 | }); 27 | if (toMoveAlreadyExists.length != 10) { 28 | return { 29 | ...state, 30 | tradeUpProducts: toMoveAlreadyExists, 31 | tradeUpProductsIDS: newTradeUpIDS, 32 | possibleOutcomes: initialState.possibleOutcomes 33 | } 34 | } else { 35 | return { 36 | ...state, 37 | tradeUpProducts: toMoveAlreadyExists, 38 | tradeUpProductsIDS: newTradeUpIDS, 39 | } 40 | } 41 | 42 | case 'TRADEUP_ADDREMOVE_COLLECTION': 43 | let collectionAlreadyExists = state.collections.filter(row => row != action.payload) 44 | if (collectionAlreadyExists.length == state.collections.length) { 45 | collectionAlreadyExists.push(action.payload) 46 | } 47 | return { 48 | ...state, 49 | collections: collectionAlreadyExists 50 | } 51 | 52 | case 'TRADEUP_ADDREMOVE_OPTION': 53 | let optionAlready = state.options.filter(row => row != action.payload) 54 | if (optionAlready.length == state.options.length) { 55 | optionAlready.push(action.payload) 56 | } 57 | return { 58 | ...state, 59 | options: optionAlready 60 | } 61 | case 'TRADEUP_SET_SEARCH': 62 | return { 63 | ...state, 64 | searchInput: action.payload.searchField 65 | } 66 | case 'TRADEUP_SET_MIN': 67 | return { 68 | ...state, 69 | MinFloat: action.payload 70 | } 71 | case 'TRADEUP_SET_MAX': 72 | return { 73 | ...state, 74 | MaxFloat: action.payload 75 | } 76 | case 'TRADEUP_SET_POSSIBLE': 77 | return { 78 | ...state, 79 | possibleOutcomes: action.payload 80 | } 81 | case 'TRADEUP_RESET': 82 | return { 83 | ...initialState, 84 | collections: state.collections 85 | } 86 | 87 | 88 | 89 | case 'SIGN_OUT': 90 | return { 91 | ...initialState 92 | } 93 | 94 | 95 | 96 | default: 97 | return {...state} 98 | 99 | } 100 | }; 101 | 102 | export default tradeUpReducer; 103 | -------------------------------------------------------------------------------- /src/main/helpers/classes/steam/backup/currency.json: -------------------------------------------------------------------------------- 1 | { 2 | "rates": { 3 | "AFN":89.39913, 4 | "ALL":114.2613, 5 | "DZD":146.518, 6 | "AOA":435.8264, 7 | "ARS":122.4815, 8 | "AMD":429.9106, 9 | "AWG":1.8005, 10 | "AUD":1.420905, 11 | "AZN":1.7, 12 | "BSD":1.007131, 13 | "BHD":0.3768777, 14 | "BBD":2.033562, 15 | "BDT":93.65155, 16 | "BZD":2.030139, 17 | "BMD":1, 18 | "BTN":78.35473, 19 | "BOB":6.934398, 20 | "BAM":1.859508, 21 | "BWP":12.15659, 22 | "BRL":4.987385, 23 | "BND":1.391139, 24 | "BGN":1.85946, 25 | "BIF":2071.297, 26 | "XPF":113.69, 27 | "KHR":4091.082, 28 | "CAD":1.27725, 29 | "CVE":104.8346, 30 | "KYD":0.8393231, 31 | "CLP":844.63, 32 | "CLF":0.02571138183, 33 | "CNY":6.7089, 34 | "COP":3879.54, 35 | "CDF":2005, 36 | "CRC":691.5383, 37 | "HRK":7.1518, 38 | "CZK":23.49575, 39 | "DKK":7.075095, 40 | "DJF":179.3021, 41 | "DOP":55.39456, 42 | "XCD":2.70255, 43 | "EGP":18.71327, 44 | "ETB":52.39237, 45 | "FJD":2.1849, 46 | "GMD":54.05, 47 | "GBP":0.8113592, 48 | "GEL":2.915, 49 | "GHS":7.981841, 50 | "GTQ":7.780376, 51 | "GNF":8915.954, 52 | "GYD":210.8291, 53 | "HTG":115.3214, 54 | "HNL":24.74615, 55 | "HKD":7.849185, 56 | "HUF":378.506, 57 | "ISK":131.86, 58 | "INR":78.1435, 59 | "IDR":14614.95, 60 | "IRR":42350, 61 | "IQD":1469.985, 62 | "ILS":3.39429, 63 | "JMD":154.4742, 64 | "JPY":134.282, 65 | "JOD":0.709, 66 | "KZT":439.3801, 67 | "KES":117.8891, 68 | "KWD":0.30675, 69 | "KGS":79.5096, 70 | "LAK":14497.15, 71 | "LBP":1522.818, 72 | "LSL":15.86, 73 | "LRD":152, 74 | "LYD":4.814223, 75 | "MOP":8.142898, 76 | "MKD":58.58053, 77 | "MGA":4083.067, 78 | "MWK":1028.919, 79 | "MYR":4.402, 80 | "MVR":15.86, 81 | "MUR":44.09584, 82 | "MXN":19.948, 83 | "MDL":19.19158, 84 | "MAD":9.982791, 85 | "MZN":63.83, 86 | "MMK":1864.773, 87 | "NAD":15.86, 88 | "NPR":125.3679, 89 | "ANG":1.815174, 90 | "NZD":1.57473, 91 | "NIO":36.10667, 92 | "NGN":415.13, 93 | "NOK":9.7054, 94 | "OMR":0.3849781, 95 | "PKR":203.6998, 96 | "PAB":1.007131, 97 | "PGK":3.594505, 98 | "PYG":6909.679, 99 | "PHP":53.06, 100 | "PLN":4.37749, 101 | "QAR":3.641, 102 | "RON":4.6935, 103 | "RUB":57.6249, 104 | "RWF":1035.976, 105 | "SVC":8.812512, 106 | "SAR":3.7518, 107 | "RSD":111.5497, 108 | "SCR":14.22324, 109 | "SLL":13170, 110 | "SGD":1.386875, 111 | "SBD":8.113698, 112 | "SOS":582, 113 | "ZAR":15.8775, 114 | "KRW":1279.28, 115 | "VES":5.24725, 116 | "LKR":361.07, 117 | "SDG":456.5, 118 | "SRD":21.771, 119 | "SZL":15.71059, 120 | "SEK":10.00588, 121 | "CHF":0.9871805, 122 | "TJS":11.07882, 123 | "TZS":2346.701, 124 | "THB":34.811, 125 | "TOP":2.32435, 126 | "TTD":6.845028, 127 | "TND":3.0695, 128 | "TRY":17.21549, 129 | "TMT":3.5, 130 | "UGX":3716.486, 131 | "UAH":29.75803, 132 | "AED":3.673015, 133 | "USD":1, 134 | "UYU":39.78798, 135 | "UZS":11083.92, 136 | "VND":23182, 137 | "XOF":623.6518, 138 | "YER":250.25, 139 | "ZMW":17.0463, 140 | "ETH":0.0006766998, 141 | "EUR":0.950765, 142 | "LTC":0.01990445, 143 | "TWD":29.662, 144 | "PEN":3.78437 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/renderer/functionsClasses/storageUnits/storageUnitsClass.tsx: -------------------------------------------------------------------------------- 1 | import combineInventory, { 2 | sortDataFunction, 3 | } from 'renderer/components/content/shared/filters/inventoryFunctions'; 4 | import { ItemRow, ItemRowStorage } from 'renderer/interfaces/items'; 5 | import { State } from 'renderer/interfaces/states'; 6 | import { inventorySetFilteredStorage } from 'renderer/store/actions/filtersInventoryActions'; 7 | import { addStorageInventoryData } from 'renderer/store/inventory/inventoryActions'; 8 | import { moveFromAddCasketToStorages } from 'renderer/store/actions/moveFromActions'; 9 | import { filterItemRows } from '../filters/custom'; 10 | import { RequestPrices } from '../prices'; 11 | 12 | export class HandleStorageData { 13 | dispatch: Function; 14 | state: State; 15 | constructor(dispatch: Function, state: State) { 16 | this.dispatch = dispatch; 17 | this.state = state; 18 | } 19 | 20 | async addStorage(storageRow: ItemRow, addArray: Array = []) { 21 | // Adding the casket ID 22 | this.dispatch(moveFromAddCasketToStorages(storageRow.item_id)); 23 | 24 | // Fetch the storage unit data 25 | let storageResult = await this._getStorageUnitData(storageRow); 26 | const ClassRequest = new RequestPrices( 27 | this.dispatch, 28 | this.state.settingsReducer, 29 | this.state.pricingReducer 30 | ); 31 | ClassRequest.handleRequestArray(storageResult.combinedStorages); 32 | if (addArray.length == 0) { 33 | addArray = this.state.inventoryReducer.storageInventory 34 | } 35 | let filteredStorage = await filterItemRows( 36 | [...addArray, ...storageResult.combinedStorages], 37 | this.state.inventoryFiltersReducer.storageFilter 38 | ); 39 | filteredStorage = await sortDataFunction( 40 | this.state.moveFromReducer.sortValue, 41 | filteredStorage, 42 | this.state.pricingReducer.prices, 43 | this.state.settingsReducer?.source?.title 44 | ); 45 | 46 | this.dispatch(inventorySetFilteredStorage(this.state.inventoryFiltersReducer.storageFilter, filteredStorage)) 47 | this.dispatch( 48 | addStorageInventoryData( 49 | storageResult.rawStorages, 50 | storageResult.combinedStorages, 51 | storageRow.item_id, 52 | this.state.moveFromReducer.sortValue 53 | ) 54 | ); 55 | return storageResult.combinedStorages 56 | } 57 | 58 | // Get storage unit 59 | async _getStorageUnitData(storageRow: ItemRow) { 60 | console.log(storageRow.item_id, storageRow.item_customname); 61 | let storageResult = await window.electron.ipcRenderer.getStorageUnitData( 62 | storageRow.item_id, 63 | storageRow.item_customname 64 | ); 65 | let returnData: Array = storageResult[1]; 66 | 67 | let finalReturnData = (await combineInventory( 68 | returnData, 69 | this.state.settingsReducer, 70 | { 71 | storage_id: storageRow.item_id, 72 | storage_name: storageRow.item_customname, 73 | } 74 | )) as Array; 75 | finalReturnData = await sortDataFunction( 76 | this.state.moveFromReducer.sortValue, 77 | finalReturnData, 78 | this.state.pricingReducer.prices, 79 | this.state.settingsReducer?.source?.title 80 | ); 81 | 82 | returnData.forEach((element) => { 83 | element.storage_id = storageRow.item_id; 84 | element.storage_name = storageRow.item_customname as string; 85 | }); 86 | 87 | return { 88 | combinedStorages: finalReturnData, 89 | rawStorages: returnData, 90 | }; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/renderer/components/content/shared/modals & notifcations/notification.tsx: -------------------------------------------------------------------------------- 1 | /* This example requires Tailwind CSS v2.0+ */ 2 | import { Fragment } from 'react'; 3 | import { Transition } from '@headlessui/react'; 4 | import { CheckCircleIcon, XCircleIcon } from '@heroicons/react/outline'; 5 | import { XIcon } from '@heroicons/react/solid'; 6 | import { useSelector } from 'react-redux'; 7 | import { classNames } from '../filters/inventoryFunctions'; 8 | 9 | export default function NotificationElement({ 10 | success, 11 | titleToDisplay, 12 | textToDisplay, 13 | doShow, 14 | setShow, 15 | }) { 16 | const settingsData = useSelector((state: any) => state.settingsReducer); 17 | return ( 18 | <> 19 | {/* Global notification live region, render this permanently at the end of the document */} 20 |
24 |
25 | {/* Notification panel, dynamically insert this into the live region when it needs to be displayed */} 26 | 36 |
37 |
38 |
39 |
40 | {success ? ( 41 |
52 |
53 |

54 | {titleToDisplay} 55 |

56 |

57 | {textToDisplay} 58 |

59 |
60 |
61 | 70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | 78 | ); 79 | } 80 | -------------------------------------------------------------------------------- /src/renderer/views/overview/filter/optionsDropdown.tsx: -------------------------------------------------------------------------------- 1 | import { Fragment } from 'react' 2 | import { Popover, Transition } from '@headlessui/react' 3 | import { useDispatch, useSelector } from 'react-redux'; 4 | import { tradeUpOptionsAddRemove } from 'renderer/store/actions/tradeUpActions'; 5 | 6 | let optionsAvailable = ['Hide equipped'] 7 | 8 | export default function TradeUpOptionsDropDown() { 9 | 10 | const tradeUpData = useSelector((state: any) => state.tradeUpReducer); 11 | const dispatch = useDispatch(); 12 | 13 | 14 | return ( 15 | 16 | 17 | 18 | Options 19 | 20 | {tradeUpData.options.length} 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 37 | 38 |
39 | {optionsAvailable.map((option, optionIdx) => ( 40 |
41 | dispatch(tradeUpOptionsAddRemove(option))} 49 | /> 50 | 56 |
57 | ))} 58 |
59 |
60 |
61 |
62 |
63 | ) 64 | } -------------------------------------------------------------------------------- /src/renderer/views/tradeUp/filter/optionsDropdown.tsx: -------------------------------------------------------------------------------- 1 | import { Fragment } from 'react' 2 | import { Popover, Transition } from '@headlessui/react' 3 | import { useDispatch, useSelector } from 'react-redux'; 4 | import { tradeUpOptionsAddRemove } from 'renderer/store/actions/tradeUpActions'; 5 | 6 | let optionsAvailable = ['Hide equipped'] 7 | 8 | export default function TradeUpOptionsDropDown() { 9 | 10 | const tradeUpData = useSelector((state: any) => state.tradeUpReducer); 11 | const dispatch = useDispatch(); 12 | 13 | 14 | return ( 15 | 16 | 17 | 18 | Options 19 | 20 | {tradeUpData.options.length} 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 37 | 38 |
39 | {optionsAvailable.map((option, optionIdx) => ( 40 |
41 | dispatch(tradeUpOptionsAddRemove(option))} 49 | /> 50 | 56 |
57 | ))} 58 |
59 |
60 |
61 |
62 |
63 | ) 64 | } -------------------------------------------------------------------------------- /src/renderer/views/overview/charts/pieChart.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Chart as ChartJS, 3 | RadialLinearScale, 4 | PointElement, 5 | LineElement, 6 | Filler, 7 | Tooltip, 8 | Legend, 9 | LinearScale, 10 | } from 'chart.js'; 11 | import { Pie } from 'react-chartjs-2'; 12 | import ChartDataLabels from 'chartjs-plugin-datalabels'; 13 | import { ConvertPricesFormatted } from 'renderer/functionsClasses/prices'; 14 | import { ReducerManager } from 'renderer/functionsClasses/reducerManager'; 15 | import { useSelector } from 'react-redux'; 16 | import { Prices, Settings } from 'renderer/interfaces/states'; 17 | 18 | ChartJS.register( 19 | RadialLinearScale, 20 | PointElement, 21 | LinearScale, 22 | LineElement, 23 | Filler, 24 | Tooltip, 25 | Legend 26 | ); 27 | 28 | export default function PieChart({ data, headerName }) { 29 | const ReducerClass = new ReducerManager(useSelector); 30 | let settingsData: Settings = ReducerClass.getStorage( 31 | ReducerClass.names.settings 32 | ); 33 | let pricingData: Prices = ReducerClass.getStorage(ReducerClass.names.pricing); 34 | const converter = new ConvertPricesFormatted(settingsData, pricingData); 35 | 36 | const title = (tooltipItems) => { 37 | let percentageData: Array = []; 38 | let sum = 0; 39 | 40 | tooltipItems.dataset.data.forEach((element) => { 41 | sum += element; 42 | }); 43 | tooltipItems.dataset.data.forEach((element) => { 44 | let percentage = (element * 100) / sum; 45 | percentageData.push(percentage); 46 | }); 47 | if (settingsData.overview.by == 'price') { 48 | return ( 49 | tooltipItems.label + ': ' + converter.formatPrice(tooltipItems.raw) + ' - ' + percentageData[tooltipItems.dataIndex].toFixed(2) + '%' 50 | ); 51 | } 52 | return tooltipItems.label + ': ' + tooltipItems.raw + ' - ' + percentageData[tooltipItems.dataIndex].toFixed(2) + '%' 53 | }; 54 | 55 | // Radar options 56 | const options = { 57 | plugins: { 58 | legend: { 59 | labels: { 60 | position: 'right', 61 | color: '#d6d3cd', 62 | }, 63 | }, 64 | title: { 65 | display: true, 66 | text: headerName, 67 | color: '#d6d3cd', 68 | }, 69 | tooltip: { 70 | callbacks: { 71 | label: title, 72 | }, 73 | }, 74 | datalabels: { 75 | formatter: (value, ctx) => { 76 | let sum = 0; 77 | let dataArr = ctx.chart.data.datasets[0].data; 78 | dataArr.map((data) => { 79 | sum += data; 80 | }); 81 | let percentage = ((value * 100) / sum).toFixed(2) + '%'; 82 | return percentage; 83 | }, 84 | color: '#fff', 85 | display: function (context) { 86 | let percentageData: Array = []; 87 | let sum = 0; 88 | 89 | context.dataset.data.forEach((element) => { 90 | sum += element; 91 | }); 92 | context.dataset.data.forEach((element) => { 93 | let percentage = (element * 100) / sum; 94 | percentageData.push(percentage); 95 | }); 96 | 97 | if (percentageData[context.dataIndex] > 4) { 98 | return true; 99 | } 100 | if (percentageData[context.dataIndex] > 2) { 101 | return 'auto'; 102 | } 103 | return false; 104 | 105 | }, 106 | }, 107 | }, 108 | }; 109 | 110 | return ( 111 | <> 112 | 113 | 116 | 117 | ); 118 | } 119 | -------------------------------------------------------------------------------- /src/renderer/views/overview/radarChart copy.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Chart as ChartJS, 3 | RadialLinearScale, 4 | PointElement, 5 | LineElement, 6 | Filler, 7 | Tooltip, 8 | Legend, 9 | LinearScale, 10 | } from 'chart.js'; 11 | import { Radar } from 'react-chartjs-2'; 12 | import { useSelector } from 'react-redux'; 13 | import {itemCategories} from 'renderer/components/content/shared/categories'; 14 | 15 | ChartJS.register( 16 | RadialLinearScale, 17 | PointElement, 18 | LinearScale, 19 | LineElement, 20 | Filler, 21 | Tooltip, 22 | Legend 23 | ); 24 | 25 | 26 | export default function RadarApp() { 27 | let categoriesFixed: Array = []; 28 | 29 | let resultingData = {} as any; 30 | itemCategories.forEach((element) => { 31 | categoriesFixed.push(element.name); 32 | resultingData[element.name] = { 33 | inventory: 0, 34 | storageUnits: 0 35 | } 36 | }); 37 | 38 | // Radar options 39 | const options = { 40 | plugins: { 41 | legend: { 42 | labels: { 43 | color: '#d6d3cd', 44 | }, 45 | 46 | }, 47 | title: { 48 | display: true, 49 | text: 'Items', 50 | color: '#d6d3cd' 51 | } 52 | }, 53 | parsing: { 54 | key: 'nested.value', 55 | }, 56 | legend: { 57 | position: 'top', 58 | labels: { 59 | fontColor: 'white', 60 | }, 61 | }, 62 | title: { 63 | display: true, 64 | text: 'Chart.js Radar Chart', 65 | fontColor: 'white', 66 | }, 67 | scales: { 68 | r: { 69 | angleLines: { 70 | color: 'rgba(255, 255, 255, 0.2)', 71 | }, 72 | 73 | grid: { 74 | color: 'rgba(255, 255, 255, 0.2)', 75 | }, 76 | ticks: { 77 | color: 'white', 78 | showLabelBackdrop: false, 79 | }, 80 | }, 81 | }, 82 | }; 83 | 84 | // Go through inventory and find matching categories 85 | const inventory = useSelector((state: any) => state.inventoryReducer); 86 | inventory.combinedInventory.forEach(element => { 87 | if (resultingData[element.category]) { 88 | resultingData[element.category].inventory = resultingData?.[element.category]?.inventory + element.combined_QTY 89 | } 90 | }); 91 | 92 | // Go through Storage Units 93 | inventory.storageInventory.forEach(element => { 94 | console.log(element) 95 | if (resultingData[element.category]) { 96 | resultingData[element.category].storageUnits = resultingData?.[element.category]?.storageUnits + element.combined_QTY 97 | } 98 | }); 99 | 100 | // Convert inventory to chart data 101 | let inventoryDataToUse: Array = []; 102 | let storageUnitDataToUse: Array = []; 103 | 104 | categoriesFixed.forEach(category => { 105 | inventoryDataToUse.push(resultingData[category].inventory) 106 | storageUnitDataToUse.push(resultingData[category].storageUnits) 107 | }); 108 | console.log(storageUnitDataToUse) 109 | 110 | 111 | const data = { 112 | labels: categoriesFixed, 113 | 114 | datasets: [ 115 | { 116 | label: 'Inventory', 117 | data: inventoryDataToUse, 118 | backgroundColor: 'rgba(255, 99, 132, 0.2)', 119 | borderColor: 'rgba(255, 99, 132, 1)', 120 | borderWidth: 1, 121 | }, 122 | { 123 | label: 'Storage Units', 124 | data: storageUnitDataToUse, 125 | backgroundColor: 'rgb(50, 91, 136, 0.2)', 126 | borderColor: 'rgb(50, 91, 136, 1)', 127 | borderWidth: 1, 128 | }, 129 | 130 | ], 131 | }; 132 | 133 | return ( 134 | <> 135 | 136 | 137 | ); 138 | } 139 | -------------------------------------------------------------------------------- /src/renderer/store/reducer/moveFromReducers.tsx: -------------------------------------------------------------------------------- 1 | import { MoveFromReducer } from "renderer/interfaces/states"; 2 | 3 | const initialState: MoveFromReducer = { 4 | hideFull: false, 5 | activeStorages: [], 6 | totalToMove: [], 7 | totalItemsToMove: 0, 8 | searchInput: '', 9 | searchInputStorage: '', 10 | sortValue: 'Default', 11 | doCancel: false, 12 | sortBack: false, 13 | }; 14 | 15 | const moveFromReducer = (state = initialState, action) => { 16 | switch (action.type) { 17 | case 'MOVE_FROM_SET_FULL': 18 | return { 19 | ...state, 20 | hideFull: !state.hideFull 21 | } 22 | case 'MOVE_FROM_SET_SORT_BACK': 23 | return { 24 | ...state, 25 | sortBack: !state.sortBack 26 | } 27 | case 'MOVE_FROM_ADD_TO': 28 | // Add to or remove from active storages 29 | let casketAlreadyExists = state.activeStorages.indexOf(action.payload.casketID) > -1; 30 | let chosenActiveCopy = state.activeStorages.slice(); 31 | 32 | if (casketAlreadyExists) { 33 | chosenActiveCopy = chosenActiveCopy.filter(id => id != action.payload.casketID) 34 | } else { 35 | chosenActiveCopy.push(action.payload.casketID) 36 | } 37 | return { 38 | ...state, 39 | activeStorages: chosenActiveCopy 40 | } 41 | case 'MOVE_FROM_CLEAR': 42 | return { 43 | ...initialState 44 | } 45 | case 'MOVE_FROM_TOTAL_TO_ADD': 46 | let toMoveAlreadyExists = state.totalToMove.filter(row => row[0] != action.payload.itemID) 47 | 48 | if (action.payload.toMove.length > 0) { 49 | toMoveAlreadyExists.push([action.payload.itemID, action.payload.casketID, action.payload.toMove, action.payload.itemName]) 50 | } 51 | let newTotalItemsToMove = 0 52 | toMoveAlreadyExists.forEach(element => { 53 | newTotalItemsToMove += element[2].length 54 | }); 55 | return { 56 | ...state, 57 | totalToMove: toMoveAlreadyExists, 58 | totalItemsToMove: newTotalItemsToMove 59 | } 60 | case 'MOVE_FROM_ALL_CASKET_RESULTS': 61 | let allCasketResults = state.totalToMove.filter(row => row[1] != action.payload.casketID) 62 | 63 | let allCasketToRemoveTotal = 0 64 | allCasketResults.forEach(element => { 65 | allCasketToRemoveTotal += element[2].length 66 | }); 67 | return { 68 | ...state, 69 | totalToMove: allCasketResults, 70 | totalItemsToMove: allCasketToRemoveTotal 71 | } 72 | 73 | case 'MOVE_FROM_SET_SEARCH': 74 | return { 75 | ...state, 76 | searchInput: action.payload.searchField 77 | } 78 | case 'MOVE_FROM_SET_SEARCH_STORAGE': 79 | return { 80 | ...state, 81 | searchInputStorage: action.payload.searchField 82 | } 83 | case 'SET_SORT': 84 | if (state.sortValue == action.payload.sortValue) { 85 | return { 86 | ...state, 87 | sortBack: !state.sortBack 88 | } 89 | } else { 90 | return { 91 | ...state, 92 | sortValue: action.payload.sortValue, 93 | sortBack: initialState.sortBack 94 | } 95 | } 96 | case 'MOVE_FROM_CLEAR_ALL': 97 | return { 98 | ...state, 99 | totalToMove: [] as any, 100 | totalItemsToMove: 0, 101 | searchInput: '', 102 | sortValue: 'Default' 103 | } 104 | 105 | case 'DO_CANCEL': 106 | return { 107 | ...state, 108 | doCancel: action.payload.doCancel 109 | } 110 | case 'SIGN_OUT': 111 | return { 112 | ...initialState 113 | } 114 | 115 | 116 | 117 | default: 118 | return { ...state } 119 | 120 | } 121 | }; 122 | 123 | export default moveFromReducer; 124 | -------------------------------------------------------------------------------- /src/renderer/store/reducer/moveToReducers.tsx: -------------------------------------------------------------------------------- 1 | import { MoveToReducer } from "renderer/interfaces/states"; 2 | 3 | const initialState: MoveToReducer = { 4 | doHide: false, 5 | hideFull: true, 6 | activeStorages: [], 7 | activeStoragesAmount: 0, 8 | totalToMove: [], 9 | totalItemsToMove: 0, 10 | searchInput: '', 11 | searchInputStorage: '', 12 | sortValue: 'Default', 13 | doCancel: false, 14 | sortBack: false, 15 | }; 16 | 17 | const moveFromReducer = (state = initialState, action) => { 18 | switch (action.type) { 19 | case 'MOVE_TO_SET_HIDE': 20 | return { 21 | ...state, 22 | doHide: !state.doHide 23 | } 24 | 25 | case 'MOVE_TO_SET_FULL': 26 | return { 27 | ...state, 28 | hideFull: !state.hideFull 29 | } 30 | case 'MOVE_TO_ADD_TO': 31 | // Add to or remove from active storages 32 | console.log(action.payload.casketID) 33 | let casketAlreadyExists = state.activeStorages.indexOf(action.payload.casketID) > -1; 34 | let chosenActiveCopy = state.activeStorages.slice(); 35 | let storageAmount = 0 36 | if (casketAlreadyExists) { 37 | chosenActiveCopy = [] 38 | } else { 39 | chosenActiveCopy = [action.payload.casketID] 40 | storageAmount = action.payload.casketVolume 41 | } 42 | console.log(chosenActiveCopy) 43 | return { 44 | ...state, 45 | activeStorages: chosenActiveCopy, 46 | activeStoragesAmount: storageAmount 47 | 48 | } 49 | case 'MOVE_TO_TOTAL_TO_ADD': 50 | let toMoveAlreadyExists = state.totalToMove.filter(row => row[0] != action.payload.itemID) 51 | 52 | if (action.payload.toMove.length > 0) { 53 | toMoveAlreadyExists.push([action.payload.itemID, action.payload.casketID, action.payload.toMove, action.payload.itemName]) 54 | } 55 | let newTotalItemsToMove = 0 56 | toMoveAlreadyExists.forEach(element => { 57 | newTotalItemsToMove += element[2].length 58 | }); 59 | return { 60 | ...state, 61 | totalToMove: toMoveAlreadyExists, 62 | totalItemsToMove: newTotalItemsToMove 63 | } 64 | 65 | case 'SET_STORAGE_AMOUNT': 66 | return { 67 | ...state, 68 | activeStoragesAmount: action.payload.storageAmount 69 | } 70 | 71 | case 'MOVE_TO_SET_SEARCH': 72 | return { 73 | ...state, 74 | searchInput: action.payload.searchField 75 | } 76 | 77 | case 'MOVE_TO_SET_SEARCH_STORAGE': 78 | return { 79 | ...state, 80 | searchInputStorage: action.payload.searchField 81 | } 82 | case 'SET_SORT': 83 | if (state.sortValue == action.payload.sortValue) { 84 | return { 85 | ...state, 86 | sortBack: !state.sortBack 87 | } 88 | } else { 89 | return { 90 | ...state, 91 | sortValue: action.payload.sortValue, 92 | sortBack: initialState.sortBack 93 | } 94 | } 95 | case 'MOVE_TO_CLEAR_ALL': 96 | return { 97 | ...state, 98 | totalToMove: [] as any, 99 | totalItemsToMove: 0, 100 | searchInput: '', 101 | sortValue: 'Default' 102 | } 103 | case 'DO_CANCEL': 104 | 105 | return { 106 | ...state, 107 | doCancel: action.payload.doCancel 108 | } 109 | case 'SIGN_OUT': 110 | return { 111 | ...initialState 112 | } 113 | 114 | 115 | 116 | default: 117 | return {...state} 118 | 119 | } 120 | }; 121 | 122 | export default moveFromReducer; 123 | -------------------------------------------------------------------------------- /src/renderer/views/overview/overviewOptionsDropdown.tsx: -------------------------------------------------------------------------------- 1 | /* This example requires Tailwind CSS v2.0+ */ 2 | import { Fragment } from 'react' 3 | import { Listbox, Transition } from '@headlessui/react' 4 | import { CheckIcon, SelectorIcon } from '@heroicons/react/solid' 5 | import { classNames } from 'renderer/components/content/shared/filters/inventoryFunctions' 6 | import { Overview, Settings } from 'renderer/interfaces/states' 7 | import { useDispatch, useSelector } from 'react-redux' 8 | import { ReducerManager } from 'renderer/functionsClasses/reducerManager' 9 | import { setOverview } from 'renderer/store/actions/settings' 10 | 11 | interface params { 12 | optionsObject: any 13 | keyToUse: keyof Overview 14 | } 15 | 16 | export default function ListBoxOptions({optionsObject, keyToUse}: params) { 17 | 18 | const dispatch = useDispatch(); 19 | const ReducerClass = new ReducerManager(useSelector); 20 | const settingsData: Settings = ReducerClass.getStorage(ReducerClass.names.settings) 21 | let selected = settingsData.overview[keyToUse] 22 | 23 | async function updateOverview(valueToset: any) { 24 | let newOverviewValue: Overview = settingsData.overview 25 | // @ts-ignore 26 | newOverviewValue[keyToUse] = valueToset 27 | 28 | dispatch(setOverview(newOverviewValue)); 29 | window.electron.store.set('overview', newOverviewValue); 30 | window.electron.ipcRenderer.refreshInventory(); 31 | } 32 | 33 | return ( 34 | 35 | {({ open }) => ( 36 | <> 37 |
38 | 39 | {optionsObject[selected]} 40 | 41 | 43 | 44 | 45 | 52 | 53 | {Object.entries(optionsObject).map(([key, name]: any) => ( 54 | 57 | classNames( 58 | active ? 'bg-dark-level-four' : '', 59 | 'cursor-default select-none text-dark-white relative py-2 pl-3 pr-9' 60 | ) 61 | } 62 | value={key} 63 | > 64 | {({ selected, active }) => ( 65 | <> 66 | 67 | {name} 68 | 69 | 70 | {selected ? ( 71 | 77 | 79 | ) : null} 80 | 81 | )} 82 | 83 | ))} 84 | 85 | 86 |
87 | 88 | )} 89 |
90 | ) 91 | } 92 | -------------------------------------------------------------------------------- /src/renderer/store/actions/filtersInventoryActions.tsx: -------------------------------------------------------------------------------- 1 | import { sortDataFunction } from "renderer/components/content/shared/filters/inventoryFunctions" 2 | import { Filter } from "renderer/interfaces/filters" 3 | import { State } from "renderer/interfaces/states" 4 | import _ from 'lodash'; 5 | import { filterItemRows } from "renderer/functionsClasses/filters/custom"; 6 | 7 | export const allButClear = (filterString: any, sortValue, inventoryFiltered) => { 8 | return { 9 | type: 'ALL_BUT_CLEAR', 10 | payload: { 11 | inventoryFilter: filterString, 12 | sortValue: sortValue, 13 | inventoryFiltered: inventoryFiltered 14 | 15 | } 16 | } 17 | } 18 | export const inventorySetFilteredStorage = (storageFilter, storageFiltered) => { 19 | return { 20 | type: 'SET_FILTERED_STORAGE', 21 | payload: { 22 | storageFiltered, 23 | storageFilter 24 | 25 | } 26 | } 27 | } 28 | export const inventorySetFilter = (inventoryFilter: any, sortValue, inventoryFiltered) => { 29 | return { 30 | type: 'SET_FILTERED', 31 | payload: { 32 | inventoryFilter, 33 | sortValue, 34 | inventoryFiltered 35 | } 36 | } 37 | } 38 | export const filterInventoryClearAll = () => { 39 | return { 40 | type: 'CLEAR_ALL' 41 | } 42 | } 43 | export const inventoryFilterSetSearch = (searchField) => { 44 | return { 45 | type: 'INVENTORY_FILTERS_SET_SEARCH', 46 | payload: { 47 | searchField: searchField 48 | } 49 | } 50 | } 51 | 52 | export const inventoryAddCategoryFilter = (filterToAdd) => { 53 | return { 54 | type: 'INVENTORY_ADD_CATEGORY_FILTER', 55 | payload: filterToAdd 56 | } 57 | } 58 | export const inventoryAddRarityFilter = (filterToAdd) => { 59 | return { 60 | type: 'INVENTORY_ADD_RARITY_FILTER', 61 | payload: filterToAdd 62 | } 63 | } 64 | 65 | export async function storageInventoryAddOption(currentState: State, newFilter: Filter) { 66 | let newFilterState = [] as Array; 67 | let wasSeen: boolean = false; 68 | currentState.inventoryFiltersReducer.storageFilter.forEach(element => { 69 | if (!_.isEqual(element, newFilter)) { 70 | newFilterState.push(element) 71 | 72 | } else { 73 | wasSeen = true; 74 | } 75 | }); 76 | 77 | if (!wasSeen) { 78 | newFilterState.push(newFilter) 79 | } 80 | 81 | let filteredStorage = await filterItemRows(currentState.inventoryReducer.storageInventory, newFilterState) 82 | filteredStorage = await sortDataFunction(currentState.moveFromReducer.sortValue, filteredStorage, currentState.pricingReducer.prices, currentState.settingsReducer?.source?.title) 83 | return inventorySetFilteredStorage(newFilterState, filteredStorage) 84 | } 85 | 86 | export async function filterInventoryAddOption(currentState: State, newFilter: Filter) { 87 | let newFilterState = [] as Array; 88 | let wasSeen: boolean = false; 89 | currentState.inventoryFiltersReducer.inventoryFilter.forEach(element => { 90 | if (!_.isEqual(element, newFilter)) { 91 | newFilterState.push(element) 92 | 93 | } else { 94 | wasSeen = true; 95 | } 96 | }); 97 | 98 | if (!wasSeen) { 99 | newFilterState.push(newFilter) 100 | } 101 | let filteredInv = await filterItemRows(currentState.inventoryReducer.combinedInventory, newFilterState) 102 | filteredInv = await sortDataFunction(currentState.inventoryFiltersReducer.sortValue, filteredInv, currentState.pricingReducer.prices, currentState.settingsReducer?.source?.title) 103 | return inventorySetFilter(newFilterState, currentState.inventoryFiltersReducer.sortValue, filteredInv) 104 | } 105 | 106 | export async function filterInventorySetSort(currentState: State, newSort: string) { 107 | let inventoryData = sortDataFunction(newSort, currentState.inventoryReducer.inventory, currentState.pricingReducer.prices, currentState.settingsReducer?.source?.title) 108 | return allButClear(currentState.inventoryFiltersReducer.inventoryFilter, newSort, inventoryData) 109 | } 110 | -------------------------------------------------------------------------------- /src/renderer/components/content/shared/modals & notifcations/modalTradeResult.tsx: -------------------------------------------------------------------------------- 1 | import { Fragment } from 'react'; 2 | import { Dialog, Transition } from '@headlessui/react'; 3 | import { useDispatch, useSelector } from 'react-redux'; 4 | import { setTradeMoveResult } from 'renderer/store/actions/modalTrade'; 5 | import { tradeUpResetPossible } from 'renderer/store/actions/tradeUpActions'; 6 | import { createCSGOImage } from '../../../../functionsClasses/createCSGOImage'; 7 | 8 | export default function TradeResultModal() { 9 | const dispatch = useDispatch(); 10 | const modalData = useSelector((state: any) => state.modalTradeReducer); 11 | 12 | let devMode = false; 13 | 14 | async function setDone() { 15 | dispatch(setTradeMoveResult()) 16 | dispatch(tradeUpResetPossible()) 17 | } 18 | 19 | 20 | return ( 21 | 22 | dispatch(setTradeMoveResult())} 26 | > 27 |
28 | 37 | 38 | 39 | 40 | {/* This element is to trick the browser into centering the modal contents. */} 41 | 47 | 56 |
57 |
58 |
59 |
65 |
66 | 68 | {modalData.rowToMatch.item_name} 69 | 70 |
71 | Trade Up Contract Reward 72 |
73 |
74 |
75 | 76 |
77 | 84 |
85 |
86 |
87 |
88 |
89 |
90 | ); 91 | } 92 | -------------------------------------------------------------------------------- /src/renderer/store/inventory/inventoryClass.tsx: -------------------------------------------------------------------------------- 1 | import { Inventory } from "renderer/interfaces/states"; 2 | import { InventoryMatchingObject } from "./inventoryInterfaces"; 3 | 4 | const initialState: Inventory = { 5 | inventory: [], 6 | combinedInventory: [], 7 | storageInventory: [], 8 | storageInventoryRaw: [], 9 | totalAccountItems: 0, 10 | itemsLookUp: {} 11 | }; 12 | 13 | 14 | export class InventoryActionsReducer { 15 | 16 | matchingObject: InventoryMatchingObject = { 17 | 'INVENTORY_SET_INVENTORY': this.setInventory, 18 | 'INVENTORY_STORAGES_ADD_TO': this.addStorageUnitsItems, 19 | 'INVENTORY_STORAGES_CLEAR_CASKET': this.clearStorageUnitItems, 20 | 'INVENTORY_STORAGES_SET_SORT_STORAGES': this.setSortStorageUnits, 21 | 'INVENTORY_STORAGES_CLEAR_ALL': this.clearAllStorageUnits, 22 | 'MOVE_FROM_CLEAR': this.clearAllStorageUnits, 23 | 'SIGN_OUT': this.initialState 24 | 25 | } 26 | relevantFunction: Function 27 | state: Inventory 28 | action 29 | 30 | constructor(state = initialState, action: any) { 31 | this.relevantFunction = this.default 32 | if (action.type in this.matchingObject) { 33 | this.relevantFunction = this.matchingObject[action.type] 34 | } 35 | this.state = state 36 | this.action = action 37 | } 38 | 39 | // Default 40 | default() { 41 | return { 42 | ...this.state 43 | } 44 | } 45 | 46 | // Initial state 47 | initialState() { 48 | return { 49 | ...initialState 50 | } 51 | } 52 | 53 | 54 | 55 | // Set the inventory whenever it changes 56 | setInventory() { 57 | let storageTotal = 0 58 | 59 | this.action.payload.inventory.forEach(element => { 60 | storageTotal += 1 61 | if (element.item_url == "econ/tools/casket") { 62 | storageTotal += element.item_storage_total 63 | } 64 | }); 65 | 66 | 67 | 68 | return { 69 | ...this.state, 70 | inventory: this.action.payload.inventory, 71 | combinedInventory: this.action.payload.combinedInventory, 72 | totalAccountItems: storageTotal 73 | } 74 | } 75 | 76 | // Add storage unit items 77 | addStorageUnitsItems() { 78 | const add_to_filtered = this.state.storageInventory?.filter(id => id.storage_id != this.action.payload.casketID) || [] 79 | const add_to_filtered_raw = this.state.storageInventoryRaw?.filter(id => id.storage_id != this.action.pay) || [] 80 | this.action.payload.storageData.forEach(storageRow => add_to_filtered.push(storageRow)) 81 | this.action.payload.storageRowsRaw.forEach(storageRow => { 82 | add_to_filtered_raw.push(storageRow) 83 | }) 84 | 85 | return { 86 | ...this.state, 87 | storageInventory: add_to_filtered, 88 | storageInventoryRaw: add_to_filtered_raw 89 | } 90 | } 91 | 92 | // Clear a caskets storage unit items 93 | clearStorageUnitItems() { 94 | const AddToFiltered = this.state.storageInventory.filter(id => id.storage_id != this.action.payload.casketID) 95 | const AddToFilteredRaw = this.state.storageInventoryRaw.filter(id => id.storage_id != this.action.payload.casketID) 96 | return { 97 | ...this.state, 98 | storageInventory: AddToFiltered, 99 | storageInventoryRaw: AddToFilteredRaw 100 | } 101 | } 102 | 103 | // Set storage unit sort 104 | setSortStorageUnits() { 105 | return { 106 | ...this.state, 107 | storageInventory: this.action.payload.storageData 108 | } 109 | } 110 | 111 | // Clear all storage units 112 | clearAllStorageUnits() { 113 | return { 114 | ...this.state, 115 | storageInventory: initialState.storageInventory, 116 | storageInventoryRaw: initialState.storageInventoryRaw 117 | } 118 | } 119 | 120 | } 121 | 122 | export function inventoryReducer(state = initialState, action) { 123 | return new InventoryActionsReducer(state, action).relevantFunction() 124 | } 125 | 126 | --------------------------------------------------------------------------------