diff --git a/Public/api.js b/Public/api.js index 16432c4..b74a49e 100644 --- a/Public/api.js +++ b/Public/api.js @@ -6,13 +6,25 @@ const apiPath = "/schafkopf" var useEnglishTexts = false +const headerKeyPassword = "password"; +const headerKeyToken = "token"; +const headerKeyName = "name"; +const headerKeyMail = "email"; + function webSocketPath() { const prefix = (window.location.protocol === "https:") ? "wss://" : "ws://" return prefix + window.location.host + apiPath + "/session/start" } -async function performRegisterPlayerRequest(name, password) { - return fetch(apiPath + "/player/register/" + name, { method: 'POST', body: password }) +async function performRegisterPlayerRequest(name, password, email) { + return fetch(apiPath + "/player/register", { + method: 'POST', + headers: { + [headerKeyName]: name, + [headerKeyPassword]: password, + [headerKeyMail]: email + } + }) .then(convertServerResponse) } @@ -44,6 +56,22 @@ async function performGetCurrentTableRequest(token) { .then(convertJsonResponse) } +async function performRecoveryEmailRequest(name) { + return fetch(apiPath + "/player/password/reset", { + method: 'POST', + headers: { [headerKeyName] : name }, + }) + .then(convertServerResponse) +} + +async function performResetPasswordRequest(token, password) { + return fetch(apiPath + "/player/reset", { + method: 'POST', + headers: { [headerKeyPassword] : password, [headerKeyToken] : token } + }) + .then(convertServerResponse) +} + async function performCreateTableRequest(token, name, visibility) { const vis = visibility ? "public" : "private"; return fetch(apiPath + "/table/create/" + vis + "/" + name, { method: 'POST', body: token }) @@ -160,4 +188,4 @@ function convertStateToString(state) { default: return state } -} \ No newline at end of file +} diff --git a/Public/elements.js b/Public/elements.js index d83a55b..cece7db 100644 --- a/Public/elements.js +++ b/Public/elements.js @@ -4,14 +4,11 @@ var playerName = "" var debugSessionToken = null -const debugMode = false // Does not load session token, to allow multiple players per browser const offlineText = "Offline" const missingPlayerText = "Leer" -const elementIdUserName = "user-name" -const elementIdUserPssword = "user-pwd" const elementIdPlayerCards = "player-cards" const elementIdLoginWindow = "login-window" const elementIdTopBar = "top-bar" @@ -28,22 +25,6 @@ const elementIdActionBar = "action-bar" const elementIdAvailableGamesList = "available-games-list" const elementIdGameSummary = "game-summary" -const localStorageTokenId = "token" - -function showDebugLogins() { - document.getElementById("login-window-inner").innerHTML += - "" + - "" + - "" + - "" -} - -function loginDebugUser(name) { - document.getElementById(elementIdUserName).value = name - document.getElementById(elementIdUserPssword).value = name - loginUser() -} - function setDisplayStyle(id, style) { document.getElementById(id).style.display = style } @@ -102,41 +83,6 @@ function setPlayerName(name) { playerName = name } -function getLoginName() { - return document.getElementById(elementIdUserName).value -} - -function clearLoginName() { - document.getElementById(elementIdUserName).value = "" -} - -function getLoginPassword() { - return document.getElementById(elementIdUserPssword).value -} - -function clearLoginPassword() { - document.getElementById(elementIdUserPssword).value = "" -} - -function getSessionToken() { - if (debugMode) { - return debugSessionToken - } - return localStorage.getItem(localStorageTokenId) -} - -function setSessionToken(token) { - if (debugMode) { - debugSessionToken = token - return - } - localStorage.setItem(localStorageTokenId, token) -} - -function deleteSessionToken() { - localStorage.removeItem(localStorageTokenId) -} - function setLoginError(text) { document.getElementById(elementIdLoginError).innerHTML = text } @@ -388,8 +334,8 @@ function textForAction(action) { return "Herz Solo" case "solo-schelln": return "Schelln Solo" -} -return action + } + return action } function performAction(action) { @@ -411,4 +357,4 @@ function showAvailableGames(games) { html += "
" + content + "
" } document.getElementById(elementIdAvailableGamesList).innerHTML = html -} \ No newline at end of file +} diff --git a/Public/game.js b/Public/game.js index ddace46..77f3c18 100644 --- a/Public/game.js +++ b/Public/game.js @@ -119,19 +119,20 @@ function deletePlayerAccount() { } function loadExistingSession() { - if (debugMode) { - showDebugLogins() - } - const token = getSessionToken() + const token = loadSessionToken() if (token == null) { - showBlankLogin() + window.location.href = "login.html"; return } resumeSessionRequest(token) .then(function(name) { setPlayerName(name) loadCurrentTable(token) - }).catch(showLoginWithError) + }).catch(function(error) { + console.log("Failed to resume session"); + console.log(error); + window.location.href = "login.html"; + }) } function loadCurrentTable(token) { @@ -265,4 +266,4 @@ function playCard(card) { .catch(function(error) { console.log(error) }) -} \ No newline at end of file +} diff --git a/Public/login.css b/Public/login.css new file mode 100644 index 0000000..cfe4376 --- /dev/null +++ b/Public/login.css @@ -0,0 +1,69 @@ + +#sheephead-logo { + color: rgb(0, 255, 0); + font-family: monospace, monospace; + white-space: pre; + text-align: center; + margin-bottom: 20px; +} + +#login-window input { + width: 100%; + margin-top: 6px; + margin-bottom: 16px; +} + +#login-window { + background-color: var(--standard-background); + height: 100%; + width: 100%; + top: 0; + left: 0; + display: table; + position: absolute; +} + +#login-window-vertical-center { + display: table-cell; + vertical-align: middle; +} + +#login-window-inner { + background-color: var(--element-background); + margin-left: auto; + margin-right: auto; + width: 300px; + border-radius: 10px; + border-style: solid; + border-width: thin; + border-color: var(--element-border); + padding: 10px; +} + +.login-buttons { + width: 100%; + margin-top: 5px; +} + +#login-error { + margin-top: 8px; +} + +label a { + color: var(--button-color); + margin-left: auto; +} + +label { + display: flex; + width: 100%; +} + +#user-hint { + padding-top: 5px; +} + +#field-note { + padding-left: 5px; + color: gray; +} diff --git a/Public/login.html b/Public/login.html new file mode 100644 index 0000000..17b43ab --- /dev/null +++ b/Public/login.html @@ -0,0 +1,70 @@ + + + + + Schafkopf + + + + + + + + +
+
+
+ + + + + + + + +
+
+
+
+ + + diff --git a/Public/login.js b/Public/login.js new file mode 100644 index 0000000..5e67705 --- /dev/null +++ b/Public/login.js @@ -0,0 +1,64 @@ +// Site-specific elements +const elementIdUserName = "user-name"; +const elementIdUserPassword = "user-pwd"; +const elementIdUserEmail = "user-email"; +const elementIdUserHint = "user-hint"; + +function getLoginName() { + return document.getElementById(elementIdUserName).value; +} + +function clearLoginName() { + document.getElementById(elementIdUserName).value = ""; +} + +function setLoginName(name) { + document.getElementById(elementIdUserName).value = name; +} + +function getLoginPassword() { + return document.getElementById(elementIdUserPassword).value; +} + +function clearLoginPassword() { + document.getElementById(elementIdUserPassword).value = ""; +} + +function setLoginPassword(password) { + document.getElementById(elementIdUserPassword).value = password; +} + +function getLoginEmail() { + return document.getElementById(elementIdUserEmail).value; +} + +/* + * Set a text to indicate an action, warning or other message below the registration field; + */ +function setTextHint(text) { + document.getElementById(elementIdUserHint).innerHTML = text; +} + +/* + * Attempt to reopen an existing session, either from the login or register page. + * + * It could be argued that this shouldn't be attempted for the registration page, + * but, a user may accidentally go to the registration page through the browser history, + * and then it will be convenient to be redirected to the game. + */ +function loadExistingSession() { + const token = loadSessionToken(); + if (token == null) { + console.log("No session token to load") + return; + } + resumeSessionRequest(token) + .then(function(name) { + storePlayerNameAndToken(name, token); + window.location.href = "list.html"; + }).catch(function(error) { + // We don't expect a session to resume if the registration page is shown, + // and if the token is expired on the login page we just fail quietly. + console.log(error); + }) +} diff --git a/Public/recovery.html b/Public/recovery.html new file mode 100644 index 0000000..9d8d07d --- /dev/null +++ b/Public/recovery.html @@ -0,0 +1,56 @@ + + + + + Schafkopf + + + + + + + +
+
+
+ + + + + +
+
+
+
+ + + diff --git a/Public/register.html b/Public/register.html new file mode 100644 index 0000000..a5773a9 --- /dev/null +++ b/Public/register.html @@ -0,0 +1,75 @@ + + + + + Schafkopf + + + + + + + + +
+
+ + + + + + + + + + + +
+
+
+ + + diff --git a/Public/reset.html b/Public/reset.html new file mode 100644 index 0000000..e9df5ee --- /dev/null +++ b/Public/reset.html @@ -0,0 +1,42 @@ + + + + + Schafkopf + + + + + + + +
+
+
+ + + + + +
+
+
+
+ + + diff --git a/Public/schafkopf.html b/Public/schafkopf.html index cb18ad7..95ec3bc 100644 --- a/Public/schafkopf.html +++ b/Public/schafkopf.html @@ -7,6 +7,7 @@ + @@ -72,25 +73,8 @@ - -
-
-
- - - - - - - - - -
-
-
-
- \ No newline at end of file + diff --git a/Public/storage.js b/Public/storage.js new file mode 100644 index 0000000..ac1b00b --- /dev/null +++ b/Public/storage.js @@ -0,0 +1,38 @@ +// Local storage element identifiers +const localStorageTokenId = "token"; +const localStoragePlayerName = "name"; + +// Can prevent loading of session token, to allow multiple players per browser +const debugMode = false + +/* + * Store the player name and session token in local storage. + * Parameter name: The user name of the player + * Parameter token: The session token for the player session + */ +function storePlayerNameAndToken(name, token) { + localStorage.setItem(localStoragePlayerName, name); + localStorage.setItem(localStorageTokenId, token); +} + +/* + * Get the last session token from local storage. + */ +function loadSessionToken() { + if (debugMode) { + return debugSessionToken + } + return localStorage.getItem(localStorageTokenId) +} + +function storeSessionToken(token) { + if (debugMode) { + debugSessionToken = token + return + } + localStorage.setItem(localStorageTokenId, token) +} + +function deleteSessionToken() { + localStorage.removeItem(localStorageTokenId) +} diff --git a/Public/style.css b/Public/style.css index 1255aa7..f69fc95 100644 --- a/Public/style.css +++ b/Public/style.css @@ -37,12 +37,6 @@ input { background-color: var(--standard-background); } -#login-window input { - width: 100%; - margin-top: 6px; - margin-bottom: 16px; -} - html * { font-family:-apple-system, BlinkMacSystemFont, "SF Hello", "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif; color: var(--text-color); @@ -60,49 +54,6 @@ body { position: absolute; } -#login-window { - background-color: var(--standard-background); - height: 100%; - width: 100%; - top: 0; - left: 0; - display: table; - position: absolute; -} - -#login-window-vertical-center { - display: table-cell; - vertical-align: middle; -} - -#login-window-inner { - background-color: var(--element-background); - margin-left: auto; - margin-right: auto; - width: 300px; - border-radius: 10px; - border-style: solid; - border-width: thin; - border-color: var(--element-border); - padding: 10px; -} - -#sheephead-logo { - color: rgb(0, 255, 0); - font-family: monospace, monospace; - white-space: pre; - text-align: center; -} - -.login-buttons { - width: 100%; - margin-top: 5px; -} - -#login-error { - margin-top: 8px; -} - #top-bar { height: 50px; background-color: var(--element-background); @@ -580,4 +531,4 @@ body { #player-card8 { grid-column: 8; z-index: 8; -} \ No newline at end of file +}