diff --git a/src/renderer/components/Application.css b/src/renderer/components/Application.css index d10ca79b..454a8aaa 100644 --- a/src/renderer/components/Application.css +++ b/src/renderer/components/Application.css @@ -2,6 +2,9 @@ height: calc(100vh - 22px); 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 { opacity: 0; diff --git a/src/renderer/components/Application.tsx b/src/renderer/components/Application.tsx index 4af53865..561efe60 100644 --- a/src/renderer/components/Application.tsx +++ b/src/renderer/components/Application.tsx @@ -8,14 +8,16 @@ import Landing from './landing/Landing' import Login from './login/Login' import Loader from './loader/Loader' import Settings from './settings/Settings' - -import './Application.css' import { StoreType } from '../redux/store' import { CSSTransition } from 'react-transition-group' -import { setCurrentView } from '../redux/actions/viewActions' +import { ViewActionDispatch } from '../redux/actions/viewActions' import { throttle } from 'lodash' import { readdir } from 'fs-extra' import { join } from 'path' +import Overlay from './overlay/Overlay' +import { OverlayPushAction, OverlayActionDispatch } from '../redux/actions/overlayActions' + +import './Application.css' declare const __static: string @@ -27,6 +29,7 @@ function setBackground(id: number) { interface ApplicationProps { currentView: View + overlayQueue: OverlayPushAction[] } interface ApplicationState { @@ -36,13 +39,15 @@ interface ApplicationState { workingView: View } -const mapState = (state: StoreType) => { +const mapState = (state: StoreType): Partial => { return { - currentView: state.currentView + currentView: state.currentView, + overlayQueue: state.overlayQueue } } const mapDispatch = { - setView: (x: View) => setCurrentView(x) + ...ViewActionDispatch, + ...OverlayActionDispatch } class Application extends React.Component { @@ -81,6 +86,10 @@ class Application extends React.Component { + return this.props.overlayQueue.length > 0 + } + private updateWorkingView = throttle(() => { this.setState({ ...this.state, @@ -109,7 +118,32 @@ class Application extends React.Component { - 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) } const diff = Date.now() - start @@ -132,7 +166,7 @@ class Application extends React.Component -
+
+ + + #upper { position: relative; transition: top 2s ease; - top: 0px; + top: 0; height: 77%; display: flex; } @@ -46,7 +46,7 @@ #landingContainer > #lower > #left { position: relative; transition: top 2s ease; - top: 0px; + top: 0; height: 100%; width: 33%; display: inline-flex; @@ -62,7 +62,7 @@ #landingContainer > #lower > #center { position: relative; transition: top 2s ease; - top: 0px; + top: 0; height: 100%; width: 34%; display: inline-flex; @@ -77,7 +77,7 @@ #landingContainer > #lower > #right { position: relative; transition: top 2s ease; - top: 0px; + top: 0; height: 100%; width: 33%; display: inline-flex; @@ -161,11 +161,11 @@ } #newsArticleTitle:hover, #newsArticleTitle:focus { - text-shadow: 0px 0px 20px white; + text-shadow: 0 0 20px white; } #newsArticleTitle:active { color: #c7c7c7; - text-shadow: 0px 0px 20px #c7c7c7; + text-shadow: 0 0 20px #c7c7c7; } /* News meta container. */ @@ -186,7 +186,7 @@ #newsArticleAuthor { display: inline-block; font-size: 10px; - padding: 0px 5px; + padding: 0 5px; font-weight: bold; border-radius: 2px; } @@ -226,7 +226,7 @@ #newsArticleContainer { width: calc(100% - 25px); height: 100%; - margin: 0px 0px 0px 25px; + margin: 0 0 0 25px; } /* Article content styles. */ @@ -234,7 +234,7 @@ font-size: 12px; overflow-y: scroll; height: 100%; - padding: 0px 15px 0px 15px; + padding: 0 15px 0 15px; } #newsArticleContentScrollable img, #newsArticleContentScrollable iframe { @@ -277,15 +277,15 @@ } .bbCodeSpoilerButton:hover, .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 { 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 { display: none; - padding: 15px 0px; + padding: 15px 0; border-bottom: 1px solid white; } @@ -312,13 +312,13 @@ -webkit-user-select: none; position: absolute; bottom: 15px; - right: 0px; + right: 0; } /* Navigation status span. */ #newsNavigationStatus { font-size: 12px; - margin: 0px 15px; + margin: 0 15px; } /* Left and right navigation button styles. */ @@ -334,8 +334,8 @@ #newsNavigateLeft:focus #newsNavigationLeftSVG, #newsNavigateRight:hover #newsNavigationRightSVG, #newsNavigateRight:focus #newsNavigationRightSVG { - filter: drop-shadow(0px 0px 2px #fff); - -webkit-filter: drop-shadow(0px 0px 2px #fff); + filter: drop-shadow(0px 0 2px #fff); + -webkit-filter: drop-shadow(0px 0 2px #fff); } #newsNavigateLeft:active #newsNavigationLeftSVG .arrowLine, #newsNavigateRight:active #newsNavigationRightSVG .arrowLine { @@ -343,8 +343,8 @@ } #newsNavigateLeft:active #newsNavigationLeftSVG, #newsNavigateRight:active #newsNavigationRightSVG { - filter: drop-shadow(0px 0px 2px #c7c7c7); - -webkit-filter: drop-shadow(0px 0px 2px #c7c7c7); + filter: drop-shadow(0px 0 2px #c7c7c7); + -webkit-filter: drop-shadow(0px 0 2px #c7c7c7); } #newsNavigateLeft:disabled #newsNavigationLeftSVG .arrowLine, #newsNavigateRight:disabled #newsNavigationRightSVG .arrowLine { @@ -397,11 +397,11 @@ } #newsErrorRetry:focus, #newsErrorRetry:hover { - text-shadow: 0px 0px 20px white; + text-shadow: 0 0 20px white; } #newsErrorRetry:active { 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); height: 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; position: relative; background-position: center; @@ -578,7 +578,7 @@ min-width: 135px; font-weight: 900; letter-spacing: 1px; - text-shadow: 0px 0px 20px black; + text-shadow: 0 0 20px black; position: absolute; right: 95px; text-align: right; @@ -615,7 +615,7 @@ height: 1px; width: 14px; background: rgb(255, 255, 255); - margin: 10px 0px; + margin: 10px 0; } /* Social media icon shared styles. */ @@ -642,7 +642,7 @@ .mediaButton { background: none; border: none; - padding: 0px; + padding: 0; display: flex; align-items: center; outline: none; @@ -745,7 +745,7 @@ font-size: 9px; letter-spacing: 1px; font-weight: bold; - text-shadow: 0px 0px 0px #bebcbb; + text-shadow: 0 0 0 #bebcbb; } /* Divider used on the bottom of the landing view. */ @@ -772,7 +772,7 @@ color: #949494; font-size: 8px; font-weight: 900; - text-shadow: 0px 0px 20px #949494; + text-shadow: 0 0 20px #949494; margin-left: 10px; } @@ -809,7 +809,7 @@ bottom: calc(100% + 15px); transform: translateX(-50%); margin-left: 50%; - box-shadow: 0px 0px 20px rgb(0, 0, 0); + box-shadow: 0 0 20px rgb(0, 0, 0); cursor: default; } #mojangStatusTooltip:after { @@ -840,7 +840,7 @@ #mojangStatusNEContainer { display: flex; align-items: center; - margin: 10px 0px; + margin: 10px 0; } /* White bar which surrounds the non essential service title. */ @@ -853,7 +853,7 @@ /* Non essential service title text. */ #mojangStatusNETitle { font-size: 10px; - padding: 0px 3px; + padding: 0 3px; text-align: center; letter-spacing: 1px; } @@ -869,7 +869,7 @@ font-size: 10px; letter-spacing: 1px; line-height: 12px; - padding: 6px 0px; + padding: 6px 0; } /* Displays the status of the mojang service. */ @@ -892,24 +892,24 @@ } #newsButton:hover #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 { 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:focus #newsButtonSVG { - filter: drop-shadow(0px 0px 2px #fff); - -webkit-filter: drop-shadow(0px 0px 2px #fff); + filter: drop-shadow(0px 0 2px #fff); + -webkit-filter: drop-shadow(0px 0 2px #fff); } #newsButton:active #newsButtonSVG .arrowLine { stroke: #c7c7c7; } #newsButton:active #newsButtonSVG { - filter: drop-shadow(0px 0px 2px #c7c7c7); - -webkit-filter: drop-shadow(0px 0px 2px #c7c7c7); + filter: drop-shadow(0px 0 2px #c7c7c7); + -webkit-filter: drop-shadow(0px 0 2px #c7c7c7); } #newsButton:disabled #newsButtonSVG .arrowLine { stroke: rgba(255, 255, 255, 0.75); @@ -938,7 +938,7 @@ color: white; font-weight: 900; letter-spacing: 2px; - text-shadow: 0px 0px 0px #bebcbb; + text-shadow: 0 0 0 #bebcbb; font-size: 11px; line-height: 30px; display: flex; @@ -963,19 +963,19 @@ cursor: pointer; font-weight: 900; letter-spacing: 2px; - text-shadow: 0px 0px 0px #bebcbb; + text-shadow: 0 0 0 #bebcbb; font-size: 20px; - padding: 0px; + padding: 0; transition: 0.25s ease; outline: none; } #launch_button:hover, #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 { 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 { color: #c7c7c7; @@ -999,7 +999,7 @@ #launch_progress_label { font-weight: 900; letter-spacing: 1px; - text-shadow: 0px 0px 0px #bebcbb; + text-shadow: 0 0 0 #bebcbb; font-size: 20px; min-width: 53.21px; max-width: 53.21px; @@ -1020,16 +1020,16 @@ outline: none; cursor: pointer; line-height: 24px; - padding: 0px; + padding: 0; transition: 0.25s ease; } #server_selection_button:hover, #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 { 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. */ diff --git a/src/renderer/components/overlay/Overlay.css b/src/renderer/components/overlay/Overlay.css new file mode 100644 index 00000000..e0a2af22 --- /dev/null +++ b/src/renderer/components/overlay/Overlay.css @@ -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); +} \ No newline at end of file diff --git a/src/renderer/components/overlay/Overlay.tsx b/src/renderer/components/overlay/Overlay.tsx new file mode 100644 index 00000000..e777324a --- /dev/null +++ b/src/renderer/components/overlay/Overlay.tsx @@ -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[] +} + +const mapState = (state: StoreType): Partial => { + return { + overlayQueue: state.overlayQueue + } +} + +class Overlay extends React.Component { + + private getGenericOverlay(action: PushGenericOverlayAction): JSX.Element { + return ( + <> + + + ) + } + + private getServerSelectOverlay(action: PushServerSelectOverlayAction): JSX.Element { + return ( + <> + + + ) + } + + 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 ( + <> + + + ) + } + } + + render(): JSX.Element { + return <> +
+ {this.getOverlayContent()} +
+ + } + +} + +export default connect(mapState)(Overlay) \ No newline at end of file diff --git a/src/renderer/components/overlay/account-select/AccountSelectOverlay.tsx b/src/renderer/components/overlay/account-select/AccountSelectOverlay.tsx new file mode 100644 index 00000000..f1cf6517 --- /dev/null +++ b/src/renderer/components/overlay/account-select/AccountSelectOverlay.tsx @@ -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 ( + <> +
+ Select an Account +
+
+ {/* Accounts populated here. */} +
+
+
+ +
+ +
+
+
+ + ) + } + +} \ No newline at end of file diff --git a/src/renderer/components/overlay/generic-overlay/GenericOverlay.css b/src/renderer/components/overlay/generic-overlay/GenericOverlay.css new file mode 100644 index 00000000..9829e4ba --- /dev/null +++ b/src/renderer/components/overlay/generic-overlay/GenericOverlay.css @@ -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); +} \ No newline at end of file diff --git a/src/renderer/components/overlay/generic-overlay/GenericOverlay.tsx b/src/renderer/components/overlay/generic-overlay/GenericOverlay.tsx new file mode 100644 index 00000000..3770b82a --- /dev/null +++ b/src/renderer/components/overlay/generic-overlay/GenericOverlay.tsx @@ -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) => void + dismissCallback?: (event: React.MouseEvent) => void +} + +const mapDispatch = { + ...OverlayActionDispatch +} + +type InternalGenericOverlayProps = GenericOverlayProps & typeof mapDispatch + +class GenericOverlay extends React.Component { + + private getAcknowledgeText = (): string => { + return this.props.acknowledgeText || 'OK' + } + + private getDismissText = (): string => { + return this.props.dismissText || 'Dismiss' + } + + private onAcknowledgeClick = (event: React.MouseEvent): void => { + if(this.props.acknowledgeCallback) { + this.props.acknowledgeCallback(event) + } + this.props.popOverlayContent() + } + + private onDismissClick = (event: React.MouseEvent): void => { + if(this.props.dismissCallback) { + this.props.dismissCallback(event) + } + this.props.popOverlayContent() + } + + render(): JSX.Element { + return <> +
+ {this.props.title} + {this.props.description} +
+ +
+ { this.props.dismissible + ? + : <> + } +
+
+
+ + } + +} + +export default connect(undefined, mapDispatch)(GenericOverlay) \ No newline at end of file diff --git a/src/renderer/components/overlay/server-select/ServerSelectOverlay.tsx b/src/renderer/components/overlay/server-select/ServerSelectOverlay.tsx new file mode 100644 index 00000000..68f54e88 --- /dev/null +++ b/src/renderer/components/overlay/server-select/ServerSelectOverlay.tsx @@ -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 { + + render(): JSX.Element { + return ( + <> +
+ Available Servers +
+
+ {/* Server listings populated here. */} +
+
+
+ +
+ +
+
+
+ + ) + } + +} \ No newline at end of file diff --git a/src/renderer/components/overlay/shared-select/SharedSelect.css b/src/renderer/components/overlay/shared-select/SharedSelect.css new file mode 100644 index 00000000..0d007913 --- /dev/null +++ b/src/renderer/components/overlay/shared-select/SharedSelect.css @@ -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); +} \ No newline at end of file diff --git a/src/renderer/index.tsx b/src/renderer/index.tsx index d5e1528b..6f20ddd8 100644 --- a/src/renderer/index.tsx +++ b/src/renderer/index.tsx @@ -15,7 +15,10 @@ document.body.appendChild(mainElement) ReactDOM.render( - + , mainElement diff --git a/src/renderer/redux/actions/overlayActions.ts b/src/renderer/redux/actions/overlayActions.ts new file mode 100644 index 00000000..a4bedb8b --- /dev/null +++ b/src/renderer/redux/actions/overlayActions.ts @@ -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 extends OverlayAction { + overlayContent: OverlayContent + timestamp: number + showNext: boolean + payload: T +} + +export interface PushGenericOverlayAction extends OverlayPushAction { + overlayContent: OverlayContent.GENERIC + payload: GenericOverlayProps +} + +export interface PushServerSelectOverlayAction extends OverlayPushAction { + 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() +} \ No newline at end of file diff --git a/src/renderer/redux/actions/viewActions.ts b/src/renderer/redux/actions/viewActions.ts index 3953e7a8..1558916f 100644 --- a/src/renderer/redux/actions/viewActions.ts +++ b/src/renderer/redux/actions/viewActions.ts @@ -14,4 +14,8 @@ export function setCurrentView(view: View): ChangeViewAction { type: ViewActionType.ChangeView, payload: view } +} + +export const ViewActionDispatch = { + setView: (x: View): ChangeViewAction => setCurrentView(x) } \ No newline at end of file diff --git a/src/renderer/redux/reducers/index.ts b/src/renderer/redux/reducers/index.ts index d5cc9281..29c0f2b7 100644 --- a/src/renderer/redux/reducers/index.ts +++ b/src/renderer/redux/reducers/index.ts @@ -1,8 +1,10 @@ import { combineReducers } from 'redux' import ViewReducer from './viewReducer' import AppReducer from './appReducer' +import OverlayReducer from './overlayReducer' export default combineReducers({ currentView: ViewReducer, + overlayQueue: OverlayReducer, app: AppReducer }) \ No newline at end of file diff --git a/src/renderer/redux/reducers/overlayReducer.ts b/src/renderer/redux/reducers/overlayReducer.ts new file mode 100644 index 00000000..23564430 --- /dev/null +++ b/src/renderer/redux/reducers/overlayReducer.ts @@ -0,0 +1,30 @@ +import { Reducer } from 'redux' +import { OverlayAction, OverlayActionType, OverlayPushAction } from '../actions/overlayActions' + +const defaultOverlayQueue: OverlayPushAction[] = [] + +const OverlayReducer: Reducer[], OverlayAction> = (state = defaultOverlayQueue, action) => { + switch(action.type) { + case OverlayActionType.PushContent: + if((action as OverlayPushAction).showNext && state.length > 0) { + return [ + state[0], + action as OverlayPushAction, + ...state.slice(1) + ] + } else { + return [ + ...state, + action as OverlayPushAction + ] + } + case OverlayActionType.PopContent: + return [ + ...state.slice(1) + ] + + } + return state +} + +export default OverlayReducer \ No newline at end of file diff --git a/src/renderer/redux/reducers/viewReducer.ts b/src/renderer/redux/reducers/viewReducer.ts index 32793e22..f279784f 100644 --- a/src/renderer/redux/reducers/viewReducer.ts +++ b/src/renderer/redux/reducers/viewReducer.ts @@ -2,7 +2,7 @@ import { Reducer } from 'redux' import { View } from '../../meta/Views' import { ChangeViewAction, ViewActionType } from '../actions/viewActions' -const defaultView = View.LOGIN +const defaultView = View.LANDING const ViewReducer: Reducer = (state = defaultView, action) => { switch(action.type) { diff --git a/test/assets/MojangIndexProcessorTest.ts b/test/assets/MojangIndexProcessorTest.ts index 9bef7804..b3f50274 100644 --- a/test/assets/MojangIndexProcessorTest.ts +++ b/test/assets/MojangIndexProcessorTest.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ import nock from 'nock' import { URL } from 'url' import { MojangIndexProcessor } from 'common/asset/processor/MojangIndexProcessor' diff --git a/test/mojang/mojangTest.ts b/test/mojang/mojangTest.ts index 8283876c..f7802455 100644 --- a/test/mojang/mojangTest.ts +++ b/test/mojang/mojangTest.ts @@ -1,8 +1,8 @@ -import { Mojang } from "common/mojang/mojang" +import { Mojang } from 'common/mojang/mojang' import { expect } from 'chai' import nock from 'nock' -import { Session } from "common/mojang/model/auth/Session" -import { MojangResponseCode } from "common/mojang/model/internal/Response" +import { Session } from 'common/mojang/model/auth/Session' +import { MojangResponseCode } from 'common/mojang/model/internal/Response' function expectMojangResponse(res: any, responseCode: MojangResponseCode, negate = false) { expect(res).to.not.be.an('error') @@ -29,7 +29,7 @@ describe('Mojang Errors', () => { .get('/check') .reply(500, 'Service temprarily offline.') - const res = await Mojang.status(); + const res = await Mojang.status() expectMojangResponse(res, MojangResponseCode.SUCCESS, true) expect(res.data).to.be.an('array') expect(res.data).to.deep.equal(defStatusHack) @@ -65,7 +65,7 @@ describe('Mojang Status', () => { .get('/check') .reply(200, defStatusHack) - const res = await Mojang.status(); + const res = await Mojang.status() expectMojangResponse(res, MojangResponseCode.SUCCESS) expect(res.data).to.be.an('array') expect(res.data).to.deep.equal(defStatusHack)