all repos — felt @ 8dfa99c8bcfbfd13b312db2654ad6b20386c9b9b

virtual tabletop for dungeons and dragons (and similar) using Go, MongoDB, and websockets

sprite upload/delete is working, token form skeleton is there
Iris Lightshard nilix@nilfm.cc
PGP Signature
-----BEGIN PGP SIGNATURE-----

iQIzBAABCAAdFiEEkFh6dA+k/6CXFXU4O3+8IhROY5gFAmSmTtIACgkQO3+8IhRO
Y5jBBw//R23fXyffj+NqTmSsnxZ9R0Z1xijU3SyiVXbkibuxPatfZSllBJNqwXQx
LGt1SkBRo2L7JMDObtTg5x5hThqz+5HYx/KYIPfbQSCgQwveyetKTFUaT3FNsDOi
K80WnWsjQ9hxQyRkOVnSVoj0vJmQh8FuF++KVrq9etWU4DzpLmUOtjhaquJe3qGs
pwkmcT7YCgYuOWXWnat/yMw8WzjM9Kv3rwOIS0nR2YkuBSog38iNZrBcPWOi+sEq
XMJW/5yz+6ff/zzMzMasTs3RU8cZep5Au/K7jJznLzOmI/rw57oAX/MMdblGKoIl
2HepjqLXz+XUD5fyNehUwneQxl3REnaTD0PcplranwgDJKOET4GTj+A7OyP9z60b
GthQLSRS+A530ObPBr4RDUopVrlbJsM2RqfnltIstwsv44zlOG0vccdNUuQFBSED
YTRbEi8+0F/FvbinI9zKGJ2lYocFhwHdppLkQDicj4fwoaBmP2xkmkx5q4ZRpB5G
IXdaT3et3gh/1rsOXzXcgpfJFrVAGpk5XdBKMZLw88advX/b0GGX0sGS+vBfrjts
Vwu52HGoMCCeGDhUMRl3lOkgj+TsBuDoIk1A5V4hlM1t9xQJhkIBmFNLayD/QsEw
eLjLllz8xcoxBmrIXmQdUJ99u00H7lzTRlOXYRMogWW89RWUX5o=
=xPqD
-----END PGP SIGNATURE-----
commit

8dfa99c8bcfbfd13b312db2654ad6b20386c9b9b

parent

43d85c2abcdbea311b821eb434ec286519206f86

3 files changed, 88 insertions(+), 6 deletions(-)

jump to
M static/admin.jsstatic/admin.js

@@ -1,7 +1,10 @@

let adminToken = null; const adminWrapper = document.getElementById("adminWrapper"); const adminZone = document.getElementById("adminZone"); +const spriteZone = document.getElementById("spriteZone"); const createTableForm = document.getElementById("createTableForm"); +const createTokenForm = document.getElementById("createTokenForm"); +const tokenWrapper = document.getElementById("adminWrapper_tokens"); const newTableName = document.getElementById("newTableName"); const newTablePass = document.getElementById("newTablePass");

@@ -18,7 +21,13 @@ const mapImgs = await fetch (`/admin/api/upload/${name}/map/?passcode=${pass}`, {

method: 'GET', headers: headers, }); - + + const tokenImgs = await fetch(`/admin/api/upload/${name}/token/?passcode=${pass}`, { + method: 'GET', + headers: headers, + }); + + let infoHtml = "" if (res.ok) { document.getElementById("input_table_name").value = name; document.getElementById("input_table_pass").value = pass;

@@ -33,7 +42,7 @@ const imgs = await mapImgs.json();

infoHtml += "<ul>"; for (const i of imgs) { const parts = i.split("/"); - infoHtml += `<li>${parts[parts.length - 1]} <a href="${i}">view</a> <button onclick="sendMapImg('${i}');">Set</button> <button onclick="deleteImg('${i}')">Delete</button></li>\n`; + infoHtml += `<li>${parts[parts.length - 1]} <a href="${i}" target="_blank">view</a> <button onclick="sendMapImg('${i}');">Set</button> <button onclick="deleteImg('${i}')">Delete</button></li>\n`; } infoHtml += "</ul>"; } else {

@@ -41,6 +50,24 @@ infoHtml += "<label>Maps couldn't be retrieved</label>";

} adminZone.innerHTML = infoHtml; + let tokenListHTML = "<input id='token_img_upload' type='file'/><button onclick='uploadTokenImg()'>Upload Token</button><br/>"; + if (tokenImgs.ok) { + tokenListHTML += "<label>Available Sprites</label>"; + const tokens = await tokenImgs.json(); + tokenListHTML += "<ul>"; + for (const t of tokens) { + const parts = t.split("/"); + tokenListHTML += `<li>${parts[parts.length - 1]} <a href="${t}" target="_blank">view</a> <button onclick="deleteImg('${t}')">Delete</button></li>\n` + } + tokenListHTML += "</ul>"; + } else { + tokenListHTML += "<label>Sprites couldn't be retrieved</label>" + } + + spriteZone.innerHTML = tokenListHTML; + + tokenWrapper.style.display = "inline"; + // also, we have to fill and toggle the tokens window } else { console.log(res.status);

