Added UI and implementation for the account settings tab.

Features:
* Add a new account.
* Switch accounts.
* Log out of an account.

Also added a cancel button to the login UI which is only shown when a user is adding an account. In that case, the operation should be and is cancellable.
This commit is contained in:
Daniel Scalzi 2018-05-30 22:22:17 -04:00
parent 2dcbb45bdb
commit 91c842dd40
No known key found for this signature in database
GPG Key ID: 5CA2F145B63535F9
7 changed files with 442 additions and 5 deletions

View File

@ -387,6 +387,68 @@ body, button {
background: rgba(0, 0, 0, 0.50); background: rgba(0, 0, 0, 0.50);
} }
/* Login cancel button styles. */
#loginCancelContainer {
position: absolute;
top: 5%;
right: 5%;
}
/* Login cancel button styles. */
#loginCancelButton {
background: none;
border: none;
outline: none;
cursor: pointer;
transition: 0.25s ease;
}
#loginCancelButton:hover #loginCancelIcon,
#loginCancelButton:hover #loginCancelText,
#loginCancelButton:focus #loginCancelIcon,
#loginCancelButton:focus #loginCancelText {
text-shadow: 0px 0px 20px white;
}
#loginCancelButton:hover #loginCancelIcon,
#loginCancelButton:focus #loginCancelIcon {
box-shadow: 0px 0px 20px white;
}
#loginCancelButton:active #loginCancelIcon,
#loginCancelButton:active #loginCancelText {
text-shadow: 0px 0px 20px rgba(255, 255, 255, 0.75);
color: rgba(255, 255, 255, 0.75);
border-color: rgba(255, 255, 255, 0.75);
}
#loginCancelButton:active #loginCancelIcon {
box-shadow: 0px 0px 20px rgba(255, 255, 255, 0.75);
}
#loginCancelButton:disabled {
pointer-events: none;
}
#loginCancelButton:disabled #loginCancelIcon,
#loginCancelButton:disabled #loginCancelText {
color: rgba(255, 255, 255, 0.75);
border-color: rgba(255, 255, 255, 0.75);
}
/* The X in a circle icon for the cancel button. */
#loginCancelIcon {
border-radius: 50%;
border: 1px solid white;
box-sizing: border-box;
height: 30px;
width: 30px;
font-size: 19px;
line-height: 30px;
margin: 0 auto;
margin-bottom: 5px;
transition: 0.25s ease;
}
/* Text for the login cancel button. */
#loginCancelText {
font-size: 15px;
transition: 0.25s ease;
}
/* Login content wrapper. */ /* Login content wrapper. */
#loginContent { #loginContent {
display: flex; display: flex;
@ -816,6 +878,7 @@ body, button {
* * * *
******************************************************************************/ ******************************************************************************/
/* Main settings container. */
#settingsContainer { #settingsContainer {
position: relative; position: relative;
height: 100%; height: 100%;
@ -823,6 +886,7 @@ body, button {
background: rgba(0, 0, 0, 0.50); background: rgba(0, 0, 0, 0.50);
} }
/* Left hand side of the settings UI, for navigation. */
#settingsContainerLeft { #settingsContainerLeft {
height: 100%; height: 100%;
width: 25%; width: 25%;
@ -830,20 +894,22 @@ body, button {
box-sizing: border-box; box-sizing: border-box;
} }
/* Settings navigation container. */
#settingsNavContainer { #settingsNavContainer {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
/* Navigation header styles. */
#settingsNavHeader { #settingsNavHeader {
display: flex; display: flex;
justify-content: center; justify-content: center;
} }
#settingsNavHeaderText { #settingsNavHeaderText {
font-size: 20px; font-size: 20px;
} }
/* Navigation items outer container. */
#settingsNavItemsContainer { #settingsNavItemsContainer {
height: 100%; height: 100%;
display: flex; display: flex;
@ -852,11 +918,13 @@ body, button {
box-sizing: border-box; box-sizing: border-box;
} }
/* Navigation items content container. */
#settingsNavItemsContent { #settingsNavItemsContent {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
/* Navigation item shared styles. */
.settingsNavItem { .settingsNavItem {
background: none; background: none;
border: none; border: none;
@ -880,23 +948,36 @@ body, button {
text-shadow: none; text-shadow: none;
} }
/* Right hand side of the settings container, for tabs. */
#settingsContainerRight { #settingsContainerRight {
height: 100%; height: 100%;
width: 75%; width: 75%;
padding: 5% 0%; padding-top: 5%;
box-sizing: border-box; box-sizing: border-box;
} }
/* Settings tab shared styles. */
.settingsTab { .settingsTab {
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow-y: auto;
}
.settingsTab::-webkit-scrollbar {
width: 2px;
}
.settingsTab::-webkit-scrollbar-track {
display: none;
}
.settingsTab::-webkit-scrollbar-thumb {
border-radius: 10px;
box-shadow: inset 0 0 10px rgba(255, 255, 255, 0.50);
} }
/* Tab header shared styles. */
.settingsTabHeader { .settingsTabHeader {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.settingsTabHeaderText { .settingsTabHeaderText {
font-size: 20px; font-size: 20px;
font-family: 'Avenir Medium'; font-family: 'Avenir Medium';
@ -905,6 +986,163 @@ body, button {
font-size: 12px; font-size: 12px;
} }
/* * *
* Settings View (Account Tab)
* * */
/* Add account button styles. */
#settingsAddAccountContainer {
margin-top: 20px;
}
#settingsAddAccount {
background: rgba(0, 0, 0, 0.25);
border: 1px solid rgba(126, 126, 126, 0.57);
border-radius: 3px;
height: 50px;
width: 75%;
text-align: left;
padding: 0px 50px;
cursor: pointer;
outline: none;
transition: 0.25s ease;
}
#settingsAddAccount:hover,
#settingsAddAccount:focus {
background: rgba(54, 54, 54, 0.25);
text-shadow: 0px 0px 20px white;
}
/* Settings auth accounts header. */
#settingsCurrentAccountsHeader {
margin: 20px 0px;
}
#settingsCurrentAccountsHeaderText {
font-size: 16px;
}
/* Auth account list container styles. */
#settingsCurrentAccounts {
margin-bottom: 5%;
}
#settingsCurrentAccounts > .settingsAuthAccount:not(:last-child) {
margin-bottom: 10px;
}
#settingsCurrentAccounts > .settingsAuthAccount:not(:first-child) {
margin-top: 10px;
}
/* Auth account shared styles. */
.settingsAuthAccount {
display: flex;
width: 75%;
background: rgba(0, 0, 0, 0.25);
border-radius: 3px;
border: 1px solid rgba(126, 126, 126, 0.57);
}
/* Left hand side of an auth account element, for the skin image. */
.settingsAuthAccountLeft {
padding: 5px 5px 5px 20px;
}
/* Image of the auth account's skin. */
.settingsAuthAccountImage {
height: 115px;
}
/* Right hand side of the auth account, for info + actions. */
.settingsAuthAccountRight {
display: flex;
width: 100%;
}
/* Account details container. */
.settingsAuthAccountDetails {
display: flex;
flex-direction: column;
justify-content: center;
margin-left: 20px;
width: 100%;
}
.settingsAuthAccountDetails > *:not(:last-child) {
margin-bottom: 20px;
}
/* Account detail element styles. */
.settingsAuthAccountDetailPane {
display: flex;
flex-direction: column;
}
.settingsAuthAccountDetailTitle {
font-size: 12px;
color: grey;
font-weight: bold;
font-family: 'Avenir Medium';
}
.settingsAuthAccountDetailValue {
font-size: 14px;
}
/* Account actions container. */
.settingsAuthAccountActions {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: flex-end;
padding: 10px;
}
/* Account select button shared styles. */
.settingsAuthAccountSelect {
opacity: 0;
border: none;
white-space: nowrap;
background: none;
font-family: 'Avenir Medium';
outline: none;
transition: 0.25s ease;
}
.settingsAuthAccountSelect:hover:not([selected]),
.settingsAuthAccountSelect:focus:not([selected]) {
text-shadow: 0px 0px 20px white, 0px 0px 20px white;
cursor: pointer;
}
.settingsAuthAccount:hover .settingsAuthAccountSelect:not([selected]),
.settingsAuthAccountSelect[selected] {
opacity: 1;
}
.settingsAuthAccountSelect[selected] {
pointer-events: none;
}
/* Account logout button shared styles. */
.settingsAuthAccountLogOut {
opacity: 0;
border: 1px solid rgb(241, 55, 55);
color: rgb(241, 55, 55);
background: none;
font-size: 12px;
border-radius: 3px;
font-family: 'Avenir Medium';
transition: 0.25s ease;
cursor: pointer;
outline: none;
}
.settingsAuthAccountLogOut:hover,
.settingsAuthAccountLogOut:focus {
box-shadow: 0px 0px 20px rgb(241, 55, 55);
background: rgba(241, 55, 55, 0.25);
}
.settingsAuthAccountLogOut:active {
box-shadow: 0px 0px 20px rgb(185, 47, 47);
background: rgba(185, 47, 47, 0.25);
border: 1px solid rgb(185, 47, 47);
color: rgb(185, 47, 47);
}
.settingsAuthAccount:hover .settingsAuthAccountLogOut {
opacity: 1;
}
/******************************************************************************* /*******************************************************************************
* * * *
* Landing View (Structural Styles) * * Landing View (Structural Styles) *

View File

@ -116,6 +116,7 @@ document.getElementById('launch_button').addEventListener('click', function(e){
// Bind settings button // Bind settings button
document.getElementById('settingsMediaButton').onclick = (e) => { document.getElementById('settingsMediaButton').onclick = (e) => {
prepareSettings()
switchView(getCurrentView(), VIEWS.settings) switchView(getCurrentView(), VIEWS.settings)
} }

View File

@ -7,6 +7,8 @@ const basicEmail = /^\S+@\S+\.\S+$/
//const validEmail = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i //const validEmail = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i
// Login Elements // Login Elements
const loginCancelContainer = document.getElementById('loginCancelContainer')
const loginCancelButton = document.getElementById('loginCancelButton')
const loginEmailError = document.getElementById('loginEmailError') const loginEmailError = document.getElementById('loginEmailError')
const loginUsername = document.getElementById('loginUsername') const loginUsername = document.getElementById('loginUsername')
const loginPasswordError = document.getElementById('loginPasswordError') const loginPasswordError = document.getElementById('loginPasswordError')
@ -139,6 +141,7 @@ function loginLoading(v){
*/ */
function formDisabled(v){ function formDisabled(v){
loginDisabled(v) loginDisabled(v)
loginCancelButton.disabled = v
loginUsername.disabled = v loginUsername.disabled = v
loginPassword.disabled = v loginPassword.disabled = v
if(v){ if(v){
@ -215,6 +218,23 @@ function resolveError(err){
} }
} }
let loginViewOnSuccess = VIEWS.landing
let loginViewOnCancel = VIEWS.settings
function loginCancelEnabled(val){
if(val){
$(loginCancelContainer).show()
} else {
$(loginCancelContainer).hide()
}
}
loginCancelButton.onclick = (e) => {
switchView(getCurrentView(), loginViewOnCancel, 500, 500, () => {
loginCancelEnabled(false)
})
}
// Disable default form behavior. // Disable default form behavior.
loginForm.onsubmit = () => { return false } loginForm.onsubmit = () => { return false }
@ -233,7 +253,13 @@ loginButton.addEventListener('click', () => {
$('.checkmark').toggle() $('.checkmark').toggle()
//console.log(value) //console.log(value)
setTimeout(() => { setTimeout(() => {
switchView(VIEWS.login, VIEWS.landing, 500, 500, () => { switchView(VIEWS.login, loginViewOnSuccess, 500, 500, () => {
// Temporary workaround
if(loginViewOnSuccess === VIEWS.settings){
prepareSettings()
}
loginViewOnSuccess = VIEWS.landing // Reset this for good measure.
loginCancelEnabled(false) // Reset this for good measure.
loginUsername.value = '' loginUsername.value = ''
loginPassword.value = '' loginPassword.value = ''
$('.circle-loader').toggleClass('load-complete') $('.circle-loader').toggleClass('load-complete')

View File

@ -1,5 +1,15 @@
const settingsAddAccount = document.getElementById('settingsAddAccount')
const settingsCurrentAccounts = document.getElementById('settingsCurrentAccounts')
/**
* General Settings Functions
*/
let selectedTab = 'settingsTabAccount' let selectedTab = 'settingsTabAccount'
/**
* Bind functionality for the settings navigation items.
*/
function setupSettingsTabs(){ function setupSettingsTabs(){
Array.from(document.getElementsByClassName('settingsNavItem')).map((val) => { Array.from(document.getElementsByClassName('settingsNavItem')).map((val) => {
val.onclick = (e) => { val.onclick = (e) => {
@ -22,4 +32,148 @@ function setupSettingsTabs(){
}) })
} }
/**
* Account Management Tab
*/
// Bind the add account button.
settingsAddAccount.onclick = (e) => {
switchView(getCurrentView(), VIEWS.login, 500, 500, () => {
loginViewOnCancel = VIEWS.settings
loginViewOnSuccess = VIEWS.settings
loginCancelEnabled(true)
})
}
/**
* Bind functionality for the account selection buttons. If another account
* is selected, the UI of the previously selected account will be updated.
*/
function bindAuthAccountSelect(){
Array.from(document.getElementsByClassName('settingsAuthAccountSelect')).map((val) => {
val.onclick = (e) => {
if(val.hasAttribute('selected')){
return
}
const selectBtns = document.getElementsByClassName('settingsAuthAccountSelect')
for(let i=0; i<selectBtns.length; i++){
if(selectBtns[i].hasAttribute('selected')){
selectBtns[i].removeAttribute('selected')
selectBtns[i].innerHTML = 'Select Account'
}
}
val.setAttribute('selected', '')
val.innerHTML = 'Selected Account &#10004;'
ConfigManager.setSelectedAccount(val.closest('.settingsAuthAccount').getAttribute('uuid'))
ConfigManager.save()
updateSelectedAccount(ConfigManager.getSelectedAccount())
}
})
}
/**
* Bind functionality for the log out button. If the logged out account was
* the selected account, another account will be selected and the UI will
* be updated accordingly.
*/
function bindAuthAccountLogOut(){
Array.from(document.getElementsByClassName('settingsAuthAccountLogOut')).map((val) => {
val.onclick = (e) => {
const parent = val.closest('.settingsAuthAccount')
const uuid = parent.getAttribute('uuid')
const prevSelAcc = ConfigManager.getSelectedAccount()
AuthManager.removeAccount(uuid).then(() => {
if(uuid === prevSelAcc.uuid){
const selAcc = ConfigManager.getSelectedAccount()
refreshAuthAccountSelected(selAcc.uuid)
updateSelectedAccount(selAcc)
}
})
$(parent).fadeOut(250, () => {
parent.remove()
})
}
})
}
/**
* Refreshes the status of the selected account on the auth account
* elements.
*
* @param {string} uuid The UUID of the new selected account.
*/
function refreshAuthAccountSelected(uuid){
Array.from(document.getElementsByClassName('settingsAuthAccount')).map((val) => {
const selBtn = val.getElementsByClassName('settingsAuthAccountSelect')[0]
if(uuid === val.getAttribute('uuid')){
selBtn.setAttribute('selected', '')
selBtn.innerHTML = 'Selected Account &#10004;'
} else {
if(selBtn.hasAttribute('selected')){
selBtn.removeAttribute('selected')
}
selBtn.innerHTML = 'Select Account'
}
})
}
/**
* Add auth account elements for each one stored in the authentication database.
*/
function populateAuthAccounts(){
const authAccounts = ConfigManager.getAuthAccounts()
const authKeys = Object.keys(authAccounts)
const selectedUUID = ConfigManager.getSelectedAccount().uuid
let authAccountStr = ``
authKeys.map((val) => {
const acc = authAccounts[val]
authAccountStr += `<div class="settingsAuthAccount" uuid="${acc.uuid}">
<div class="settingsAuthAccountLeft">
<img class="settingsAuthAccountImage" alt="${acc.displayName}" src="https://crafatar.com/renders/body/${acc.uuid}?scale=3&default=MHF_Steve&overlay">
</div>
<div class="settingsAuthAccountRight">
<div class="settingsAuthAccountDetails">
<div class="settingsAuthAccountDetailPane">
<div class="settingsAuthAccountDetailTitle">Username</div>
<div class="settingsAuthAccountDetailValue">${acc.displayName}</div>
</div>
<div class="settingsAuthAccountDetailPane">
<div class="settingsAuthAccountDetailTitle">${acc.displayName === acc.username ? 'UUID' : 'Email'}</div>
<div class="settingsAuthAccountDetailValue">${acc.displayName === acc.username ? acc.uuid : acc.username}</div>
</div>
</div>
<div class="settingsAuthAccountActions">
<button class="settingsAuthAccountSelect" ${selectedUUID === acc.uuid ? 'selected>Selected Account &#10004;' : '>Select Account'}</button>
<div class="settingsAuthAccountWrapper">
<button class="settingsAuthAccountLogOut">Log Out</button>
</div>
</div>
</div>
</div>`
})
settingsCurrentAccounts.innerHTML = authAccountStr
}
function prepareAccountsTab() {
populateAuthAccounts()
bindAuthAccountSelect()
bindAuthAccountLogOut()
}
/**
* Settings preparation functions.
*/
/**
* Prepare the entire settings UI.
*/
function prepareSettings() {
setupSettingsTabs() setupSettingsTabs()
prepareAccountsTab()
}
// Prepare the settings UI on startup.
prepareSettings()

View File

@ -145,6 +145,7 @@ async function validateSelectedAccount(){
setOverlayHandler(() => { setOverlayHandler(() => {
document.getElementById('loginUsername').value = selectedAcc.username document.getElementById('loginUsername').value = selectedAcc.username
validateEmail(selectedAcc.username) validateEmail(selectedAcc.username)
loginViewOnSuccess = getCurrentView()
switchView(getCurrentView(), VIEWS.login) switchView(getCurrentView(), VIEWS.login)
toggleOverlay(false) toggleOverlay(false)
}) })

View File

@ -1,4 +1,10 @@
<div id="loginContainer" style="display: none;"> <div id="loginContainer" style="display: none;">
<div id="loginCancelContainer" style="display: none;">
<button id="loginCancelButton">
<div id="loginCancelIcon">X</div>
<span id="loginCancelText">Cancel</span>
</button>
</div>
<div id="loginContent"> <div id="loginContent">
<form id="loginForm"> <form id="loginForm">
<img id="loginImageSeal" src="assets/images/WesterosSealCircle.png"/> <img id="loginImageSeal" src="assets/images/WesterosSealCircle.png"/>

View File

@ -20,6 +20,17 @@
<span class="settingsTabHeaderText">Account Settings</span> <span class="settingsTabHeaderText">Account Settings</span>
<span class="settingsTabHeaderDesc">Add new accounts or manage existing ones.</span> <span class="settingsTabHeaderDesc">Add new accounts or manage existing ones.</span>
</div> </div>
<div id="settingsAddAccountContainer">
<button id="settingsAddAccount">
<span id="settingsAddAccountText">&#43; Add Account</span>
</button>
</div>
<div id="settingsCurrentAccountsHeader">
<span id="settingsCurrentAccountsHeaderText">Current Accounts</span>
</div>
<div id="settingsCurrentAccounts">
<!-- Auth accounts populated here. -->
</div>
</div> </div>
<div id="settingsTabMinecraft" class="settingsTab" style="display: none;"> <div id="settingsTabMinecraft" class="settingsTab" style="display: none;">
<div class="settingsTabHeader"> <div class="settingsTabHeader">