├── .env ├── Procfile ├── .gitignore ├── image.png ├── types ├── h2m.d.ts └── reactn.d.ts ├── renderer.tsx ├── index.html ├── src ├── components │ ├── Counter.js │ ├── DownloadBar.tsx │ ├── TitleBar.jsx │ ├── ManifestListItem.tsx │ ├── ManifestEditorJson.tsx │ ├── CampaignListItem.tsx │ ├── NavBar.tsx │ ├── CampaignList.tsx │ ├── ManifestList.tsx │ ├── ManifestEditorScreenshotItem.tsx │ ├── ManifestEditorMapItem.tsx │ ├── SettingsForm.tsx │ ├── CampaignDetails.tsx │ └── ManifestEditor.tsx ├── constants │ ├── ipcmessages.d.ts │ └── ipcmessages.js ├── store │ ├── config │ │ ├── actions.ts │ │ ├── types.ts │ │ └── reducer.ts │ ├── campaign │ │ ├── middleware.ts │ │ ├── selectors.ts │ │ ├── types.ts │ │ ├── actions.ts │ │ └── reducer.ts │ ├── index.ts │ └── middleware.ts ├── configureStore.ts ├── App.tsx ├── classes │ ├── Platform.ts │ ├── Mapster.ts │ ├── Config.ts │ ├── Campaign.ts │ └── Downloader.ts ├── containers │ ├── ScrapperPane.tsx │ ├── SettingsPane.tsx │ ├── MapMakerPane.tsx │ └── CampaignPane.tsx ├── App.scss └── pages │ ├── InitialSetup.tsx │ └── Home.tsx ├── .babelrc ├── tsconfig.json ├── .vscode └── tasks.json ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── README.md ├── webpack.config.js ├── public └── sources.json ├── package.json ├── CODE_OF_CONDUCT.md ├── electron ├── Download.js └── main.js ├── LICENSE.md └── main.js /.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=dev 2 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | electron: npm run electron 2 | dev: npm run dev 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | dist/ 3 | bundle.js 4 | *.swp[a-z] 5 | node_modules -------------------------------------------------------------------------------- /image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abrahamYG/sc2-campaign-manager/HEAD/image.png -------------------------------------------------------------------------------- /types/h2m.d.ts: -------------------------------------------------------------------------------- 1 | export = index; 2 | declare type H2MOptions = { 3 | converter?:any, 4 | overrides?:any 5 | } 6 | declare function index(html: string, options?: H2MOptions): string; 7 | -------------------------------------------------------------------------------- /renderer.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import * as ReactDOM from "react-dom"; 3 | import App from "./src/App"; 4 | 5 | ReactDOM.render(, 6 | document.getElementById("root") 7 | ); 8 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | StarCraft 2 Campaign Manager 5 | 6 | 7 |
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/components/Counter.js: -------------------------------------------------------------------------------- 1 | import React from 'reactn'; 2 | import { useGlobal } from 'reactn'; 3 | 4 | export default function Counter() { 5 | 6 | const [number, setNumber] = useGlobal("number"); 7 | 8 | return ( 9 | 12 | ); 13 | } 14 | 15 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["react-hot-loader/babel","@babel/plugin-proposal-class-properties","@babel/plugin-transform-react-jsx"], 3 | /* 4 | "presets":[ 5 | [ 6 | "@babel/env", 7 | { 8 | "targets": { 9 | "browsers": ["last 2 Chrome versions"] 10 | } 11 | } 12 | ], 13 | "@babel/preset-react", 14 | //"flow" 15 | ], 16 | */ 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/constants/ipcmessages.d.ts: -------------------------------------------------------------------------------- 1 | declare interface IPCMessages { 2 | DOWNLOAD_MAP:string; 3 | DOWNLOAD_MAP_STATUS:string; 4 | DOWNLOAD_CAMPAIGN:string; 5 | DOWNLOAD_CAMPAIGN_STATUS:string; 6 | DOWNLOAD_CAMPAIGN_FINISH:string; 7 | PLAY_CAMPAIGN:string; 8 | PLAY_MAP:string; 9 | UNZIP_CAMPAIGN:string; 10 | 11 | } 12 | 13 | declare const ipcmessages:IPCMessages; 14 | 15 | export = ipcmessages; -------------------------------------------------------------------------------- /src/constants/ipcmessages.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | DOWNLOAD_MAP: "downloadMap", 3 | DOWNLOAD_MAP_STATUS: "downloadMapProgress", 4 | DOWNLOAD_CAMPAIGN: "downloadCampaign", 5 | UNZIP_CAMPAIGN: "unzipCampaign", 6 | DOWNLOAD_CAMPAIGN_STATUS: "downloadCampaignProgress", 7 | DOWNLOAD_CAMPAIGN_FINISH: "downloadCampaignProgress", 8 | PLAY_CAMPAIGN: "playCampaign", 9 | PLAY_MAP: "playMap", 10 | 11 | } -------------------------------------------------------------------------------- /src/store/config/actions.ts: -------------------------------------------------------------------------------- 1 | import {GET_CONFIG, SET_CONFIG, ConfigActionTypes} from "./types"; 2 | 3 | import {IConfig} from "../../classes/Config" 4 | 5 | 6 | export const getConfig = (config:IConfig):ConfigActionTypes => ({ 7 | type: GET_CONFIG, 8 | payload: config 9 | }); 10 | 11 | export const setConfig = (config:IConfig):ConfigActionTypes => { 12 | return ({ 13 | type: SET_CONFIG, 14 | payload: config 15 | }) 16 | }; 17 | 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { "*": ["types/*"] }, 5 | "outDir": "./dist/", 6 | "sourceMap": true, 7 | "allowSyntheticDefaultImports": true, 8 | "resolveJsonModule":true, 9 | "esModuleInterop": true, 10 | "noImplicitAny": true, 11 | "module": "commonJS", 12 | "target": "es6", 13 | "jsx": "react", 14 | }, 15 | "include": [ 16 | "./src/**/*" 17 | ] 18 | } -------------------------------------------------------------------------------- /src/store/config/types.ts: -------------------------------------------------------------------------------- 1 | import { IConfig } from '../../classes/Config'; 2 | 3 | export const GET_CONFIG = "GET_CONFIG"; 4 | export const SET_CONFIG = "SET_CONFIG"; 5 | 6 | export interface IConfigState { 7 | config:IConfig 8 | } 9 | 10 | interface SetConfigAction { 11 | type: typeof SET_CONFIG 12 | payload: IConfig 13 | } 14 | 15 | 16 | interface GetConfigAction { 17 | type: typeof GET_CONFIG 18 | payload: IConfig 19 | } 20 | export type ConfigActionTypes = SetConfigAction | GetConfigAction -------------------------------------------------------------------------------- /src/components/DownloadBar.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | 4 | export default function DownloadBar(props:any){ 5 | const percent = props.progress*100; 6 | return(/* 7 |
8 |
{percent}% Downloading
17 |
18 | */ 19 | 20 | ); 21 | } -------------------------------------------------------------------------------- /src/components/TitleBar.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import '@fortawesome/fontawesome-free/scss/fontawesome.scss' 3 | 4 | function TitleBar(props) { 5 | return ( 6 | 16 | ); 17 | } 18 | 19 | 20 | export default TitleBar; -------------------------------------------------------------------------------- /src/configureStore.ts: -------------------------------------------------------------------------------- 1 | import { createStore,compose, applyMiddleware } from "redux"; 2 | import rootReducer from "./store"; 3 | import { middleware } from "./store/middleware"; 4 | 5 | 6 | declare global { 7 | interface Window { 8 | __REDUX_DEVTOOLS_EXTENSION__?: typeof compose; 9 | } 10 | } 11 | 12 | const reduxDevTools = window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() 13 | const appliedMiddleware = applyMiddleware(...middleware) 14 | const enhancers = compose( 15 | appliedMiddleware,reduxDevTools 16 | ); 17 | const store = createStore(rootReducer,enhancers); 18 | export default store; -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "build", 9 | "problemMatcher": [], 10 | "group": { 11 | "kind": "build", 12 | "isDefault": true 13 | } 14 | }, 15 | { 16 | "type": "npm", 17 | "script": "electron", 18 | "problemMatcher": [], 19 | "group": "test" 20 | }, 21 | { 22 | "type": "npm", 23 | "script": "dev", 24 | "problemMatcher": [], 25 | "group": { 26 | "kind": "test", 27 | "isDefault": true 28 | } 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import './App.scss'; 2 | import React, {useState} from "react"; 3 | import { hot } from "react-hot-loader"; 4 | import Config from './classes/Config'; 5 | import Home from "./pages/Home"; 6 | import InitialSetup from './pages/InitialSetup' 7 | 8 | import { Provider } from "react-redux"; 9 | import store from "./configureStore"; 10 | 11 | 12 | function App() { 13 | const loadInitialSetup = !Config.configFileExists(); 14 | const [configured, setConfigured] = useState(Config.configFileExists()); 15 | return ( 16 | 17 | {!configured && } 18 | {configured && } 19 | 20 | 21 | ); 22 | } 23 | 24 | export default hot(module)(App); 25 | -------------------------------------------------------------------------------- /src/store/config/reducer.ts: -------------------------------------------------------------------------------- 1 | import { ConfigActionTypes,SET_CONFIG,GET_CONFIG, IConfigState } from "./types"; 2 | import _ from 'lodash'; 3 | 4 | 5 | 6 | const configState:IConfigState = { 7 | config:{ 8 | campaignSources:[], 9 | campaignLocalSources:[], 10 | feed:"", 11 | installDir:"", 12 | runCommand:"", 13 | runParams:"" 14 | } 15 | }; 16 | 17 | export default function(state = configState, action:ConfigActionTypes) { 18 | switch (action.type) { 19 | case SET_CONFIG: { 20 | const config = action.payload; 21 | return { 22 | ...state, 23 | config 24 | }; 25 | } 26 | case GET_CONFIG: { 27 | const config = action.payload; 28 | return state; 29 | } 30 | default: 31 | return state; 32 | } 33 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: abrahamYG 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Version [e.g. 22] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /src/store/campaign/middleware.ts: -------------------------------------------------------------------------------- 1 | import { Middleware } from "redux"; 2 | import { AppState } from ".."; 3 | import { CampaignActionTypes, SET_CAMPAIGNS_LOCAL, SET_CAMPAIGNS_REMOTE, SET_CAMPAIGN_LOCAL } from "./types"; 4 | import { setCampaigns } from "./actions"; 5 | 6 | 7 | export const consolidateCampaigns:Middleware = store => next => (action:CampaignActionTypes) => { 8 | let result = next(action) 9 | if ( 10 | (action.type === SET_CAMPAIGNS_LOCAL) || 11 | (action.type === SET_CAMPAIGNS_REMOTE) || 12 | (action.type === SET_CAMPAIGN_LOCAL) 13 | ){ 14 | const {campaignsLocal, campaignsRemote} = store.getState().campaignState; 15 | store.dispatch(setCampaigns([...campaignsRemote, ...campaignsLocal])); 16 | } 17 | return result; 18 | } -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from "redux"; 2 | //import visibilityFilter from "./visibilityFilter"; 3 | import campaignReducer from "./campaign/reducer"; 4 | import configReducer from "./config/reducer"; 5 | import { Reducer } from "react"; 6 | import { IConfigState, ConfigActionTypes } from "./config/types"; 7 | import { ICampaignState, CampaignActionTypes } from "./campaign/types"; 8 | 9 | export interface AppState { 10 | campaignState: ICampaignState; 11 | configState: IConfigState; 12 | } 13 | 14 | const rootReducer: Reducer = combineReducers({ 15 | campaignState: campaignReducer, 16 | configState: configReducer 17 | //visibilityFilter 18 | }); 19 | 20 | //export type AppState = ReturnType 21 | export default rootReducer; 22 | -------------------------------------------------------------------------------- /src/classes/Platform.ts: -------------------------------------------------------------------------------- 1 | const os = require('os'); 2 | 3 | export const platforms:{ 4 | WINDOWS: string, 5 | MAC: string, 6 | LINUX: string, 7 | SUN: string, 8 | OPENBSD: string, 9 | ANDROID: string, 10 | AIX: string, 11 | } = { 12 | WINDOWS: 'WINDOWS', 13 | MAC: 'MAC', 14 | LINUX: 'LINUX', 15 | SUN: 'SUN', 16 | OPENBSD: 'OPENBSD', 17 | ANDROID: 'ANDROID', 18 | AIX: 'AIX', 19 | }; 20 | 21 | export const platformsNames:{ 22 | "win32": string, 23 | "darwin": string, 24 | "linux": string, 25 | "sunos": string, 26 | "openbsd": string, 27 | "android": string, 28 | "aix": string 29 | [index: string]: string 30 | } = { 31 | win32: platforms.WINDOWS, 32 | darwin: platforms.MAC, 33 | linux: platforms.LINUX, 34 | sunos: platforms.SUN, 35 | openbsd: platforms.OPENBSD, 36 | android: platforms.ANDROID, 37 | aix: platforms.AIX, 38 | }; 39 | 40 | export const currentPlatform:string = platformsNames[os.platform()]; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Screenshot](image.png) 2 | 3 | # sc2-campaign-manager 4 | 5 | A Campaign Manager for StarCraft II. Since the game doesnt natively support custom campaigns, and a regular user has to go through hoops to get one running, this tool aims to simplify that process, providing a simple "marketplace" where to download and run custom campaigns. 6 | 7 | # Where to Download 8 | 9 | You can always find the latest release below: 10 | 11 | :link: **[Download](https://github.com/abrahamYG/sc2-campaign-manager/releases/latest)** 12 | 13 | # Contribute 14 | 15 | ## How to run 16 | 17 | * `npm install`: Install all required dependencies 18 | 19 | * `npm run dev`: Creates the Renderer 20 | * `npm run electron`: Creates the Electron Instance 21 | * `npm start`: Creates both the renderer and the electron instance. 22 | 23 | ## Building for Production 24 | 25 | `npm run build` 26 | 27 | 28 | Uses [electron-react-boilerplate-minimal](https://github.com/f-prime/electron-react-boilerplate-minimal) as a boilerplate. 29 | -------------------------------------------------------------------------------- /src/store/middleware.ts: -------------------------------------------------------------------------------- 1 | import { Middleware, Dispatch, Action, AnyAction, ActionCreator } from "redux"; 2 | import { SetCampaignsAction, CampaignActionTypes, SET_CAMPAIGNS_LOCAL, SET_CAMPAIGNS_REMOTE } from "./campaign/types"; 3 | import { AppState } from "."; 4 | import { setCampaigns, selectCampaignLocal } from "./campaign/actions"; 5 | import { ConfigActionTypes } from "./config/types"; 6 | import { consolidateCampaigns } from "./campaign/middleware"; 7 | 8 | export const logger:Middleware = store => next => action => { 9 | console.log('dispatching', action); 10 | let result = next(action); 11 | console.log('next state', store.getState()); 12 | return result 13 | } 14 | 15 | 16 | 17 | 18 | export const crashReporter:Middleware = store => next => action => { 19 | try { 20 | return next(action); 21 | } catch (err) { 22 | console.error('Caught an exception!', err) 23 | throw err 24 | } 25 | } 26 | 27 | export const middleware = [crashReporter, logger, consolidateCampaigns]; -------------------------------------------------------------------------------- /src/components/ManifestListItem.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import { ICampaign } from '../classes/Campaign'; 3 | import { connect, MapDispatchToProps } from 'react-redux'; 4 | import { selectCampaignLocal } from '../store/campaign/actions' 5 | 6 | interface IManifestListItemProps { 7 | campaign: ICampaign, 8 | index: number, 9 | selectedIndex: number, 10 | onClick?: typeof selectCampaignLocal 11 | } 12 | const ManifestListItem: FC = (props) => { 13 | const { campaign, index, selectedIndex, onClick } = props; 14 | const { id, thumbnail, author, name, summmary, progress } = campaign; 15 | const downloadProgress = (progress) ? progress : 0; 16 | return ( 17 | 20 | ); 21 | } 22 | 23 | 24 | const mapDispatchToProps: MapDispatchToProps = (dispatch, ownProps) => { 25 | return { 26 | ...ownProps, 27 | onClick: (campaign,index) => { 28 | return dispatch(selectCampaignLocal(campaign,index)); 29 | } 30 | }; 31 | }; 32 | 33 | 34 | export default connect( 35 | null,//mapStateToProps, 36 | mapDispatchToProps 37 | )(ManifestListItem); -------------------------------------------------------------------------------- /src/components/ManifestEditorJson.tsx: -------------------------------------------------------------------------------- 1 | import { ICampaign } from "../classes/Campaign"; 2 | import React, { FC, ChangeEvent } from "react"; 3 | 4 | interface IManifestEditorJsonProps{ 5 | campaign:ICampaign 6 | } 7 | 8 | const ManifestEditorJson:FC = (props) => { 9 | const {campaign} = props; 10 | const {id} = campaign 11 | const json = JSON.stringify(campaign,null,4) 12 | const isCampaign = (campaign:ICampaign):campaign is ICampaign =>{ 13 | if((campaign as ICampaign).id){ 14 | return true; 15 | } 16 | return false; 17 | } 18 | const onChange = (e:ChangeEvent)=>{ 19 | const json = JSON.parse(e.target.value); 20 | console.log(json); 21 | console.log("isCampaign",isCampaign(json)) 22 | }; 23 | return ( 24 |
25 | 26 | 27 | 28 |