Split chat messages into username and message, and change UI for displaying messages

This commit is contained in:
Micah Lee 2020-08-19 19:40:00 -04:00
parent 8e1b34ce13
commit 9482461a00
No known key found for this signature in database
GPG Key ID: 403C2657CD994F73
6 changed files with 202 additions and 80 deletions

View File

@ -69,12 +69,7 @@ class ChatModeWeb:
)
self.web.add_request(self.web.REQUEST_LOAD, request.path)
r = make_response(
jsonify(
username=session.get("name"),
success=True,
)
)
r = make_response(jsonify(username=session.get("name"), success=True,))
return self.web.add_security_headers(r)
@self.web.socketio.on("joined", namespace="/chat")
@ -86,6 +81,7 @@ class ChatModeWeb:
emit(
"status",
{
"username": session.get("name"),
"msg": "{} has joined.".format(session.get("name")),
"connected_users": self.connected_users,
"user": session.get("name"),
@ -99,7 +95,7 @@ class ChatModeWeb:
The message is sent to all people in the room."""
emit(
"message",
{"msg": "{}: {}".format(session.get("name"), message["msg"])},
{"username": session.get("name"), "msg": message["msg"]},
room=session.get("room"),
)

112
poetry.lock generated
View File

@ -6,6 +6,14 @@ optional = false
python-versions = "*"
version = "0.17"
[[package]]
category = "dev"
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
name = "appdirs"
optional = false
python-versions = "*"
version = "1.4.4"
[[package]]
category = "dev"
description = "Atomic file writes."
@ -28,6 +36,26 @@ dev = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.int
docs = ["sphinx", "zope.interface"]
tests = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"]
[[package]]
category = "dev"
description = "The uncompromising code formatter."
name = "black"
optional = false
python-versions = ">=3.6"
version = "19.10b0"
[package.dependencies]
appdirs = "*"
attrs = ">=18.1.0"
click = ">=6.5"
pathspec = ">=0.6,<1"
regex = "*"
toml = ">=0.9.4"
typed-ast = ">=1.4.0"
[package.extras]
d = ["aiohttp (>=3.3.2)", "aiohttp-cors"]
[[package]]
category = "main"
description = "Python package for providing Mozilla's CA Bundle."
@ -249,6 +277,14 @@ version = "20.4"
pyparsing = ">=2.0.2"
six = "*"
[[package]]
category = "dev"
description = "Utility library for gitignore style pattern matching of file paths."
name = "pathspec"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "0.8.0"
[[package]]
category = "main"
description = "Python PE parsing module"
@ -457,6 +493,14 @@ maintainer = ["zest.releaser"]
pil = ["pillow"]
test = ["pytest", "pytest-cov", "mock"]
[[package]]
category = "dev"
description = "Alternative regular expression module, to replace re."
name = "regex"
optional = false
python-versions = "*"
version = "2020.7.14"
[[package]]
category = "main"
description = "Python HTTP for Humans."
@ -499,6 +543,14 @@ optional = false
python-versions = "*"
version = "0.10.1"
[[package]]
category = "dev"
description = "a fork of Python 2 and 3 ast modules with type comment support"
name = "typed-ast"
optional = false
python-versions = "*"
version = "1.4.1"
[[package]]
category = "main"
description = "HTTP library with thread-safe connection pooling, file post, and more."
@ -538,7 +590,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
testing = ["jaraco.itertools", "func-timeout"]
[metadata]
content-hash = "de18641607a5f3bf11a3051b84eb8a02d4263f435f3554c1aa5860136011cbf3"
content-hash = "04baf07d49586f9567f78935d759b4245d75ed42cba8bdfe0bdb6a3bc9533aee"
lock-version = "1.0"
python-versions = "^3.7"
@ -547,6 +599,10 @@ altgraph = [
{file = "altgraph-0.17-py2.py3-none-any.whl", hash = "sha256:c623e5f3408ca61d4016f23a681b9adb100802ca3e3da5e718915a9e4052cebe"},
{file = "altgraph-0.17.tar.gz", hash = "sha256:1f05a47122542f97028caf78775a095fbe6a2699b5089de8477eb583167d69aa"},
]
appdirs = [
{file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"},
{file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"},
]
atomicwrites = [
{file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
{file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
@ -555,6 +611,10 @@ attrs = [
{file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"},
{file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"},
]
black = [
{file = "black-19.10b0-py36-none-any.whl", hash = "sha256:1b30e59be925fafc1ee4565e5e08abef6b03fe455102883820fe5ee2e4734e0b"},
{file = "black-19.10b0.tar.gz", hash = "sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"},
]
certifi = [
{file = "certifi-2020.6.20-py2.py3-none-any.whl", hash = "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"},
{file = "certifi-2020.6.20.tar.gz", hash = "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3"},
@ -684,6 +744,10 @@ packaging = [
{file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"},
{file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"},
]
pathspec = [
{file = "pathspec-0.8.0-py2.py3-none-any.whl", hash = "sha256:7d91249d21749788d07a2d0f94147accd8f845507400749ea19c1ec9054a12b0"},
{file = "pathspec-0.8.0.tar.gz", hash = "sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061"},
]
pefile = [
{file = "pefile-2019.4.18.tar.gz", hash = "sha256:a5d6e8305c6b210849b47a6174ddf9c452b2888340b8177874b862ba6c207645"},
]
@ -793,6 +857,29 @@ qrcode = [
{file = "qrcode-6.1-py2.py3-none-any.whl", hash = "sha256:3996ee560fc39532910603704c82980ff6d4d5d629f9c3f25f34174ce8606cf5"},
{file = "qrcode-6.1.tar.gz", hash = "sha256:505253854f607f2abf4d16092c61d4e9d511a3b4392e60bff957a68592b04369"},
]
regex = [
{file = "regex-2020.7.14-cp27-cp27m-win32.whl", hash = "sha256:e46d13f38cfcbb79bfdb2964b0fe12561fe633caf964a77a5f8d4e45fe5d2ef7"},
{file = "regex-2020.7.14-cp27-cp27m-win_amd64.whl", hash = "sha256:6961548bba529cac7c07af2fd4d527c5b91bb8fe18995fed6044ac22b3d14644"},
{file = "regex-2020.7.14-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:c50a724d136ec10d920661f1442e4a8b010a4fe5aebd65e0c2241ea41dbe93dc"},
{file = "regex-2020.7.14-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8a51f2c6d1f884e98846a0a9021ff6861bdb98457879f412fdc2b42d14494067"},
{file = "regex-2020.7.14-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:9c568495e35599625f7b999774e29e8d6b01a6fb684d77dee1f56d41b11b40cd"},
{file = "regex-2020.7.14-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:51178c738d559a2d1071ce0b0f56e57eb315bcf8f7d4cf127674b533e3101f88"},
{file = "regex-2020.7.14-cp36-cp36m-win32.whl", hash = "sha256:9eddaafb3c48e0900690c1727fba226c4804b8e6127ea409689c3bb492d06de4"},
{file = "regex-2020.7.14-cp36-cp36m-win_amd64.whl", hash = "sha256:14a53646369157baa0499513f96091eb70382eb50b2c82393d17d7ec81b7b85f"},
{file = "regex-2020.7.14-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:1269fef3167bb52631ad4fa7dd27bf635d5a0790b8e6222065d42e91bede4162"},
{file = "regex-2020.7.14-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d0a5095d52b90ff38592bbdc2644f17c6d495762edf47d876049cfd2968fbccf"},
{file = "regex-2020.7.14-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:4c037fd14c5f4e308b8370b447b469ca10e69427966527edcab07f52d88388f7"},
{file = "regex-2020.7.14-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bc3d98f621898b4a9bc7fecc00513eec8f40b5b83913d74ccb445f037d58cd89"},
{file = "regex-2020.7.14-cp37-cp37m-win32.whl", hash = "sha256:46bac5ca10fb748d6c55843a931855e2727a7a22584f302dd9bb1506e69f83f6"},
{file = "regex-2020.7.14-cp37-cp37m-win_amd64.whl", hash = "sha256:0dc64ee3f33cd7899f79a8d788abfbec168410be356ed9bd30bbd3f0a23a7204"},
{file = "regex-2020.7.14-cp38-cp38-manylinux1_i686.whl", hash = "sha256:5ea81ea3dbd6767873c611687141ec7b06ed8bab43f68fad5b7be184a920dc99"},
{file = "regex-2020.7.14-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:bbb332d45b32df41200380fff14712cb6093b61bd142272a10b16778c418e98e"},
{file = "regex-2020.7.14-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:c11d6033115dc4887c456565303f540c44197f4fc1a2bfb192224a301534888e"},
{file = "regex-2020.7.14-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:75aaa27aa521a182824d89e5ab0a1d16ca207318a6b65042b046053cfc8ed07a"},
{file = "regex-2020.7.14-cp38-cp38-win32.whl", hash = "sha256:d6cff2276e502b86a25fd10c2a96973fdb45c7a977dca2138d661417f3728341"},
{file = "regex-2020.7.14-cp38-cp38-win_amd64.whl", hash = "sha256:7a2dd66d2d4df34fa82c9dc85657c5e019b87932019947faece7983f2089a840"},
{file = "regex-2020.7.14.tar.gz", hash = "sha256:3a3af27a8d23143c49a3420efe5b3f8cf1a48c6fc8bc6856b03f638abc1833bb"},
]
requests = [
{file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"},
{file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"},
@ -808,6 +895,29 @@ toml = [
{file = "toml-0.10.1-py2.py3-none-any.whl", hash = "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"},
{file = "toml-0.10.1.tar.gz", hash = "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f"},
]
typed-ast = [
{file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"},
{file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb"},
{file = "typed_ast-1.4.1-cp35-cp35m-win32.whl", hash = "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919"},
{file = "typed_ast-1.4.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01"},
{file = "typed_ast-1.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75"},
{file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652"},
{file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"},
{file = "typed_ast-1.4.1-cp36-cp36m-win32.whl", hash = "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1"},
{file = "typed_ast-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa"},
{file = "typed_ast-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614"},
{file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41"},
{file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b"},
{file = "typed_ast-1.4.1-cp37-cp37m-win32.whl", hash = "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe"},
{file = "typed_ast-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355"},
{file = "typed_ast-1.4.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6"},
{file = "typed_ast-1.4.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907"},
{file = "typed_ast-1.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d"},
{file = "typed_ast-1.4.1-cp38-cp38-win32.whl", hash = "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c"},
{file = "typed_ast-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4"},
{file = "typed_ast-1.4.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"},
{file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"},
]
urllib3 = [
{file = "urllib3-1.25.10-py2.py3-none-any.whl", hash = "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461"},
{file = "urllib3-1.25.10.tar.gz", hash = "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a"},

View File

@ -45,6 +45,7 @@ six = "*"
urllib3 = "*"
setuptools = "*"
pyinstaller = {version = "*", platform = "darwin"}
black = {version = "^19.10b0", allow-prereleases = true}
[build-system]
requires = ["poetry>=0.12"]

View File

@ -206,6 +206,16 @@ ul.breadcrumbs li a:link, ul.breadcrumbs li a:visited {
padding: 0 1rem;
}
.chat-wrapper .username {
font-weight: bold;
display: block;
}
.chat-wrapper .message {
font-weight: normal;
display: block;
margin-bottom: 0.3em;
}
.chat-wrapper .chat-form {
display: flex;
}

View File

@ -1,5 +1,5 @@
$(function(){
$(document).ready(function(){
$(function () {
$(document).ready(function () {
$('.chat-container').removeClass('no-js');
var socket = io.connect('http://' + document.domain + ':' + location.port + '/chat');
@ -8,43 +8,45 @@ $(function(){
// On browser connect, emit a socket event to be added to
// room and assigned random username
socket.on('connect', function() {
socket.on('connect', function () {
socket.emit('joined', {});
});
// Triggered on any status change by any user, such as some
// user joined, or changed username, or left, etc.
socket.on('status', function(data) {
socket.on('status', function (data) {
addMessageToRoom(data, current_username, 'status');
console.log(data, current_username);
});
// Triggered when message is received from a user. Even when sent
// by self, it get triggered after the server sends back the emit.
socket.on('message', function(data) {
socket.on('message', function (data) {
addMessageToRoom(data, current_username, 'chat');
console.log(data, current_username);
});
// Triggered when disconnected either by server stop or timeout
socket.on('disconnect', function(data) {
addMessageToRoom({'msg': 'The chat server is disconnected.'}, current_username, 'status');
socket.on('disconnect', function (data) {
addMessageToRoom({ 'msg': 'The chat server is disconnected.' }, current_username, 'status');
})
socket.on('connect_error', function(error) {
socket.on('connect_error', function (error) {
console.log("error");
})
// Trigger new message on enter or click of send message button.
$('#new-message').on('keypress', function(e) {
$('#new-message').on('keypress', function (e) {
var code = e.keyCode || e.which;
if (code == 13) {
emitMessage(socket);
}
});
$('#send-button').on('click', function(e) {
$('#send-button').on('click', function (e) {
emitMessage(socket);
});
// Keep buttons disabled unless changed or not empty
$('#username').on('keyup',function(event) {
$('#username').on('keyup', function (event) {
if ($('#username').val() !== '' && $('#username').val() !== current_username) {
$('#update-username').removeAttr('disabled');
if (event.keyCode == 13) {
@ -56,7 +58,7 @@ $(function(){
});
// Update username
$('#update-username').on('click', function() {
$('#update-username').on('click', function () {
current_username = updateUsername(socket);
});
@ -69,7 +71,7 @@ $(function(){
});
});
var addMessageToRoom = function(data, current_username, messageType) {
var addMessageToRoom = function (data, current_username, messageType) {
var scrollDiff = getScrollDiffBefore();
if (messageType === 'status') {
addStatusMessage(data.msg);
@ -77,28 +79,28 @@ var addMessageToRoom = function(data, current_username, messageType) {
addUserList(data.connected_users, current_username);
}
} else if (messageType === 'chat') {
addChatMessage(data.msg)
addChatMessage(data.username, data.msg)
}
scrollBottomMaybe(scrollDiff);
}
var emitMessage = function(socket) {
var emitMessage = function (socket) {
var text = $('#new-message').val();
$('#new-message').val('');
$('#chat').scrollTop($('#chat')[0].scrollHeight);
socket.emit('text', {msg: text});
socket.emit('text', { msg: text });
}
var updateUsername = function(socket) {
var updateUsername = function (socket) {
var username = $('#username').val();
socket.emit('update_username', {username: username});
socket.emit('update_username', { username: username });
$.ajax({
method: 'POST',
url: `http://${document.domain}:${location.port}/update-session-username`,
contentType: 'application/json',
dataType: 'json',
data: JSON.stringify({'username': username})
}).done(function(response) {
data: JSON.stringify({ 'username': username })
}).done(function (response) {
console.log(response);
});
$('#update-username').attr('disabled', true);
@ -109,10 +111,10 @@ var updateUsername = function(socket) {
/********* Util Functions ***********/
/************************************/
var createUserListHTML = function(connected_users, current_user) {
var createUserListHTML = function (connected_users, current_user) {
var userListHTML = '';
connected_users.sort();
connected_users.forEach(function(username) {
connected_users.forEach(function (username) {
if (username !== current_user) {
userListHTML += `<li>${sanitizeHTML(username)}</li>`;
}
@ -120,11 +122,11 @@ var createUserListHTML = function(connected_users, current_user) {
return userListHTML;
}
var getScrollDiffBefore = function() {
var getScrollDiffBefore = function () {
return $('#chat').scrollTop() - ($('#chat')[0].scrollHeight - $('#chat')[0].offsetHeight);
}
var scrollBottomMaybe = function(scrollDiff) {
var scrollBottomMaybe = function (scrollDiff) {
// Scrolls to bottom if the user is scrolled at bottom
// if the user has scrolled upp, it wont scroll at bottom.
// Note: when a user themselves send a message, it will still
@ -134,17 +136,17 @@ var scrollBottomMaybe = function(scrollDiff) {
}
}
var addStatusMessage = function(message) {
var addStatusMessage = function (message) {
$('#chat').append(
`<p><small><i>${sanitizeHTML(message)}</i></small></p>`
);
}
var addChatMessage = function(message) {
$('#chat').append(`<p>${sanitizeHTML(message)}</p>`);
var addChatMessage = function (username, message) {
$('#chat').append(`<p><span class="username">${sanitizeHTML(username)}</span><span class="message">${sanitizeHTML(message)}</span></p>`);
}
var addUserList = function(connected_users, current_username) {
var addUserList = function (connected_users, current_username) {
$('#user-list').html(
createUserListHTML(
connected_users,
@ -153,8 +155,8 @@ var addUserList = function(connected_users, current_username) {
);
}
var sanitizeHTML = function(str) {
var temp = document.createElement('span');
temp.textContent = str;
return temp.innerHTML;
var sanitizeHTML = function (str) {
var temp = document.createElement('span');
temp.textContent = str;
return temp.innerHTML;
};

View File

@ -1,46 +1,49 @@
<!DOCTYPE html>
<html>
<head>
<title>OnionShare</title>
<link href="{{ static_url_path }}/img/favicon.ico" rel="icon" type="image/x-icon">
<link rel="stylesheet" rel="subresource" type="text/css" href="{{ static_url_path }}/css/style.css" media="all">
</head>
<body>
<header class="clearfix">
<img class="logo" src="{{ static_url_path }}/img/logo.png" title="OnionShare">
<h1>OnionShare</h1>
</header>
<noscript>
<p>
Chat <b>requires JavaScript</b>, so you must set your Tor Browser security
level to <b>Safer</b> or <b>Standard</b> to join.
</p>
</noscript>
<div class="chat-container no-js">
<div class="chat-users">
<div class="editable-username">
<input id="username" value="{{ username }}" />
<button id="update-username" disabled>Save</button>
</div>
<ul id="user-list">
</ul>
</div>
<div class="chat-wrapper">
<p class="chat-header">Chat Messages</p>
<div id="chat"></div>
<div class="chat-form">
<p><input type="text" id="new-message" name="new-message" placeholder="Type your message"/></p>
<p><button type="button" id="send-button" class="button">Send Message</button></p>
</div>
<head>
<title>OnionShare</title>
<link href="{{ static_url_path }}/img/favicon.ico" rel="icon" type="image/x-icon">
<link rel="stylesheet" rel="subresource" type="text/css" href="{{ static_url_path }}/css/style.css" media="all">
</head>
<body>
<header class="clearfix">
<img class="logo" src="{{ static_url_path }}/img/logo.png" title="OnionShare">
<h1>OnionShare</h1>
</header>
<noscript>
<p>
Chat <b>requires JavaScript</b>, so you must set your Tor Browser security
level to <b>Safer</b> or <b>Standard</b> to join.
</p>
</noscript>
<div class="chat-container no-js">
<div class="chat-users">
<div class="editable-username">
<input id="username" value="{{ username }}" />
<button id="update-username" disabled>Set Name</button>
</div>
<ul id="user-list">
</ul>
</div>
<script src="{{ static_url_path }}/js/jquery-3.5.1.min.js"></script>
<script src="{{ static_url_path }}/js/socket.io.min.js"></script>
<script async src="{{ static_url_path }}/js/chat.js"></script>
</body>
</html>
<div class="chat-wrapper">
<p class="chat-header">Chat Messages</p>
<div id="chat"></div>
<div class="chat-form">
<p><input type="text" id="new-message" name="new-message" placeholder="Type your message" /></p>
<p><button type="button" id="send-button" class="button">Send Message</button></p>
</div>
</div>
</div>
<script src="{{ static_url_path }}/js/jquery-3.5.1.min.js"></script>
<script src="{{ static_url_path }}/js/socket.io.min.js"></script>
<script async src="{{ static_url_path }}/js/chat.js"></script>
</body>
</html>