mirror of
https://github.com/AlecM33/Werewolf.git
synced 2025-12-26 15:57:50 +01:00
confirmation module, tutorial updates
This commit is contained in:
BIN
client/src/images/tutorial/player-view.gif
Normal file
BIN
client/src/images/tutorial/player-view.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 599 KiB |
63
client/src/modules/front_end_components/Confirmation.js
Normal file
63
client/src/modules/front_end_components/Confirmation.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import { toast } from './Toast.js';
|
||||
|
||||
export const Confirmation = (message, onYes) => {
|
||||
document.querySelector('#confirmation')?.remove();
|
||||
document.querySelector('#confirmation-background')?.remove();
|
||||
|
||||
let confirmation = document.createElement('div');
|
||||
confirmation.setAttribute('id', 'confirmation');
|
||||
confirmation.innerHTML =
|
||||
`<div id="confirmation-message"></div>
|
||||
<div class="confirmation-buttons">
|
||||
<button id="confirmation-cancel-button" class="app-button cancel">Cancel</button>
|
||||
<button id="confirmation-yes-button" class="app-button">Yes</button>
|
||||
</div>`;
|
||||
|
||||
confirmation.querySelector('#confirmation-message').innerText = message;
|
||||
|
||||
let background = document.createElement('div');
|
||||
background.setAttribute('id', 'confirmation-background');
|
||||
|
||||
const cancelHandler = () => {
|
||||
confirmation.remove();
|
||||
background.remove();
|
||||
confirmation = null;
|
||||
background = null;
|
||||
};
|
||||
|
||||
const confirmHandler = () => {
|
||||
const button = confirmation.querySelector('#confirmation-yes-button');
|
||||
button.classList.add('disabled');
|
||||
button.innerText = '...';
|
||||
button.removeEventListener('click', confirmHandler);
|
||||
new Promise((resolve, reject) => {
|
||||
try {
|
||||
resolve(onYes());
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
}).then((res) => {
|
||||
if (res && typeof res === 'string') {
|
||||
toast(res, 'success', true, true, 'medium', false);
|
||||
}
|
||||
confirmation.remove();
|
||||
background.remove();
|
||||
confirmation = null;
|
||||
background = null;
|
||||
}).catch((e) => {
|
||||
if (typeof e === 'string') {
|
||||
toast(e, 'error', true, true, 'medium', false);
|
||||
}
|
||||
button.addEventListener('click', confirmHandler);
|
||||
button.classList.remove('disabled');
|
||||
button.innerText = 'Yes';
|
||||
});
|
||||
};
|
||||
|
||||
confirmation.querySelector('#confirmation-cancel-button').addEventListener('click', cancelHandler);
|
||||
confirmation.querySelector('#confirmation-yes-button').addEventListener('click', confirmHandler);
|
||||
background.addEventListener('click', cancelHandler);
|
||||
|
||||
document.body.appendChild(background);
|
||||
document.body.appendChild(confirmation);
|
||||
};
|
||||
@@ -63,8 +63,8 @@ export const HTMLFragments = {
|
||||
<p id='role-description'></p>
|
||||
</div>
|
||||
<div id='game-role-back'>
|
||||
<h4>Double-tap here to show your role</h4>
|
||||
<p>(Double-tap here again to hide)</p>
|
||||
<h4>Double-click here to show your role</h4>
|
||||
<p>(Double-click here again to hide)</p>
|
||||
</div>
|
||||
<div id='game-people-container'>
|
||||
<label id='players-alive-label'></label>
|
||||
@@ -141,8 +141,8 @@ export const HTMLFragments = {
|
||||
<p id='role-description'></p>
|
||||
</div>
|
||||
<div id='game-role-back'>
|
||||
<h4>Double-tap here to show your role</h4>
|
||||
<p>(Double-tap here again to hide)</p>
|
||||
<h4>Double-click here to show your role</h4>
|
||||
<p>(Double-click here again to hide)</p>
|
||||
</div>
|
||||
<div id='game-people-container'>
|
||||
<label id='players-alive-label'></label>
|
||||
|
||||
@@ -43,7 +43,7 @@ function getNavbarLinks (page = null, device) {
|
||||
'<a class="' + linkClass + '" href="/">Home</a>' +
|
||||
'<a class="' + linkClass + '" href="/create">Create</a>' +
|
||||
'<a class="' + linkClass + '" href="/how-to-use">How to Use</a>' +
|
||||
'<a class="' + linkClass + ' "href="mailto:play.werewolf.contact@gmail.com?Subject=Werewolf App" target="_top">Contact</a>' +
|
||||
'<a class="' + linkClass + ' "href="mailto:play.werewolf.contact@gmail.com?Subject=Werewolf App" target="_top">Feedback</a>' +
|
||||
'<a class="' + linkClass + ' "href="https://github.com/alecm33/Werewolf" target="_top">Github</a>' +
|
||||
'<a class="' + linkClass + '" href="https://www.buymeacoffee.com/alecm33">Support the App</a>';
|
||||
}
|
||||
|
||||
@@ -134,6 +134,7 @@ export class DeckStateManager {
|
||||
document.getElementById('deck-list').appendChild(placeholder);
|
||||
};
|
||||
|
||||
// TODO: refactor
|
||||
updateDeckStatus = () => {
|
||||
document.getElementById('deck-count').innerText = this.getDeckSize() + ' Players';
|
||||
if (this.deck.length > 0) {
|
||||
@@ -186,10 +187,14 @@ export class DeckStateManager {
|
||||
const infoHandler = (e) => {
|
||||
if (e.type === 'click' || e.code === 'Enter') {
|
||||
const alignmentEl = document.getElementById('custom-role-info-modal-alignment');
|
||||
const nameEl = document.getElementById('custom-role-info-modal-name');
|
||||
alignmentEl.classList.remove(globals.ALIGNMENT.GOOD);
|
||||
alignmentEl.classList.remove(globals.ALIGNMENT.EVIL);
|
||||
nameEl.classList.remove(globals.ALIGNMENT.GOOD);
|
||||
nameEl.classList.remove(globals.ALIGNMENT.EVIL);
|
||||
e.preventDefault();
|
||||
document.getElementById('custom-role-info-modal-name').innerText = sortedDeck[i].role;
|
||||
nameEl.innerText = sortedDeck[i].role;
|
||||
nameEl.classList.add(sortedDeck[i].team);
|
||||
alignmentEl.classList.add(sortedDeck[i].team);
|
||||
document.getElementById('custom-role-info-modal-description').innerText = sortedDeck[i].description;
|
||||
alignmentEl.innerText = sortedDeck[i].team;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { globals } from '../../config/globals.js';
|
||||
import { defaultRoles } from '../../config/defaultRoles.js';
|
||||
import { toast } from '../front_end_components/Toast.js';
|
||||
import { ModalManager } from '../front_end_components/ModalManager.js';
|
||||
import { Confirmation } from '../front_end_components/Confirmation.js';
|
||||
|
||||
export class RoleBox {
|
||||
constructor (container, deckManager) {
|
||||
@@ -218,13 +219,13 @@ export class RoleBox {
|
||||
if (remove) {
|
||||
const removeHandler = (e) => {
|
||||
if (e.type === 'click' || e.code === 'Enter') {
|
||||
if (confirm("Delete the role '" + name + "'?")) {
|
||||
Confirmation("Delete the role '" + name + "'?", () => {
|
||||
e.preventDefault();
|
||||
this.removeFromCustomRoles(name);
|
||||
if (this.category === 'custom') {
|
||||
this.displayCustomRoles(document.getElementById('role-select'));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
role.querySelector('.role-remove').addEventListener('click', removeHandler);
|
||||
@@ -234,8 +235,11 @@ export class RoleBox {
|
||||
const infoHandler = (e) => {
|
||||
if (e.type === 'click' || e.code === 'Enter') {
|
||||
const alignmentEl = document.getElementById('custom-role-info-modal-alignment');
|
||||
const nameEl = document.getElementById('custom-role-info-modal-name');
|
||||
alignmentEl.classList.remove(globals.ALIGNMENT.GOOD);
|
||||
alignmentEl.classList.remove(globals.ALIGNMENT.EVIL);
|
||||
nameEl.classList.remove(globals.ALIGNMENT.GOOD);
|
||||
nameEl.classList.remove(globals.ALIGNMENT.EVIL);
|
||||
e.preventDefault();
|
||||
let role;
|
||||
if (isCustom) {
|
||||
@@ -243,7 +247,8 @@ export class RoleBox {
|
||||
} else {
|
||||
role = this.getDefaultRole(name);
|
||||
}
|
||||
document.getElementById('custom-role-info-modal-name').innerText = name;
|
||||
nameEl.innerText = name;
|
||||
nameEl.classList.add(role.team);
|
||||
alignmentEl.classList.add(role.team);
|
||||
document.getElementById('custom-role-info-modal-description').innerText = role.description;
|
||||
alignmentEl.innerText = role.team;
|
||||
|
||||
@@ -6,6 +6,7 @@ import { XHRUtility } from '../utility/XHRUtility.js';
|
||||
import { UserUtility } from '../utility/UserUtility.js';
|
||||
// QRCode module via: https://github.com/soldair/node-qrcode
|
||||
import { QRCode } from '../third_party/qrcode.js';
|
||||
import { Confirmation } from '../front_end_components/Confirmation.js';
|
||||
|
||||
export class GameStateRenderer {
|
||||
constructor (stateBucket, socket) {
|
||||
@@ -16,9 +17,9 @@ export class GameStateRenderer {
|
||||
this.transferModHandlers = {};
|
||||
this.startGameHandler = (e) => { // TODO: prevent multiple emissions of this event (recommend converting to XHR)
|
||||
e.preventDefault();
|
||||
if (confirm('Start the game and deal roles?')) {
|
||||
Confirmation('Start game and deal roles?', () => {
|
||||
socket.emit(globals.SOCKET_EVENTS.IN_GAME_MESSAGE, globals.EVENT_IDS.START_GAME, stateBucket.currentGameState.accessCode);
|
||||
}
|
||||
});
|
||||
};
|
||||
this.restartGameHandler = (e) => {
|
||||
e.preventDefault();
|
||||
@@ -356,9 +357,9 @@ export class GameStateRenderer {
|
||||
}
|
||||
} else if (!player.out && moderatorType) {
|
||||
killPlayerHandlers[player.id] = () => {
|
||||
if (confirm('KILL ' + player.name + '?')) {
|
||||
Confirmation('Kill \'' + player.name + '\'?', () => {
|
||||
socket.emit(globals.SOCKET_EVENTS.IN_GAME_MESSAGE, globals.EVENT_IDS.KILL_PLAYER, accessCode, { personId: player.id });
|
||||
}
|
||||
});
|
||||
};
|
||||
playerEl.querySelector('.kill-player-button').addEventListener('click', killPlayerHandlers[player.id]);
|
||||
}
|
||||
@@ -371,9 +372,9 @@ export class GameStateRenderer {
|
||||
}
|
||||
} else if (!player.revealed && moderatorType) {
|
||||
revealRoleHandlers[player.id] = () => {
|
||||
if (confirm('REVEAL ' + player.name + '?')) {
|
||||
Confirmation('Reveal \'' + player.name + '\'?', () => {
|
||||
socket.emit(globals.SOCKET_EVENTS.IN_GAME_MESSAGE, globals.EVENT_IDS.REVEAL_PLAYER, accessCode, { personId: player.id });
|
||||
}
|
||||
});
|
||||
};
|
||||
playerEl.querySelector('.reveal-role-button').addEventListener('click', revealRoleHandlers[player.id]);
|
||||
}
|
||||
@@ -398,13 +399,19 @@ function renderPotentialMods (gameState, group, transferModHandlers, socket) {
|
||||
container.innerText = member.name;
|
||||
transferModHandlers[member.id] = (e) => {
|
||||
if (e.type === 'click' || e.code === 'Enter') {
|
||||
if (confirm('Transfer moderator powers to ' + member.name + '?')) {
|
||||
ModalManager.dispelModal('transfer-mod-modal', 'transfer-mod-modal-background');
|
||||
Confirmation('Transfer moderator powers to \'' + member.name + '\'?', () => {
|
||||
const transferPrompt = document.getElementById('transfer-mod-prompt');
|
||||
if (transferPrompt !== null) {
|
||||
transferPrompt.innerHTML = '';
|
||||
}
|
||||
socket.emit(globals.SOCKET_EVENTS.IN_GAME_MESSAGE, globals.EVENT_IDS.TRANSFER_MODERATOR, gameState.accessCode, { personId: member.id });
|
||||
}
|
||||
socket.emit(
|
||||
globals.SOCKET_EVENTS.IN_GAME_MESSAGE,
|
||||
globals.EVENT_IDS.TRANSFER_MODERATOR,
|
||||
gameState.accessCode,
|
||||
{ personId: member.id }
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -532,13 +539,13 @@ function createEndGamePromptComponent (socket, stateBucket) {
|
||||
div.innerHTML = HTMLFragments.END_GAME_PROMPT;
|
||||
div.querySelector('#end-game-button').addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
if (confirm('End the game?')) {
|
||||
Confirmation('End the game?', () => {
|
||||
socket.emit(
|
||||
globals.SOCKET_EVENTS.IN_GAME_MESSAGE,
|
||||
globals.EVENT_IDS.END_GAME,
|
||||
stateBucket.currentGameState.accessCode
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
document.getElementById('game-content').appendChild(div);
|
||||
}
|
||||
|
||||
@@ -307,7 +307,7 @@ button {
|
||||
}
|
||||
|
||||
#how-to-use-container h1 {
|
||||
color: #4b6bfa;
|
||||
color: #d7d7d7;
|
||||
font-family: signika-negative, sans-serif;
|
||||
background-color: #1e1b26;
|
||||
width: fit-content;
|
||||
@@ -359,7 +359,7 @@ input {
|
||||
color: #d7d7d7;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 1em auto 0 auto;
|
||||
margin: 1em auto 2em auto;
|
||||
width: 90%;
|
||||
max-width: 64em;
|
||||
line-height: 1.5;
|
||||
@@ -370,10 +370,39 @@ input {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#tutorial-links {text-align: left;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#tutorial-links li {
|
||||
font-size: 25px;
|
||||
color: #768df0;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#tutorial-links li a {
|
||||
color: #768df0;
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
font-family: 'signika-negative', sans-serif;
|
||||
font-size: 18px;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
#tutorial-links li a:hover {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.tutorial-image-small {
|
||||
width: 30em !important;
|
||||
}
|
||||
|
||||
.tutorial-image-small-portrait {
|
||||
width: 20em !important;
|
||||
}
|
||||
|
||||
#desktop-links > a:nth-child(1), #mobile-links a:nth-child(1) {
|
||||
margin: 0 0.5em;
|
||||
width: 50px;
|
||||
|
||||
48
client/src/styles/confirmation.css
Normal file
48
client/src/styles/confirmation.css
Normal file
@@ -0,0 +1,48 @@
|
||||
#confirmation {
|
||||
border-radius: 2px;
|
||||
text-align: center;
|
||||
position: fixed;
|
||||
border: 2px solid #333243;
|
||||
width: 85%;
|
||||
z-index: 100001;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background-color: #191920;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
max-width: 25em;
|
||||
font-family: 'signika-negative', sans-serif;
|
||||
flex-direction: column;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
#confirmation-background {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: calc(100% + 100px);
|
||||
background-color: rgba(0, 0, 0, 0.80);
|
||||
z-index: 100000;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#confirmation-message {
|
||||
font-size: 20px;
|
||||
color: #e7e7e7;
|
||||
margin: 1em 0 2em 0;
|
||||
}
|
||||
|
||||
.confirmation-buttons button {
|
||||
min-width: 5em;
|
||||
}
|
||||
|
||||
.confirmation-buttons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#confirmation-cancel-button {
|
||||
background-color: #762323 !important;
|
||||
}
|
||||
@@ -843,7 +843,7 @@ canvas {
|
||||
}
|
||||
|
||||
#game-role-back h4 {
|
||||
font-size: 24px;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
flex-direction: column;
|
||||
padding: 1em;
|
||||
display: none;
|
||||
border: 2px solid #333243;
|
||||
}
|
||||
|
||||
.modal-background {
|
||||
@@ -55,14 +56,20 @@
|
||||
color: #d7d7d7;
|
||||
text-align: left;
|
||||
font-family: signika-negative, sans-serif;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
#custom-role-info-modal h3 {
|
||||
margin: 0 0 0.5em 0;
|
||||
}
|
||||
|
||||
#custom-role-info-modal-description {
|
||||
margin: 2em 0;
|
||||
border-radius: 3px;
|
||||
background-color: black;
|
||||
max-height: 10em;
|
||||
overflow: auto;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid whitesmoke
|
||||
padding: 5px;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
#custom-role-info-modal-name {
|
||||
@@ -70,9 +77,16 @@
|
||||
font-size: 23px;
|
||||
}
|
||||
|
||||
#custom-role-info-modal label {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
#custom-role-info-modal-alignment {
|
||||
font-size: 20px;
|
||||
border-radius: 3px;
|
||||
background-color: black;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#change-name-modal, #transfer-mod-modal, #role-info-modal {
|
||||
|
||||
@@ -35,7 +35,9 @@ const template =
|
||||
</div>
|
||||
<div tabindex="-1" id="custom-role-info-modal" class="modal">
|
||||
<h3 id="custom-role-info-modal-name"></h3>
|
||||
<label for="custom-role-info-modal-alignment">alignment:</label>
|
||||
<div id="custom-role-info-modal-alignment"></div>
|
||||
<label for="custom-role-info-modal-alignment">description:</label>
|
||||
<div id="custom-role-info-modal-description"></div>
|
||||
<div class="modal-button-container">
|
||||
<button id="close-custom-role-info-modal-button" class="cancel app-button">Close</button>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
<link rel="stylesheet" href="./styles/GLOBAL.css">
|
||||
<link rel="stylesheet" href="./styles/create.css">
|
||||
<link rel="stylesheet" href="./styles/modal.css">
|
||||
<link rel="stylesheet" href="./styles/confirmation.css">
|
||||
<link rel="stylesheet" href="/styles/hamburgers.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<link rel="stylesheet" href="/styles/GLOBAL.css">
|
||||
<link rel="stylesheet" href="/styles/game.css">
|
||||
<link rel="stylesheet" href="/styles/modal.css">
|
||||
<link rel="stylesheet" href="/styles/confirmation.css">
|
||||
<link rel="stylesheet" href="/styles/hamburgers.css">
|
||||
<link rel="preload" href="/webfonts/SignikaNegative-Light.woff2" as="font" type="font/woff2" crossorigin>
|
||||
</head>
|
||||
|
||||
@@ -22,7 +22,23 @@
|
||||
<div id="mobile-menu-background-overlay"></div>
|
||||
<div id="navbar"></div>
|
||||
<div id="how-to-use-container">
|
||||
<h1 class="how-to-use-header">Purpose of the Application</h1>
|
||||
<div id="tutorial-links">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#purpose-of-the-app">Purpose of the App</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#creating-a-game">Creating a Game</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#being-the-moderator">Being the Moderator</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#being-a-player">Being a Player</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<h1 class="how-to-use-header" id="purpose-of-the-app">Purpose of the Application</h1>
|
||||
<div class="how-to-use-section">This app serves as a means of running games in a social setting where a traditional
|
||||
running of the game is hindered. This might be when people are meeting virtually, and thus roles can't be handed
|
||||
out in-person, or when people are in-person but don't have Werewolf cards with them. You can use a deck of regular
|
||||
@@ -32,7 +48,7 @@
|
||||
players. This app attempts to provide the utilities necessary to run Werewolf with all the different roles you want,
|
||||
wherever you can access the internet.
|
||||
</div>
|
||||
<h1 class="how-to-use-header">Creating a Game</h1>
|
||||
<h1 class="how-to-use-header" id="creating-a-game">Creating a Game</h1>
|
||||
<div class="how-to-use-section">
|
||||
Creating a game through the app has 3 main components:
|
||||
<br>
|
||||
@@ -80,7 +96,7 @@
|
||||
end. Whether or not the game ends immediately after that or continues longer is up to the moderator.
|
||||
<br><br>
|
||||
</div>
|
||||
<h1 class="how-to-use-header">Being the Moderator</h1>
|
||||
<h1 class="how-to-use-header" id="being-the-moderator">Being the Moderator</h1>
|
||||
<div class="how-to-use-section">
|
||||
This is an example of what a <span class="emphasized">dedicated moderator</span> sees during the game:
|
||||
<br><br>
|
||||
@@ -105,8 +121,23 @@
|
||||
become a spectator:
|
||||
<br><br>
|
||||
<img class='tutorial-image-small' src="../images/tutorial/transfer-mod.gif"/>
|
||||
</div>
|
||||
<h1 class="how-to-use-header" id="being-a-player">Being a Player</h1>
|
||||
<div class="how-to-use-section">
|
||||
This is an example of what a <span class="emphasized">player</span> is seeing. The timer is running, and they view their
|
||||
role by double-clicking it:
|
||||
<br><br>
|
||||
<img class='tutorial-image-small-portrait' src="../images/tutorial/player-view.gif"/>
|
||||
<br><br>
|
||||
There are three main things - the <span class="emphasized">timer</span>, your <span class="emphasized">role card</span>
|
||||
and the <span class="emphasized">player list</span>. Players can view the timer, but only the current moderator can play and pause it.
|
||||
<span class="emphasized">Your role card starts flipped over</span> - this is useful if you are in-person and don't want someone else accidentally seeing your role as
|
||||
it is dealt. <span class="emphasized">You can view your role at any time by double-clicking/double-tapping it</span>. Requiring a double-click guards against the possibility
|
||||
of accidentally flipping your role when tapping other things. Within the <span class="emphasized">player list</span>, you can see
|
||||
<span class="emphasized">who is alive or dead</span> and <span class="emphasized">who has had their role revealed</span>. There is
|
||||
also a <span class="emphasized">role info button</span> that, when pressed, displays all the different roles in the current game,
|
||||
including their descriptions and alignment (good/evil).
|
||||
<br><br>
|
||||
<p class="teaser">More content coming soon.</p>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/dist/howToUse-bundle.js"></script>
|
||||
|
||||
Reference in New Issue
Block a user