@@ -86,7 +113,30 @@ } catch (err) {

setErr(`${err.name}: ${err.message}`); } } - +async function uploadTokenImg() { + try { + var input = document.getElementById("token_img_upload"); + var data = new FormData(); + data.append('file', input.files[0]); + data.append('name', tableKey.name); + data.append('passcode', tableKey.passcode); + const headers = new Headers(); + headers.set('Authorization', 'Bearer ' + adminToken.access_token); + res = await fetch(`/admin/api/upload/${tableKey.name}/token/`, { + headers: headers, + method: "POST", + body: data, + }); + if (res.ok) { + // refresh so we can see the new entry in the list + getTable(tableKey.name, tableKey.passcode); + } else { + throw new Error("Something went wrong uploading the token sprite..."); + } + } catch (err) { + setErr(`${err.name}: ${err.message}`); + } +} async function deleteImg(url) { try { if (url.startsWith("/uploads/")) {

@@ -155,6 +205,7 @@ tableListHTML += `<li><a href="#" onclick="getTable('${t.name}','${t.passcode}');return false;">${t.name}</a></li>\n`

} tableListHTML += "</ul>" adminZone.innerHTML = tableListHTML; + tokenWrapper.style.display = "none"; } else { if (res.status == 404) { return;

@@ -210,6 +261,15 @@ }

if (newTablePass) { newTablePass.value = ""; } + } +} + +function setTokenCreateFormVisible(v) { + if (createTokenForm) { + createTokenForm.style.display = v ? "block" : "none"; + } + if (!v) { + // clear the form } }
M static/index.htmlstatic/index.html

@@ -74,7 +74,8 @@ <button type="submit" id="admin_login" onclick="doLogin();">Login</button>

</form> </details> <br/> - <details id="adminWrapper" class="ui_win admin_win" style="display:none;"><summary>table</summary> + <div id="adminWrapper" style="display:none;"> + <details id="admin_table_win" class="ui_win admin_win"><summary>table</summary> <button onclick="setTableCreateFormVisible(true)">New Table</button> <form onsubmit="return false" id="createTableForm" style="display:none;"> <label>Name<input id="newTableName"/></label><br/>

@@ -82,9 +83,29 @@ <label>Passcode<input id="newTablePass" type="password"/></label><br/>

<button type="submit" onclick="createTable()">Create</button> <button onclick="setTableCreateFormVisible(false)">Cancel</button> </form> + <div id="adminZone"></div> + </details><br/> + <div id="adminWrapper_tokens"> + <details id="admin_token_win" class="ui_win admin_win"><summary>tokens</summary> + <button onclick="setTokenCreateFormVisible(true)">New Token</button> + <form onsubmit="return false" id="createTokenForm"> + <label>Sprite<select id="token_combobox"></select></label><br/> + <label>Name<input id="newToken_name"/></label><br/> + <label>Width<input type="number" id="newToken_width" min="1" max="9999"/></label><br/> + <label>Height<input type="number" id="newToken_height" min="1" max="9999"/></label><br/> + <label>cX<input type="number" id="newToken_cx" min="0" max="9999"/></label><br/> + <label>cY<input type="number" id="newToken_cy" min="0" max="9999"/></label><br/> + <div id="tokenPreview_zone"></div> + <button type="submit" onlcick="createToken()">Create</button> + <button onclick="setTokenCreateFormVisible(false)">Cancel</button> + </form> + <div id="tokenZone"></div> + </details><br/> + <details id="admin_sprite_win" class="ui_win admin_win"><summary>sprites</summary> + <div id="spriteZone"></div> + </details> </div> - <div id="adminZone"></div> - </details><br/> + </div> </section> </nav> </body>
M static/style.cssstatic/style.css

@@ -23,6 +23,7 @@

body { background: url('./bg.png'); background-repeat: repeat; + background-attachment: fixed; } label {