1
0
mirror of https://github.com/mgerb/ps-launcher synced 2026-01-11 19:02:50 +00:00

add/update/delete servers

This commit is contained in:
2017-10-29 12:57:26 -05:00
parent 5db4b138e0
commit 9806b8ebe6
16 changed files with 483 additions and 71 deletions

View File

@@ -41,7 +41,6 @@ export class Header extends React.Component<Props, any> {
return (
<div className="header">
<div className="header__version">
{/* <i className="fa fa-2x fa-github"/> */}
<img src={headerIcon}/>
<span style={{ fontSize: '10px' }}>v{VERSION}</span>
</div>

View File

@@ -0,0 +1,50 @@
@import '../../scss/variables';
.CustomModal--hidden {
opacity: 0;
pointer-events: none;
}
.CustomModal__base {
position: fixed;
height: 100%;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
top: 0;
left: 0;
transition: opacity 0.2s ease-in-out;
}
.CustomModal__content {
height: 400px;
width: 400px;
background: $dark-blue--1;
border-radius: 4px;
z-index: 200;
box-shadow: 0 3px 7px rgba(0, 0, 0, 0.2);
display: flex;
flex-direction: column;
}
.CustomModal__overlay {
position: fixed;
height: 100%;
width: 100%;
top: 0;
left: 0;
z-index: 100;
background: $dark-blue;
opacity: 0.8;
}
.CustomModal__header {
height: 50px;
display: flex;
align-items: center;
padding-left: 20px;
background: $dark-blue--2;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}

View File

@@ -0,0 +1,33 @@
import React from 'react';
import './Modal.scss';
interface Props {
isOpen: boolean;
title: string;
onClose?(): any;
}
export class Modal extends React.Component<Props, any> {
constructor(props: Props) {
super(props);
}
public render(): any {
const { isOpen, onClose, title } = this.props;
const hiddenClass = !isOpen ? 'CustomModal--hidden' : '';
return (
<div className={'CustomModal__base ' + hiddenClass}>
<div className="CustomModal__content">
<div className="CustomModal__header">
<h3>{title}</h3>
</div>
{this.props.children}
</div>
<div className="CustomModal__overlay" onClick={onClose.bind(this)} />
</div>
);
}
}

View File

@@ -4,7 +4,7 @@
display: flex;
flex-direction: column;
background: $dark-blue;
min-width: 200px;
width: 250px;
border-right: 1px solid $dark-blue--3;
.server-list-heading {
@@ -12,15 +12,6 @@
align-items: center;
justify-content: space-between;
padding: 10px;
.fa {
cursor: pointer;
color: $gray;
&:hover {
color: $gray--1;
}
}
}
.item-container {
@@ -31,20 +22,43 @@
.list-item {
cursor: pointer;
height: 50px;
line-height: 50px;
text-align: center;
display: flex;
align-items: center;
justify-content: space-between;
background: $dark-blue;
transition: all 0.2s linear;
border-right: 4px solid transparent;
border-left: 4px solid transparent;
padding: 0 10px;
&:hover, &.selected {
.edit-button {
opacity: 0;
}
&:hover {
.edit-button {
opacity: 1;
}
}
&:hover,
&.selected {
background: $dark-blue--1;
color: lighten($white, 10%);
border-left-color: $blue;
}
}
.edit-button {
cursor: pointer;
color: $gray;
&:hover {
color: $gray--1;
}
}
.start-button-container {
height: 100px;
padding: 10px;
@@ -71,4 +85,25 @@
}
}
.modal-content {
display: flex;
flex-direction: column;
padding: 20px;
flex: 1;
}
.button-group {
display: flex;
justify-content: space-between;
}
.delete-button {
align-self: center;
color: $red;
cursor: pointer;
&:hover {
color: lighten($red, 5%);
}
}
}

View File

@@ -2,8 +2,9 @@ import React from 'react';
import fs from 'fs';
import { exec } from 'child_process';
import { inject, observer } from 'mobx-react';
import { AppState } from '../../state/AppState';
import { AppState, ServerType } from '../../state/AppState';
import { toast } from '../../util';
import { Modal } from '../Modal/Modal';
import './ServerList.scss';
@@ -11,23 +12,34 @@ interface Props {
AppState?: AppState;
}
interface State {
showModal: boolean;
editModal: boolean;
editServerIndex: number;
modalTitle: string;
modalServerName: string;
modalRealmList: string;
modalWebsiteURL: string;
}
@inject('AppState')
@observer
export class ServerList extends React.Component<Props, any> {
export class ServerList extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = this.defaultState;
}
private renderItems(): any {
const { AppState } = this.props;
return this.props.AppState.selectedExpansion.servers.map((server, index) => {
const selected = AppState.selectedExpansion.selectedServerIndex === index ? ' selected' : '';
return (
<div key={index} className={'list-item' + selected} onClick={() => AppState.setSelectedServerIndex(index)}>
{server.name}
</div>
);
});
private get defaultState(): State {
return {
showModal: false,
editModal: false,
editServerIndex: undefined,
modalTitle: '',
modalServerName: '',
modalRealmList: '',
modalWebsiteURL: '',
};
}
private async play(): Promise<void> {
@@ -72,14 +84,143 @@ export class ServerList extends React.Component<Props, any> {
});
}
private modalOnCancel(): void {
this.setState(this.defaultState);
}
private modalOnDelete(): void {
if (confirm('Are you sure you want to delete this server?')) {
this.props.AppState.deleteServer(this.state.editServerIndex);
this.setState(this.defaultState);
}
}
private modalOnSave(): void {
const { modalServerName, modalRealmList, modalWebsiteURL } = this.state;
if (modalServerName === '' || modalRealmList === '' || modalWebsiteURL === '') {
toast.error('All fields are required');
return;
}
const newServer: ServerType = {
name: modalServerName,
realmlist: modalRealmList,
website: modalWebsiteURL,
};
// edit server
if (this.state.editModal) {
this.props.AppState.editServer(this.state.editServerIndex, newServer);
toast.success('Saved');
this.setState(this.defaultState);
} else { // else add new server
if (!this.props.AppState.addServer(newServer)) {
toast.error('Server already exists');
} else {
toast.success('Saved');
this.setState(this.defaultState);
}
}
}
private addServerClick(): void {
this.setState({
showModal: true,
editModal: false,
modalTitle: 'New Server',
});
}
private editServerClick(serverIndex: number): void {
const server = this.props.AppState.selectedExpansion.servers[serverIndex];
this.setState({
showModal: true,
editModal: true,
editServerIndex: serverIndex,
modalTitle: server.name,
modalServerName: server.name,
modalRealmList: server.realmlist,
modalWebsiteURL: server.website,
});
}
private renderItems(): any {
const { AppState } = this.props;
return this.props.AppState.selectedExpansion.servers.map((server, index) => {
const selected = AppState.selectedExpansion.selectedServerIndex === index ? ' selected' : '';
return (
<div key={index} className={'list-item' + selected} onClick={() => AppState.setSelectedServerIndex(index, true)}>
<div className="ellipsis" title={server.name}>{server.name}</div>
<i className="fa fa-pencil-square-o edit-button" title="Edit" onClick={() => this.editServerClick(index)}/>
</div>
);
});
}
private renderModal(): any {
const { showModal, editModal, modalTitle, modalServerName, modalRealmList, modalWebsiteURL } = this.state;
return (
<Modal isOpen={showModal} onClose={this.modalOnCancel.bind(this)} title={modalTitle}>
<div className="modal-content">
<div style={{ flex: 1 }}>
<div className="form-group">
<label className="form-group__label">Server Name</label>
<input
className="input"
placeholder="Name"
value={modalServerName}
onChange={e => this.setState({ modalServerName: e.target.value })}
/>
</div>
<div className="form-group">
<label className="form-group__label">Realm List</label>
<input
className="input"
placeholder="logon.server.com"
value={modalRealmList}
onChange={e => this.setState({ modalRealmList: e.target.value })}
/>
</div>
<div className="form-group">
<label className="form-group__label">Website URL</label>
<input
className="input"
placeholder="https://www.server.com"
value={modalWebsiteURL}
onChange={e => this.setState({ modalWebsiteURL: e.target.value })}
/>
</div>
</div>
<div className="button-group">
<div style={{ display: 'flex' }}>
{editModal && <i className="fa fa-trash fa-lg delete-button" onClick={() => this.modalOnDelete()}/>}
</div>
<div>
<button className="button" onClick={this.modalOnCancel.bind(this)}>
Cancel
</button>
<button className="button button--success" onClick={this.modalOnSave.bind(this)}>
Save
</button>
</div>
</div>
</div>
</Modal>
);
}
public render(): any {
const { selectedServer } = this.props.AppState;
return (
<div className="server-list">
{this.renderModal()}
<div className="server-list-heading">
<div>Servers</div>
<i className="fa fa-plus" />
<i className="fa fa-plus edit-button" onClick={this.addServerClick.bind(this)} />
</div>
<div className="item-container">{this.renderItems()}</div>
<div className="start-button-container">

View File

@@ -0,0 +1,7 @@
@import '../../scss/variables.scss';
.wrapper {
display: flex;
flex-direction: column;
height: 100%;
}

View File

@@ -0,0 +1,33 @@
import React from 'react';
import { inject, observer } from 'mobx-react';
import { AppState } from '../../state/AppState';
import { Content, Header, ServerList, SubHeader } from '../';
import './Wrapper.scss';
interface Props {
AppState?: AppState;
}
@inject('AppState')
@observer
export class Wrapper extends React.Component<Props, any> {
private renderMain(): any {
return (
<div className="wrapper">
<Header />
<SubHeader />
<div style={{ display: 'flex', flex: 1 }}>
<ServerList />
<Content />
</div>
</div>
);
}
public render(): any {
// make sure app is bootstrapped before rendering
return this.props.AppState.isBootstrapped ? this.renderMain() : <div />;
}
}

View File

@@ -2,3 +2,4 @@ export * from './Content/Content';
export * from './Header/Header';
export * from './ServerList/ServerList';
export * from './SubHeader/SubHeader';
export * from './Wrapper/Wrapper';