mirror of
https://github.com/dscalzi/HeliosLauncher.git
synced 2024-12-22 11:42:14 -08:00
Initial work on overlay system.
The overlay display is driven by an array in the global redux store. Overlay content is now queueabale, so alerts can be asynchronously dispatched without interferring with existing displayed content. The server/account select overlay components are yet to be completed. Some usability features also need to be implemented, such as keybinds for the acknowledge/dismiss buttons.
This commit is contained in:
parent
691cf937fc
commit
c670b7d88d
@ -2,6 +2,9 @@
|
|||||||
height: calc(100vh - 22px);
|
height: calc(100vh - 22px);
|
||||||
background: linear-gradient(to top, rgba(0, 0, 0, 0.75) 0%, rgba(0, 0, 0, 0) 100%);
|
background: linear-gradient(to top, rgba(0, 0, 0, 0.75) 0%, rgba(0, 0, 0, 0) 100%);
|
||||||
}
|
}
|
||||||
|
.appWrapper[overlay] {
|
||||||
|
filter: blur(3px) contrast(0.9) brightness(1.0);
|
||||||
|
}
|
||||||
|
|
||||||
.loader-enter {
|
.loader-enter {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
@ -8,14 +8,16 @@ import Landing from './landing/Landing'
|
|||||||
import Login from './login/Login'
|
import Login from './login/Login'
|
||||||
import Loader from './loader/Loader'
|
import Loader from './loader/Loader'
|
||||||
import Settings from './settings/Settings'
|
import Settings from './settings/Settings'
|
||||||
|
|
||||||
import './Application.css'
|
|
||||||
import { StoreType } from '../redux/store'
|
import { StoreType } from '../redux/store'
|
||||||
import { CSSTransition } from 'react-transition-group'
|
import { CSSTransition } from 'react-transition-group'
|
||||||
import { setCurrentView } from '../redux/actions/viewActions'
|
import { ViewActionDispatch } from '../redux/actions/viewActions'
|
||||||
import { throttle } from 'lodash'
|
import { throttle } from 'lodash'
|
||||||
import { readdir } from 'fs-extra'
|
import { readdir } from 'fs-extra'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
|
import Overlay from './overlay/Overlay'
|
||||||
|
import { OverlayPushAction, OverlayActionDispatch } from '../redux/actions/overlayActions'
|
||||||
|
|
||||||
|
import './Application.css'
|
||||||
|
|
||||||
declare const __static: string
|
declare const __static: string
|
||||||
|
|
||||||
@ -27,6 +29,7 @@ function setBackground(id: number) {
|
|||||||
|
|
||||||
interface ApplicationProps {
|
interface ApplicationProps {
|
||||||
currentView: View
|
currentView: View
|
||||||
|
overlayQueue: OverlayPushAction<unknown>[]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ApplicationState {
|
interface ApplicationState {
|
||||||
@ -36,13 +39,15 @@ interface ApplicationState {
|
|||||||
workingView: View
|
workingView: View
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapState = (state: StoreType) => {
|
const mapState = (state: StoreType): Partial<ApplicationProps> => {
|
||||||
return {
|
return {
|
||||||
currentView: state.currentView
|
currentView: state.currentView,
|
||||||
|
overlayQueue: state.overlayQueue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const mapDispatch = {
|
const mapDispatch = {
|
||||||
setView: (x: View) => setCurrentView(x)
|
...ViewActionDispatch,
|
||||||
|
...OverlayActionDispatch
|
||||||
}
|
}
|
||||||
|
|
||||||
class Application extends React.Component<ApplicationProps & typeof mapDispatch, ApplicationState> {
|
class Application extends React.Component<ApplicationProps & typeof mapDispatch, ApplicationState> {
|
||||||
@ -81,6 +86,10 @@ class Application extends React.Component<ApplicationProps & typeof mapDispatch,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private hasOverlay = (): boolean => {
|
||||||
|
return this.props.overlayQueue.length > 0
|
||||||
|
}
|
||||||
|
|
||||||
private updateWorkingView = throttle(() => {
|
private updateWorkingView = throttle(() => {
|
||||||
this.setState({
|
this.setState({
|
||||||
...this.state,
|
...this.state,
|
||||||
@ -109,7 +118,32 @@ class Application extends React.Component<ApplicationProps & typeof mapDispatch,
|
|||||||
})
|
})
|
||||||
// TODO temp
|
// TODO temp
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.props.setView(View.WELCOME)
|
//this.props.setView(View.WELCOME)
|
||||||
|
this.props.pushGenericOverlay({
|
||||||
|
title: 'Test Title',
|
||||||
|
description: 'Test Description',
|
||||||
|
dismissible: true
|
||||||
|
})
|
||||||
|
this.props.pushGenericOverlay({
|
||||||
|
title: 'Test Title 2',
|
||||||
|
description: 'Test Description',
|
||||||
|
dismissible: true
|
||||||
|
})
|
||||||
|
this.props.pushGenericOverlay({
|
||||||
|
title: 'Test Title 3',
|
||||||
|
description: 'Test Description',
|
||||||
|
dismissible: true
|
||||||
|
})
|
||||||
|
this.props.pushGenericOverlay({
|
||||||
|
title: 'Test Title 4',
|
||||||
|
description: 'Test Description',
|
||||||
|
dismissible: true
|
||||||
|
})
|
||||||
|
this.props.pushGenericOverlay({
|
||||||
|
title: 'Test Title IMPORTANT',
|
||||||
|
description: 'Test Description',
|
||||||
|
dismissible: true
|
||||||
|
}, true)
|
||||||
}, 5000)
|
}, 5000)
|
||||||
}
|
}
|
||||||
const diff = Date.now() - start
|
const diff = Date.now() - start
|
||||||
@ -132,7 +166,7 @@ class Application extends React.Component<ApplicationProps & typeof mapDispatch,
|
|||||||
classNames="appWrapper"
|
classNames="appWrapper"
|
||||||
unmountOnExit
|
unmountOnExit
|
||||||
>
|
>
|
||||||
<div className="appWrapper">
|
<div className="appWrapper" {...(this.hasOverlay() ? {overlay: 'true'} : {})}>
|
||||||
<CSSTransition
|
<CSSTransition
|
||||||
in={this.props.currentView == this.state.workingView}
|
in={this.props.currentView == this.state.workingView}
|
||||||
appear={true}
|
appear={true}
|
||||||
@ -146,6 +180,15 @@ class Application extends React.Component<ApplicationProps & typeof mapDispatch,
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</CSSTransition>
|
</CSSTransition>
|
||||||
|
<CSSTransition
|
||||||
|
in={this.hasOverlay()}
|
||||||
|
appear={true}
|
||||||
|
timeout={500}
|
||||||
|
classNames="appWrapper"
|
||||||
|
unmountOnExit
|
||||||
|
>
|
||||||
|
<Overlay overlayQueue={this.props.overlayQueue} />
|
||||||
|
</CSSTransition>
|
||||||
<CSSTransition
|
<CSSTransition
|
||||||
in={this.state.loading}
|
in={this.state.loading}
|
||||||
appear={true}
|
appear={true}
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
#landingContainer > #upper {
|
#landingContainer > #upper {
|
||||||
position: relative;
|
position: relative;
|
||||||
transition: top 2s ease;
|
transition: top 2s ease;
|
||||||
top: 0px;
|
top: 0;
|
||||||
height: 77%;
|
height: 77%;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
@ -46,7 +46,7 @@
|
|||||||
#landingContainer > #lower > #left {
|
#landingContainer > #lower > #left {
|
||||||
position: relative;
|
position: relative;
|
||||||
transition: top 2s ease;
|
transition: top 2s ease;
|
||||||
top: 0px;
|
top: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 33%;
|
width: 33%;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
@ -62,7 +62,7 @@
|
|||||||
#landingContainer > #lower > #center {
|
#landingContainer > #lower > #center {
|
||||||
position: relative;
|
position: relative;
|
||||||
transition: top 2s ease;
|
transition: top 2s ease;
|
||||||
top: 0px;
|
top: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 34%;
|
width: 34%;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
@ -77,7 +77,7 @@
|
|||||||
#landingContainer > #lower > #right {
|
#landingContainer > #lower > #right {
|
||||||
position: relative;
|
position: relative;
|
||||||
transition: top 2s ease;
|
transition: top 2s ease;
|
||||||
top: 0px;
|
top: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 33%;
|
width: 33%;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
@ -161,11 +161,11 @@
|
|||||||
}
|
}
|
||||||
#newsArticleTitle:hover,
|
#newsArticleTitle:hover,
|
||||||
#newsArticleTitle:focus {
|
#newsArticleTitle:focus {
|
||||||
text-shadow: 0px 0px 20px white;
|
text-shadow: 0 0 20px white;
|
||||||
}
|
}
|
||||||
#newsArticleTitle:active {
|
#newsArticleTitle:active {
|
||||||
color: #c7c7c7;
|
color: #c7c7c7;
|
||||||
text-shadow: 0px 0px 20px #c7c7c7;
|
text-shadow: 0 0 20px #c7c7c7;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* News meta container. */
|
/* News meta container. */
|
||||||
@ -186,7 +186,7 @@
|
|||||||
#newsArticleAuthor {
|
#newsArticleAuthor {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
padding: 0px 5px;
|
padding: 0 5px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
@ -226,7 +226,7 @@
|
|||||||
#newsArticleContainer {
|
#newsArticleContainer {
|
||||||
width: calc(100% - 25px);
|
width: calc(100% - 25px);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin: 0px 0px 0px 25px;
|
margin: 0 0 0 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Article content styles. */
|
/* Article content styles. */
|
||||||
@ -234,7 +234,7 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding: 0px 15px 0px 15px;
|
padding: 0 15px 0 15px;
|
||||||
}
|
}
|
||||||
#newsArticleContentScrollable img,
|
#newsArticleContentScrollable img,
|
||||||
#newsArticleContentScrollable iframe {
|
#newsArticleContentScrollable iframe {
|
||||||
@ -277,15 +277,15 @@
|
|||||||
}
|
}
|
||||||
.bbCodeSpoilerButton:hover,
|
.bbCodeSpoilerButton:hover,
|
||||||
.bbCodeSpoilerButton:focus {
|
.bbCodeSpoilerButton:focus {
|
||||||
text-shadow: 0px 0px 20px #ffffff, 0px 0px 20px #ffffff, 0px 0px 20px #ffffff;
|
text-shadow: 0 0 20px #ffffff, 0 0 20px #ffffff, 0 0 20px #ffffff;
|
||||||
}
|
}
|
||||||
.bbCodeSpoilerButton:active {
|
.bbCodeSpoilerButton:active {
|
||||||
color: #c7c7c7;
|
color: #c7c7c7;
|
||||||
text-shadow: 0px 0px 20px #c7c7c7, 0px 0px 20px #c7c7c7, 0px 0px 20px #c7c7c7;
|
text-shadow: 0 0 20px #c7c7c7, 0 0 20px #c7c7c7, 0 0 20px #c7c7c7;
|
||||||
}
|
}
|
||||||
.bbCodeSpoilerText {
|
.bbCodeSpoilerText {
|
||||||
display: none;
|
display: none;
|
||||||
padding: 15px 0px;
|
padding: 15px 0;
|
||||||
border-bottom: 1px solid white;
|
border-bottom: 1px solid white;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,13 +312,13 @@
|
|||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 15px;
|
bottom: 15px;
|
||||||
right: 0px;
|
right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Navigation status span. */
|
/* Navigation status span. */
|
||||||
#newsNavigationStatus {
|
#newsNavigationStatus {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
margin: 0px 15px;
|
margin: 0 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Left and right navigation button styles. */
|
/* Left and right navigation button styles. */
|
||||||
@ -334,8 +334,8 @@
|
|||||||
#newsNavigateLeft:focus #newsNavigationLeftSVG,
|
#newsNavigateLeft:focus #newsNavigationLeftSVG,
|
||||||
#newsNavigateRight:hover #newsNavigationRightSVG,
|
#newsNavigateRight:hover #newsNavigationRightSVG,
|
||||||
#newsNavigateRight:focus #newsNavigationRightSVG {
|
#newsNavigateRight:focus #newsNavigationRightSVG {
|
||||||
filter: drop-shadow(0px 0px 2px #fff);
|
filter: drop-shadow(0px 0 2px #fff);
|
||||||
-webkit-filter: drop-shadow(0px 0px 2px #fff);
|
-webkit-filter: drop-shadow(0px 0 2px #fff);
|
||||||
}
|
}
|
||||||
#newsNavigateLeft:active #newsNavigationLeftSVG .arrowLine,
|
#newsNavigateLeft:active #newsNavigationLeftSVG .arrowLine,
|
||||||
#newsNavigateRight:active #newsNavigationRightSVG .arrowLine {
|
#newsNavigateRight:active #newsNavigationRightSVG .arrowLine {
|
||||||
@ -343,8 +343,8 @@
|
|||||||
}
|
}
|
||||||
#newsNavigateLeft:active #newsNavigationLeftSVG,
|
#newsNavigateLeft:active #newsNavigationLeftSVG,
|
||||||
#newsNavigateRight:active #newsNavigationRightSVG {
|
#newsNavigateRight:active #newsNavigationRightSVG {
|
||||||
filter: drop-shadow(0px 0px 2px #c7c7c7);
|
filter: drop-shadow(0px 0 2px #c7c7c7);
|
||||||
-webkit-filter: drop-shadow(0px 0px 2px #c7c7c7);
|
-webkit-filter: drop-shadow(0px 0 2px #c7c7c7);
|
||||||
}
|
}
|
||||||
#newsNavigateLeft:disabled #newsNavigationLeftSVG .arrowLine,
|
#newsNavigateLeft:disabled #newsNavigationLeftSVG .arrowLine,
|
||||||
#newsNavigateRight:disabled #newsNavigationRightSVG .arrowLine {
|
#newsNavigateRight:disabled #newsNavigationRightSVG .arrowLine {
|
||||||
@ -397,11 +397,11 @@
|
|||||||
}
|
}
|
||||||
#newsErrorRetry:focus,
|
#newsErrorRetry:focus,
|
||||||
#newsErrorRetry:hover {
|
#newsErrorRetry:hover {
|
||||||
text-shadow: 0px 0px 20px white;
|
text-shadow: 0 0 20px white;
|
||||||
}
|
}
|
||||||
#newsErrorRetry:active {
|
#newsErrorRetry:active {
|
||||||
color: #c7c7c7;
|
color: #c7c7c7;
|
||||||
text-shadow: 0px 0px 20px #c7c7c7;
|
text-shadow: 0 0 20px #c7c7c7;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -537,7 +537,7 @@
|
|||||||
background: rgba(1, 2, 1, 0.5);
|
background: rgba(1, 2, 1, 0.5);
|
||||||
height: 70px;
|
height: 70px;
|
||||||
width: 70px;
|
width: 70px;
|
||||||
box-shadow: 0px 0px 10px 0px rgb(0, 0, 0);
|
box-shadow: 0 0 10px 0 rgb(0, 0, 0);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
@ -578,7 +578,7 @@
|
|||||||
min-width: 135px;
|
min-width: 135px;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
letter-spacing: 1px;
|
letter-spacing: 1px;
|
||||||
text-shadow: 0px 0px 20px black;
|
text-shadow: 0 0 20px black;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 95px;
|
right: 95px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
@ -615,7 +615,7 @@
|
|||||||
height: 1px;
|
height: 1px;
|
||||||
width: 14px;
|
width: 14px;
|
||||||
background: rgb(255, 255, 255);
|
background: rgb(255, 255, 255);
|
||||||
margin: 10px 0px;
|
margin: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Social media icon shared styles. */
|
/* Social media icon shared styles. */
|
||||||
@ -642,7 +642,7 @@
|
|||||||
.mediaButton {
|
.mediaButton {
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 0px;
|
padding: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
outline: none;
|
outline: none;
|
||||||
@ -745,7 +745,7 @@
|
|||||||
font-size: 9px;
|
font-size: 9px;
|
||||||
letter-spacing: 1px;
|
letter-spacing: 1px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-shadow: 0px 0px 0px #bebcbb;
|
text-shadow: 0 0 0 #bebcbb;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Divider used on the bottom of the landing view. */
|
/* Divider used on the bottom of the landing view. */
|
||||||
@ -772,7 +772,7 @@
|
|||||||
color: #949494;
|
color: #949494;
|
||||||
font-size: 8px;
|
font-size: 8px;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
text-shadow: 0px 0px 20px #949494;
|
text-shadow: 0 0 20px #949494;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -809,7 +809,7 @@
|
|||||||
bottom: calc(100% + 15px);
|
bottom: calc(100% + 15px);
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
margin-left: 50%;
|
margin-left: 50%;
|
||||||
box-shadow: 0px 0px 20px rgb(0, 0, 0);
|
box-shadow: 0 0 20px rgb(0, 0, 0);
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
#mojangStatusTooltip:after {
|
#mojangStatusTooltip:after {
|
||||||
@ -840,7 +840,7 @@
|
|||||||
#mojangStatusNEContainer {
|
#mojangStatusNEContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin: 10px 0px;
|
margin: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* White bar which surrounds the non essential service title. */
|
/* White bar which surrounds the non essential service title. */
|
||||||
@ -853,7 +853,7 @@
|
|||||||
/* Non essential service title text. */
|
/* Non essential service title text. */
|
||||||
#mojangStatusNETitle {
|
#mojangStatusNETitle {
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
padding: 0px 3px;
|
padding: 0 3px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
letter-spacing: 1px;
|
letter-spacing: 1px;
|
||||||
}
|
}
|
||||||
@ -869,7 +869,7 @@
|
|||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
letter-spacing: 1px;
|
letter-spacing: 1px;
|
||||||
line-height: 12px;
|
line-height: 12px;
|
||||||
padding: 6px 0px;
|
padding: 6px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Displays the status of the mojang service. */
|
/* Displays the status of the mojang service. */
|
||||||
@ -892,24 +892,24 @@
|
|||||||
}
|
}
|
||||||
#newsButton:hover #newsButtonText,
|
#newsButton:hover #newsButtonText,
|
||||||
#newsButton:focus #newsButtonText {
|
#newsButton:focus #newsButtonText {
|
||||||
text-shadow: 0px 0px 20px #fff, 0px 0px 20px #fff;
|
text-shadow: 0 0 20px #fff, 0 0 20px #fff;
|
||||||
}
|
}
|
||||||
#newsButton:active {
|
#newsButton:active {
|
||||||
color: #c7c7c7;
|
color: #c7c7c7;
|
||||||
text-shadow: 0px 0px 20px #c7c7c7, 0px 0px 20px #c7c7c7;
|
text-shadow: 0 0 20px #c7c7c7, 0 0 20px #c7c7c7;
|
||||||
}
|
}
|
||||||
|
|
||||||
#newsButton:hover #newsButtonSVG,
|
#newsButton:hover #newsButtonSVG,
|
||||||
#newsButton:focus #newsButtonSVG {
|
#newsButton:focus #newsButtonSVG {
|
||||||
filter: drop-shadow(0px 0px 2px #fff);
|
filter: drop-shadow(0px 0 2px #fff);
|
||||||
-webkit-filter: drop-shadow(0px 0px 2px #fff);
|
-webkit-filter: drop-shadow(0px 0 2px #fff);
|
||||||
}
|
}
|
||||||
#newsButton:active #newsButtonSVG .arrowLine {
|
#newsButton:active #newsButtonSVG .arrowLine {
|
||||||
stroke: #c7c7c7;
|
stroke: #c7c7c7;
|
||||||
}
|
}
|
||||||
#newsButton:active #newsButtonSVG {
|
#newsButton:active #newsButtonSVG {
|
||||||
filter: drop-shadow(0px 0px 2px #c7c7c7);
|
filter: drop-shadow(0px 0 2px #c7c7c7);
|
||||||
-webkit-filter: drop-shadow(0px 0px 2px #c7c7c7);
|
-webkit-filter: drop-shadow(0px 0 2px #c7c7c7);
|
||||||
}
|
}
|
||||||
#newsButton:disabled #newsButtonSVG .arrowLine {
|
#newsButton:disabled #newsButtonSVG .arrowLine {
|
||||||
stroke: rgba(255, 255, 255, 0.75);
|
stroke: rgba(255, 255, 255, 0.75);
|
||||||
@ -938,7 +938,7 @@
|
|||||||
color: white;
|
color: white;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
letter-spacing: 2px;
|
letter-spacing: 2px;
|
||||||
text-shadow: 0px 0px 0px #bebcbb;
|
text-shadow: 0 0 0 #bebcbb;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -963,19 +963,19 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
letter-spacing: 2px;
|
letter-spacing: 2px;
|
||||||
text-shadow: 0px 0px 0px #bebcbb;
|
text-shadow: 0 0 0 #bebcbb;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
padding: 0px;
|
padding: 0;
|
||||||
transition: 0.25s ease;
|
transition: 0.25s ease;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
#launch_button:hover,
|
#launch_button:hover,
|
||||||
#launch_button:focus {
|
#launch_button:focus {
|
||||||
text-shadow: 0px 0px 20px #fff, 0px 0px 20px #fff;
|
text-shadow: 0 0 20px #fff, 0 0 20px #fff;
|
||||||
}
|
}
|
||||||
#launch_button:active {
|
#launch_button:active {
|
||||||
color: #c7c7c7;
|
color: #c7c7c7;
|
||||||
text-shadow: 0px 0px 20px #c7c7c7, 0px 0px 20px #c7c7c7;
|
text-shadow: 0 0 20px #c7c7c7, 0 0 20px #c7c7c7;
|
||||||
}
|
}
|
||||||
#launch_button:disabled {
|
#launch_button:disabled {
|
||||||
color: #c7c7c7;
|
color: #c7c7c7;
|
||||||
@ -999,7 +999,7 @@
|
|||||||
#launch_progress_label {
|
#launch_progress_label {
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
letter-spacing: 1px;
|
letter-spacing: 1px;
|
||||||
text-shadow: 0px 0px 0px #bebcbb;
|
text-shadow: 0 0 0 #bebcbb;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
min-width: 53.21px;
|
min-width: 53.21px;
|
||||||
max-width: 53.21px;
|
max-width: 53.21px;
|
||||||
@ -1020,16 +1020,16 @@
|
|||||||
outline: none;
|
outline: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
padding: 0px;
|
padding: 0;
|
||||||
transition: 0.25s ease;
|
transition: 0.25s ease;
|
||||||
}
|
}
|
||||||
#server_selection_button:hover,
|
#server_selection_button:hover,
|
||||||
#server_selection_button:focus {
|
#server_selection_button:focus {
|
||||||
text-shadow: 0px 0px 20px #fff, 0px 0px 20px #fff, 0px 0px 20px #fff;
|
text-shadow: 0 0 20px #fff, 0 0 20px #fff, 0 0 20px #fff;
|
||||||
}
|
}
|
||||||
#server_selection_button:active {
|
#server_selection_button:active {
|
||||||
color: #c7c7c7;
|
color: #c7c7c7;
|
||||||
text-shadow: 0px 0px 20px #c7c7c7, 0px 0px 20px #c7c7c7, 0px 0px 20px #c7c7c7;
|
text-shadow: 0 0 20px #c7c7c7, 0 0 20px #c7c7c7, 0 0 20px #c7c7c7;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Progress bar styles. */
|
/* Progress bar styles. */
|
||||||
|
12
src/renderer/components/overlay/Overlay.css
Normal file
12
src/renderer/components/overlay/Overlay.css
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/* Overlay container, placed over the main div. */
|
||||||
|
#overlayContainer {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 500;
|
||||||
|
top: 22px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100% - 22px);
|
||||||
|
background: rgba(0, 0, 0, 0.50);
|
||||||
|
}
|
79
src/renderer/components/overlay/Overlay.tsx
Normal file
79
src/renderer/components/overlay/Overlay.tsx
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import * as React from 'react'
|
||||||
|
import GenericOverlay from './generic-overlay/GenericOverlay'
|
||||||
|
import ServerSelectOverlay from './server-select/ServerSelectOverlay'
|
||||||
|
import AccountSelectOverlay from './account-select/AccountSelectOverlay'
|
||||||
|
import { OverlayContent, PushGenericOverlayAction, PushServerSelectOverlayAction } from '../../redux/actions/overlayActions'
|
||||||
|
import { StoreType } from '../../redux/store'
|
||||||
|
import { OverlayPushAction } from '../../redux/actions/overlayActions'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
|
||||||
|
import './Overlay.css'
|
||||||
|
|
||||||
|
interface OverlayProps {
|
||||||
|
overlayQueue: OverlayPushAction<unknown>[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapState = (state: StoreType): Partial<OverlayProps> => {
|
||||||
|
return {
|
||||||
|
overlayQueue: state.overlayQueue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Overlay extends React.Component<OverlayProps> {
|
||||||
|
|
||||||
|
private getGenericOverlay(action: PushGenericOverlayAction): JSX.Element {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<GenericOverlay
|
||||||
|
title={action.payload.title}
|
||||||
|
description={action.payload.description}
|
||||||
|
dismissible={action.payload.dismissible}
|
||||||
|
acknowledgeText={action.payload.acknowledgeText}
|
||||||
|
dismissText={action.payload.dismissText}
|
||||||
|
acknowledgeCallback={action.payload.acknowledgeCallback}
|
||||||
|
dismissCallback={action.payload.dismissCallback}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private getServerSelectOverlay(action: PushServerSelectOverlayAction): JSX.Element {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ServerSelectOverlay
|
||||||
|
servers={action.payload.servers}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private getOverlayContent(): JSX.Element {
|
||||||
|
if(!this.props.overlayQueue || this.props.overlayQueue.length < 1) {
|
||||||
|
return (<></>)
|
||||||
|
}
|
||||||
|
const currentContent = this.props.overlayQueue[0]
|
||||||
|
switch(currentContent.overlayContent) {
|
||||||
|
case OverlayContent.GENERIC:
|
||||||
|
return this.getGenericOverlay(currentContent as PushGenericOverlayAction)
|
||||||
|
case OverlayContent.SERVER_SELECT:
|
||||||
|
return this.getServerSelectOverlay(currentContent as PushServerSelectOverlayAction)
|
||||||
|
case OverlayContent.ACCOUNT_SELECT:
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<AccountSelectOverlay />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render(): JSX.Element {
|
||||||
|
return <>
|
||||||
|
<div id="overlayContainer">
|
||||||
|
{this.getOverlayContent()}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect<unknown, unknown>(mapState)(Overlay)
|
@ -0,0 +1,28 @@
|
|||||||
|
import * as React from 'react'
|
||||||
|
|
||||||
|
import '../shared-select/SharedSelect.css'
|
||||||
|
|
||||||
|
export default class AccountSelectOverlay extends React.Component {
|
||||||
|
|
||||||
|
render(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div id="accountSelectContent">
|
||||||
|
<span id="accountSelectHeader">Select an Account</span>
|
||||||
|
<div id="accountSelectList">
|
||||||
|
<div id="accountSelectListScrollable">
|
||||||
|
{/* Accounts populated here. */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="accountSelectActions">
|
||||||
|
<button id="accountSelectConfirm" className="overlayKeybindEnter" type="submit">Select</button>
|
||||||
|
<div id="accountSelectCancelWrapper">
|
||||||
|
<button id="accountSelectCancel" className="overlayKeybindEsc">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,104 @@
|
|||||||
|
/* Main overlay content. */
|
||||||
|
#overlayContent {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
/*justify-content: space-between;*/
|
||||||
|
width: 300px;
|
||||||
|
/*height: 35%;*/
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 15px 0px;
|
||||||
|
/* background-color: #424242; */
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main overlay content anchor styles. */
|
||||||
|
#overlayContent a,
|
||||||
|
#overlayDismiss {
|
||||||
|
color: rgba(202, 202, 202, 0.75);
|
||||||
|
transition: 0.25s ease;
|
||||||
|
}
|
||||||
|
#overlayContent a:hover,
|
||||||
|
#overlayContent a:focus,
|
||||||
|
#overlayDismiss:focus {
|
||||||
|
color: rgba(255, 255, 255, 0.75);
|
||||||
|
}
|
||||||
|
#overlayContent a:active,
|
||||||
|
#overlayDismiss:active {
|
||||||
|
color: rgba(165, 165, 165, 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add spacing between overlay content elements. */
|
||||||
|
#overlayContent > *:first-child {
|
||||||
|
margin-top: 0px !important;
|
||||||
|
}
|
||||||
|
#overlayContent > *:last-child {
|
||||||
|
margin-bottom: 0px !important;
|
||||||
|
}
|
||||||
|
#overlayContent > * {
|
||||||
|
margin: 8px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Overlay title styles. */
|
||||||
|
#overlayTitle {
|
||||||
|
font-family: 'Avenir Medium';
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
-webkit-user-select: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Overlay description styles. */
|
||||||
|
#overlayDesc {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
-webkit-user-select: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Div which contains action buttons. */
|
||||||
|
#overlayActionContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Overlay acknowledge button styles. */
|
||||||
|
#overlayAcknowledge {
|
||||||
|
background: none;
|
||||||
|
border: 1px solid #ffffff;
|
||||||
|
color: white;
|
||||||
|
font-family: 'Avenir Medium';
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 0px 8.1px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: 0.25s ease;
|
||||||
|
}
|
||||||
|
#overlayAcknowledge:hover,
|
||||||
|
#overlayAcknowledge:focus {
|
||||||
|
box-shadow: 0px 0px 10px 0px #fff;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
#overlayAcknowledge:active {
|
||||||
|
border-color: rgba(255, 255, 255, 0.75);
|
||||||
|
color: rgba(255, 255, 255, 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Overlay dismiss option styles. */
|
||||||
|
#overlayDismiss {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 10px;
|
||||||
|
text-decoration: none;
|
||||||
|
padding-top: 2.5px;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
#overlayDismiss:hover {
|
||||||
|
color: rgba(255, 255, 255, 0.75);
|
||||||
|
}
|
||||||
|
#overlayDismiss:active {
|
||||||
|
color: rgba(165, 165, 165, 0.75);
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
import * as React from 'react'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { OverlayActionDispatch } from '../../../redux/actions/overlayActions'
|
||||||
|
|
||||||
|
import './GenericOverlay.css'
|
||||||
|
|
||||||
|
export interface GenericOverlayProps {
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
acknowledgeText?: string
|
||||||
|
dismissText?: string
|
||||||
|
dismissible: boolean
|
||||||
|
acknowledgeCallback?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void
|
||||||
|
dismissCallback?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatch = {
|
||||||
|
...OverlayActionDispatch
|
||||||
|
}
|
||||||
|
|
||||||
|
type InternalGenericOverlayProps = GenericOverlayProps & typeof mapDispatch
|
||||||
|
|
||||||
|
class GenericOverlay extends React.Component<InternalGenericOverlayProps> {
|
||||||
|
|
||||||
|
private getAcknowledgeText = (): string => {
|
||||||
|
return this.props.acknowledgeText || 'OK'
|
||||||
|
}
|
||||||
|
|
||||||
|
private getDismissText = (): string => {
|
||||||
|
return this.props.dismissText || 'Dismiss'
|
||||||
|
}
|
||||||
|
|
||||||
|
private onAcknowledgeClick = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>): void => {
|
||||||
|
if(this.props.acknowledgeCallback) {
|
||||||
|
this.props.acknowledgeCallback(event)
|
||||||
|
}
|
||||||
|
this.props.popOverlayContent()
|
||||||
|
}
|
||||||
|
|
||||||
|
private onDismissClick = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>): void => {
|
||||||
|
if(this.props.dismissCallback) {
|
||||||
|
this.props.dismissCallback(event)
|
||||||
|
}
|
||||||
|
this.props.popOverlayContent()
|
||||||
|
}
|
||||||
|
|
||||||
|
render(): JSX.Element {
|
||||||
|
return <>
|
||||||
|
<div id="overlayContent">
|
||||||
|
<span id="overlayTitle">{this.props.title}</span>
|
||||||
|
<span id="overlayDesc">{this.props.description}</span>
|
||||||
|
<div id="overlayActionContainer">
|
||||||
|
<button onClick={this.onAcknowledgeClick} id="overlayAcknowledge" className="overlayKeybindEnter">{this.getAcknowledgeText()}</button>
|
||||||
|
<div id="overlayDismissWrapper">
|
||||||
|
{ this.props.dismissible
|
||||||
|
? <button onClick={this.onDismissClick} id="overlayDismiss" className="overlayKeybindEsc">{this.getDismissText()}</button>
|
||||||
|
: <></>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect<unknown, typeof mapDispatch>(undefined, mapDispatch)(GenericOverlay)
|
@ -0,0 +1,33 @@
|
|||||||
|
import * as React from 'react'
|
||||||
|
|
||||||
|
import '../shared-select/SharedSelect.css'
|
||||||
|
import { Server } from 'helios-distribution-types'
|
||||||
|
|
||||||
|
export interface ServerSelectOverlayProps {
|
||||||
|
servers: Server[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class ServerSelectOverlay extends React.Component<ServerSelectOverlayProps> {
|
||||||
|
|
||||||
|
render(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div id="serverSelectContent">
|
||||||
|
<span id="serverSelectHeader">Available Servers</span>
|
||||||
|
<div id="serverSelectList">
|
||||||
|
<div id="serverSelectListScrollable">
|
||||||
|
{/* Server listings populated here. */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="serverSelectActions">
|
||||||
|
<button id="serverSelectConfirm" className="overlayKeybindEnter" type="submit">Select</button>
|
||||||
|
<div id="serverSelectCancelWrapper">
|
||||||
|
<button id="serverSelectCancel" className="overlayKeybindEsc">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
284
src/renderer/components/overlay/shared-select/SharedSelect.css
Normal file
284
src/renderer/components/overlay/shared-select/SharedSelect.css
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
/* * *
|
||||||
|
* Overlay View (Server + Account Selection Content)
|
||||||
|
* * */
|
||||||
|
|
||||||
|
/* Server selection content container. */
|
||||||
|
#serverSelectContent,
|
||||||
|
#accountSelectContent {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 75%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Server selection header. */
|
||||||
|
#serverSelectHeader,
|
||||||
|
#accountSelectHeader {
|
||||||
|
font-family: 'Avenir Medium';
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #fff;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wrapper div for the list of available servers. */
|
||||||
|
#serverSelectList,
|
||||||
|
#accountSelectList {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
max-height: 65%;
|
||||||
|
min-height: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scrollable div which lists the available servers. */
|
||||||
|
#serverSelectListScrollable,
|
||||||
|
#accountSelectListScrollable {
|
||||||
|
padding: 0px 5px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
#serverSelectListScrollable::-webkit-scrollbar,
|
||||||
|
#accountSelectListScrollable::-webkit-scrollbar {
|
||||||
|
width: 2px;
|
||||||
|
}
|
||||||
|
#serverSelectListScrollable::-webkit-scrollbar-track,
|
||||||
|
#accountSelectListScrollable::-webkit-scrollbar-track {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#serverSelectListScrollable::-webkit-scrollbar-thumb,
|
||||||
|
#accountSelectListScrollable::-webkit-scrollbar-thumb {
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: inset 0 0 10px rgba(255, 255, 255, 0.50);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Content container for a server listing. */
|
||||||
|
.serverListing {
|
||||||
|
border: none;
|
||||||
|
padding: 0px;
|
||||||
|
width: 375px;
|
||||||
|
min-height: 60px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
opacity: 0.6;
|
||||||
|
transition: 0.25s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
background: rgba(131, 131, 131, 0.25);
|
||||||
|
}
|
||||||
|
.serverListing[selected] {
|
||||||
|
cursor: default;
|
||||||
|
opacity: 1.0;
|
||||||
|
}
|
||||||
|
.serverListing:hover,
|
||||||
|
.serverListing:focus {
|
||||||
|
outline: none;
|
||||||
|
opacity: 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accountListing {
|
||||||
|
color: white;
|
||||||
|
border: 1px solid rgba(126, 126, 126, 0.57);
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 5px 45px;
|
||||||
|
width: 250px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
opacity: 0.6;
|
||||||
|
transition: 0.25s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
background: rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
|
.accountListing[selected] {
|
||||||
|
cursor: default;
|
||||||
|
opacity: 1.0;
|
||||||
|
}
|
||||||
|
.accountListing:hover,
|
||||||
|
.accountListing:focus {
|
||||||
|
outline: none;
|
||||||
|
opacity: 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accountListingName {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add spacing between server listings. */
|
||||||
|
#serverSelectListScrollable > .serverListing:not(:first-child):not(:last-child),
|
||||||
|
#accountSelectListScrollable > .accountListing:not(:first-child):not(:last-child) {
|
||||||
|
margin: 5px 0px;
|
||||||
|
}
|
||||||
|
#serverSelectListScrollable > .serverListing:first-child,
|
||||||
|
#accountSelectListScrollable > .accountListing:first-child {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
#serverSelectListScrollable > .serverListing:last-child,
|
||||||
|
#accountSelectListScrollable > .accountListing:last-child {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Server listing image. */
|
||||||
|
.serverListingImg {
|
||||||
|
margin: 0px 10px 0px 5px;
|
||||||
|
border: 1px solid #fff;
|
||||||
|
height: 50px;
|
||||||
|
width: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Content container for the server listing's details. */
|
||||||
|
.serverListingDetails {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The name of the server listing. */
|
||||||
|
.serverListingName {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Description for the server listing. */
|
||||||
|
.serverListingDescription {
|
||||||
|
font-size: 10px;
|
||||||
|
line-height: 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Content container for the server listing's information. */
|
||||||
|
.serverListingInfo {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The minecraft version of the server listing. */
|
||||||
|
.serverListingVersion {
|
||||||
|
font-size: 10px;
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
line-height: 12px;
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: rgba(31, 140, 11, 0.8);
|
||||||
|
padding: 0px 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The revision version of the server's manifest. */
|
||||||
|
.serverListingRevision {
|
||||||
|
color: #969696;
|
||||||
|
font-size: 10px;
|
||||||
|
line-height: 12px;
|
||||||
|
padding: 0px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Star which indicates the default (main) server. */
|
||||||
|
.serverListingStarWrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
height: 12px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
/* Tooltip which displays when hovering over the star. */
|
||||||
|
.serverListingStarTooltip {
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
width: 65px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.40);
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
left: 130%;
|
||||||
|
font-size: 10px;
|
||||||
|
transition: visibility 0s linear 0.25s, opacity 0.25s ease;
|
||||||
|
}
|
||||||
|
.serverListingStarTooltip::after {
|
||||||
|
content: " ";
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: 100%; /* To the left of the tooltip */
|
||||||
|
margin-top: -5px;
|
||||||
|
border-width: 5px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: transparent rgba(0, 0, 0, 0.40) transparent transparent;
|
||||||
|
}
|
||||||
|
.serverListingStarWrapper:hover .serverListingStarTooltip {
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1;
|
||||||
|
transition-delay:0s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Content container which contains the server select actions. */
|
||||||
|
#serverSelectActions,
|
||||||
|
#accountSelectActions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Server selection confirm button styles. */
|
||||||
|
#serverSelectConfirm,
|
||||||
|
#accountSelectConfirm {
|
||||||
|
background: none;
|
||||||
|
border: 1px solid #ffffff;
|
||||||
|
color: white;
|
||||||
|
font-family: 'Avenir Medium';
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 0px 8.1px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: 0.25s ease;
|
||||||
|
min-height: 20.67px;
|
||||||
|
}
|
||||||
|
#serverSelectConfirm:hover,
|
||||||
|
#serverSelectConfirm:focus,
|
||||||
|
#accountSelectConfirm:hover,
|
||||||
|
#accountSelectConfirm:focus {
|
||||||
|
box-shadow: 0px 0px 10px 0px #fff;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
#serverSelectConfirm:active,
|
||||||
|
#accountSelectConfirm:active {
|
||||||
|
border-color: rgba(255, 255, 255, 0.75);
|
||||||
|
color: rgba(255, 255, 255, 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Server selection cancel button styles. */
|
||||||
|
#serverSelectCancel,
|
||||||
|
#accountSelectCancel {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 10px;
|
||||||
|
text-decoration: none;
|
||||||
|
padding-top: 2.5px;
|
||||||
|
color: rgba(202, 202, 202, 0.75);
|
||||||
|
transition: 0.25s ease;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
#serverSelectCancel:hover,
|
||||||
|
#serverSelectCancel:focus,
|
||||||
|
#accountSelectCancel:hover,
|
||||||
|
#accountSelectCancel:focus {
|
||||||
|
color: rgba(255, 255, 255, 0.75);
|
||||||
|
}
|
||||||
|
#serverSelectCancel:active,
|
||||||
|
#accountSelectCancel:active {
|
||||||
|
color: rgba(165, 165, 165, 0.75);
|
||||||
|
}
|
@ -15,7 +15,10 @@ document.body.appendChild(mainElement)
|
|||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<AppContainer>
|
<AppContainer>
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<Application currentView={store.getState().currentView} />
|
<Application
|
||||||
|
currentView={store.getState().currentView}
|
||||||
|
overlayQueue={store.getState().overlayQueue}
|
||||||
|
/>
|
||||||
</Provider>
|
</Provider>
|
||||||
</AppContainer>,
|
</AppContainer>,
|
||||||
mainElement
|
mainElement
|
||||||
|
66
src/renderer/redux/actions/overlayActions.ts
Normal file
66
src/renderer/redux/actions/overlayActions.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import { Action } from 'redux'
|
||||||
|
import { GenericOverlayProps } from 'src/renderer/components/overlay/generic-overlay/GenericOverlay'
|
||||||
|
import { ServerSelectOverlayProps } from 'src/renderer/components/overlay/server-select/ServerSelectOverlay'
|
||||||
|
|
||||||
|
export enum OverlayContent {
|
||||||
|
ACCOUNT_SELECT,
|
||||||
|
SERVER_SELECT,
|
||||||
|
GENERIC
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum OverlayActionType {
|
||||||
|
PushContent = 'PUSH_CONTENT',
|
||||||
|
PopContent = 'POP_CONTENT'
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||||
|
export interface OverlayAction extends Action {}
|
||||||
|
|
||||||
|
export interface OverlayPushAction<T> extends OverlayAction {
|
||||||
|
overlayContent: OverlayContent
|
||||||
|
timestamp: number
|
||||||
|
showNext: boolean
|
||||||
|
payload: T
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PushGenericOverlayAction extends OverlayPushAction<GenericOverlayProps> {
|
||||||
|
overlayContent: OverlayContent.GENERIC
|
||||||
|
payload: GenericOverlayProps
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PushServerSelectOverlayAction extends OverlayPushAction<ServerSelectOverlayProps> {
|
||||||
|
overlayContent: OverlayContent.SERVER_SELECT
|
||||||
|
payload: ServerSelectOverlayProps
|
||||||
|
}
|
||||||
|
|
||||||
|
export function pushGenericOverlay(options: GenericOverlayProps, showNext = false): PushGenericOverlayAction {
|
||||||
|
return {
|
||||||
|
type: OverlayActionType.PushContent,
|
||||||
|
overlayContent: OverlayContent.GENERIC,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
showNext,
|
||||||
|
payload: options
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function pushServerSelectOverlay(options: ServerSelectOverlayProps, showNext = false): PushServerSelectOverlayAction {
|
||||||
|
return {
|
||||||
|
type: OverlayActionType.PushContent,
|
||||||
|
overlayContent: OverlayContent.SERVER_SELECT,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
showNext,
|
||||||
|
payload: options
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function popOverlayContent(): OverlayAction {
|
||||||
|
return {
|
||||||
|
type: OverlayActionType.PopContent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const OverlayActionDispatch = {
|
||||||
|
pushGenericOverlay: (options: GenericOverlayProps, showNext = false): PushGenericOverlayAction => pushGenericOverlay(options, showNext),
|
||||||
|
pushServerSelectOverlay: (options: ServerSelectOverlayProps, showNext = false): PushServerSelectOverlayAction => pushServerSelectOverlay(options, showNext),
|
||||||
|
popOverlayContent: (): OverlayAction => popOverlayContent()
|
||||||
|
}
|
@ -15,3 +15,7 @@ export function setCurrentView(view: View): ChangeViewAction {
|
|||||||
payload: view
|
payload: view
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const ViewActionDispatch = {
|
||||||
|
setView: (x: View): ChangeViewAction => setCurrentView(x)
|
||||||
|
}
|
@ -1,8 +1,10 @@
|
|||||||
import { combineReducers } from 'redux'
|
import { combineReducers } from 'redux'
|
||||||
import ViewReducer from './viewReducer'
|
import ViewReducer from './viewReducer'
|
||||||
import AppReducer from './appReducer'
|
import AppReducer from './appReducer'
|
||||||
|
import OverlayReducer from './overlayReducer'
|
||||||
|
|
||||||
export default combineReducers({
|
export default combineReducers({
|
||||||
currentView: ViewReducer,
|
currentView: ViewReducer,
|
||||||
|
overlayQueue: OverlayReducer,
|
||||||
app: AppReducer
|
app: AppReducer
|
||||||
})
|
})
|
30
src/renderer/redux/reducers/overlayReducer.ts
Normal file
30
src/renderer/redux/reducers/overlayReducer.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { Reducer } from 'redux'
|
||||||
|
import { OverlayAction, OverlayActionType, OverlayPushAction } from '../actions/overlayActions'
|
||||||
|
|
||||||
|
const defaultOverlayQueue: OverlayPushAction<unknown>[] = []
|
||||||
|
|
||||||
|
const OverlayReducer: Reducer<OverlayPushAction<unknown>[], OverlayAction> = (state = defaultOverlayQueue, action) => {
|
||||||
|
switch(action.type) {
|
||||||
|
case OverlayActionType.PushContent:
|
||||||
|
if((action as OverlayPushAction<unknown>).showNext && state.length > 0) {
|
||||||
|
return [
|
||||||
|
state[0],
|
||||||
|
action as OverlayPushAction<unknown>,
|
||||||
|
...state.slice(1)
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
...state,
|
||||||
|
action as OverlayPushAction<unknown>
|
||||||
|
]
|
||||||
|
}
|
||||||
|
case OverlayActionType.PopContent:
|
||||||
|
return [
|
||||||
|
...state.slice(1)
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OverlayReducer
|
@ -2,7 +2,7 @@ import { Reducer } from 'redux'
|
|||||||
import { View } from '../../meta/Views'
|
import { View } from '../../meta/Views'
|
||||||
import { ChangeViewAction, ViewActionType } from '../actions/viewActions'
|
import { ChangeViewAction, ViewActionType } from '../actions/viewActions'
|
||||||
|
|
||||||
const defaultView = View.LOGIN
|
const defaultView = View.LANDING
|
||||||
|
|
||||||
const ViewReducer: Reducer<View, ChangeViewAction> = (state = defaultView, action) => {
|
const ViewReducer: Reducer<View, ChangeViewAction> = (state = defaultView, action) => {
|
||||||
switch(action.type) {
|
switch(action.type) {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||||
import nock from 'nock'
|
import nock from 'nock'
|
||||||
import { URL } from 'url'
|
import { URL } from 'url'
|
||||||
import { MojangIndexProcessor } from 'common/asset/processor/MojangIndexProcessor'
|
import { MojangIndexProcessor } from 'common/asset/processor/MojangIndexProcessor'
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { Mojang } from "common/mojang/mojang"
|
import { Mojang } from 'common/mojang/mojang'
|
||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import nock from 'nock'
|
import nock from 'nock'
|
||||||
import { Session } from "common/mojang/model/auth/Session"
|
import { Session } from 'common/mojang/model/auth/Session'
|
||||||
import { MojangResponseCode } from "common/mojang/model/internal/Response"
|
import { MojangResponseCode } from 'common/mojang/model/internal/Response'
|
||||||
|
|
||||||
function expectMojangResponse(res: any, responseCode: MojangResponseCode, negate = false) {
|
function expectMojangResponse(res: any, responseCode: MojangResponseCode, negate = false) {
|
||||||
expect(res).to.not.be.an('error')
|
expect(res).to.not.be.an('error')
|
||||||
@ -29,7 +29,7 @@ describe('Mojang Errors', () => {
|
|||||||
.get('/check')
|
.get('/check')
|
||||||
.reply(500, 'Service temprarily offline.')
|
.reply(500, 'Service temprarily offline.')
|
||||||
|
|
||||||
const res = await Mojang.status();
|
const res = await Mojang.status()
|
||||||
expectMojangResponse(res, MojangResponseCode.SUCCESS, true)
|
expectMojangResponse(res, MojangResponseCode.SUCCESS, true)
|
||||||
expect(res.data).to.be.an('array')
|
expect(res.data).to.be.an('array')
|
||||||
expect(res.data).to.deep.equal(defStatusHack)
|
expect(res.data).to.deep.equal(defStatusHack)
|
||||||
@ -65,7 +65,7 @@ describe('Mojang Status', () => {
|
|||||||
.get('/check')
|
.get('/check')
|
||||||
.reply(200, defStatusHack)
|
.reply(200, defStatusHack)
|
||||||
|
|
||||||
const res = await Mojang.status();
|
const res = await Mojang.status()
|
||||||
expectMojangResponse(res, MojangResponseCode.SUCCESS)
|
expectMojangResponse(res, MojangResponseCode.SUCCESS)
|
||||||
expect(res.data).to.be.an('array')
|
expect(res.data).to.be.an('array')
|
||||||
expect(res.data).to.deep.equal(defStatusHack)
|
expect(res.data).to.deep.equal(defStatusHack)
|
||||||
|
Loading…
Reference in New Issue
Block a user