Compare commits
2 Commits
8cc6f6b50f
...
541fe10f1a
Author | SHA1 | Date | |
---|---|---|---|
541fe10f1a | |||
b3ae7ba703 |
10
build.rs
10
build.rs
@ -46,9 +46,11 @@ fn generate_web_files() {
|
|||||||
"commons/script.js",
|
"commons/script.js",
|
||||||
].iter().for_each(|file_name| {
|
].iter().for_each(|file_name| {
|
||||||
let path = Path::new(file_name);
|
let path = Path::new(file_name);
|
||||||
|
let src_path = src_dir.join(path);
|
||||||
|
println!("cargo:rerun-if-changed={}", src_path.to_str().unwrap());
|
||||||
let extension = path.extension().unwrap().to_str().unwrap();
|
let extension = path.extension().unwrap().to_str().unwrap();
|
||||||
let mut content = read_to_string(src_dir.join(path)).unwrap();
|
let mut content = read_to_string(src_path).unwrap();
|
||||||
if extension == "css" {
|
if extension == "css" || file_name == &"login.html" {
|
||||||
replace_fields(&mut content, fields);
|
replace_fields(&mut content, fields);
|
||||||
}
|
}
|
||||||
if file_name == &"index.html" {
|
if file_name == &"index.html" {
|
||||||
@ -59,7 +61,9 @@ fn generate_web_files() {
|
|||||||
dst.write(minified_content.as_bytes()).unwrap();
|
dst.write(minified_content.as_bytes()).unwrap();
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut text_avatar = read_to_string("src/frontend/imgs/text_avatar.svg").unwrap();
|
const TEXT_AVATAR_PATH: &str = "src/frontend/imgs/text_avatar.svg";
|
||||||
|
let mut text_avatar = read_to_string(TEXT_AVATAR_PATH).unwrap();
|
||||||
|
println!("cargo:rerun-if-changed={}", TEXT_AVATAR_PATH);
|
||||||
replace_fields(&mut text_avatar, fields);
|
replace_fields(&mut text_avatar, fields);
|
||||||
File::create(out_dir.join("text_avatar.svg")).unwrap().write(text_avatar.as_bytes()).unwrap();
|
File::create(out_dir.join("text_avatar.svg")).unwrap().write(text_avatar.as_bytes()).unwrap();
|
||||||
}
|
}
|
||||||
|
@ -14,4 +14,54 @@ function generateAvatar(sessionId, name, timestamp) {
|
|||||||
let img = generateImgAvatar();
|
let img = generateImgAvatar();
|
||||||
img.src = "/avatar/"+sessionId+"/"+name+"?"+timestamp;
|
img.src = "/avatar/"+sessionId+"/"+name+"?"+timestamp;
|
||||||
return img;
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
|
function removePopup() {
|
||||||
|
let popups = document.querySelectorAll(".popup_background");
|
||||||
|
if (popups.length > 0) {
|
||||||
|
popups[popups.length-1].remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function showPopup(content, cancelable = true) {
|
||||||
|
let popup_background = document.createElement("div");
|
||||||
|
popup_background.classList.add("popup_background");
|
||||||
|
let popup = document.createElement("div");
|
||||||
|
popup.classList.add("popup");
|
||||||
|
if (cancelable) {
|
||||||
|
popup_background.onclick = function(e) {
|
||||||
|
if (e.target == popup_background) {
|
||||||
|
removePopup();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let close = document.createElement("button");
|
||||||
|
close.classList.add("close");
|
||||||
|
close.onclick = removePopup;
|
||||||
|
popup.appendChild(close);
|
||||||
|
}
|
||||||
|
popup.appendChild(content);
|
||||||
|
popup_background.appendChild(popup);
|
||||||
|
let main = document.querySelector("main");
|
||||||
|
main.appendChild(popup_background);
|
||||||
|
}
|
||||||
|
|
||||||
|
function uploadAvatar(event, onUploaded) {
|
||||||
|
let file = event.target.files[0];
|
||||||
|
if (file.size < 10000000) {
|
||||||
|
let formData = new FormData();
|
||||||
|
formData.append("avatar", file);
|
||||||
|
fetch("/set_avatar", {method: "POST", body: formData}).then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
onUploaded();
|
||||||
|
} else {
|
||||||
|
console.log(response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let mainDiv = document.createElement("div");
|
||||||
|
mainDiv.appendChild(generatePopupWarningTitle());
|
||||||
|
let p = document.createElement("p");
|
||||||
|
p.textContent = "Avatar cannot be larger than 10MB.";
|
||||||
|
mainDiv.appendChild(p);
|
||||||
|
showPopup(mainDiv);
|
||||||
|
}
|
||||||
}
|
}
|
@ -24,10 +24,97 @@ input[type="text"], input[type="password"] {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
input[type="file"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
margin-right: .5em;
|
margin-right: .5em;
|
||||||
width: 1.5em;
|
width: 1.5em;
|
||||||
height: 1.5em;
|
height: 1.5em;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
main.card {
|
||||||
|
max-width: 500px;
|
||||||
|
background-color: #2B2F31;
|
||||||
|
border-radius: 10px;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: auto;
|
||||||
|
width: 40vw;
|
||||||
|
max-height: 90vh;
|
||||||
|
overflow: auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 20px 70px 10px;
|
||||||
|
background-color: #2B2F31;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
.popup:last-child::after {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
height: 20px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.popup_background {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
background-color: rgba(0, 0, 0, .5);
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
.popup .close {
|
||||||
|
background-color: unset;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 6px;
|
||||||
|
}
|
||||||
|
.popup .close::after {
|
||||||
|
content: url("/static/imgs/icons/cancel");
|
||||||
|
background-color: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
#avatarContainer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding-bottom: 1.5em;
|
||||||
|
}
|
||||||
|
#avatarContainer .avatar {
|
||||||
|
margin-right: unset;
|
||||||
|
}
|
||||||
|
#avatarContainer label:hover .avatar {
|
||||||
|
opacity: .4;
|
||||||
|
}
|
||||||
|
#avatarContainer label {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
#avatarContainer .avatar + p {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
#avatarContainer label:hover p {
|
||||||
|
display: block;
|
||||||
}
|
}
|
1
src/frontend/imgs/icons/profile.svg
Normal file
1
src/frontend/imgs/icons/profile.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FILL_COLOR"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></svg>
|
After Width: | Height: | Size: 243 B |
@ -48,9 +48,6 @@ button:hover::after {
|
|||||||
.classic_button:hover {
|
.classic_button:hover {
|
||||||
background-color: var(--accent);
|
background-color: var(--accent);
|
||||||
}
|
}
|
||||||
input[type="file"] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.file_picker {
|
.file_picker {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -60,57 +57,12 @@ input[type="file"] {
|
|||||||
content: url("/static/imgs/icons/attach/ACCENT_COLOR");
|
content: url("/static/imgs/icons/attach/ACCENT_COLOR");
|
||||||
width: 2em;
|
width: 2em;
|
||||||
}
|
}
|
||||||
.popup {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
margin: auto;
|
|
||||||
width: 40vw;
|
|
||||||
max-height: 90vh;
|
|
||||||
overflow: auto;
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 20px 70px 10px;
|
|
||||||
background-color: #2B2F31;
|
|
||||||
border-radius: 10px;
|
|
||||||
font-size: 1.2em;
|
|
||||||
}
|
|
||||||
.popup:last-child::after {
|
|
||||||
content: "";
|
|
||||||
display: block;
|
|
||||||
height: 20px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.popup_background {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
position: absolute;
|
|
||||||
background-color: rgba(0, 0, 0, .5);
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
.popup .close {
|
|
||||||
background-color: unset;
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: 6px;
|
|
||||||
}
|
|
||||||
.popup .close:hover {
|
|
||||||
background-color: unset;
|
|
||||||
}
|
|
||||||
.popup .close::after {
|
|
||||||
content: url("/static/imgs/icons/cancel");
|
|
||||||
background-color: unset;
|
|
||||||
}
|
|
||||||
.popup h2.warning::before {
|
.popup h2.warning::before {
|
||||||
content: url("/static/imgs/icons/warning/ACCENT_COLOR");
|
content: url("/static/imgs/icons/warning/ACCENT_COLOR");
|
||||||
width: 9%;
|
width: 9%;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
label {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.switch_preference {
|
.switch_preference {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -165,31 +117,18 @@ label {
|
|||||||
transform: translateX(26px);
|
transform: translateX(26px);
|
||||||
}
|
}
|
||||||
#avatarContainer {
|
#avatarContainer {
|
||||||
display: flex;
|
position: relative;
|
||||||
justify-content: center;
|
|
||||||
}
|
}
|
||||||
#avatarContainer .avatar {
|
#avatarContainer .avatar {
|
||||||
font-size: 4em;
|
font-size: 4em;
|
||||||
margin-right: unset;
|
|
||||||
}
|
}
|
||||||
#avatarContainer label:hover .avatar {
|
#removeAvatar {
|
||||||
opacity: .4;
|
|
||||||
}
|
|
||||||
#avatarContainer>label {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
#avatarContainer .avatar + p {
|
|
||||||
display: none;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
bottom: 0;
|
||||||
transform: translateY(-50%);
|
cursor: pointer;
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
margin: auto;
|
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
#avatarContainer label:hover p {
|
#removeAvatar:hover {
|
||||||
display: block;
|
color: var(--accent);
|
||||||
}
|
}
|
||||||
#profile_info section {
|
#profile_info section {
|
||||||
display: block;
|
display: block;
|
||||||
@ -199,12 +138,12 @@ label {
|
|||||||
#profile_info section:first-of-type {
|
#profile_info section:first-of-type {
|
||||||
border-top: unset;
|
border-top: unset;
|
||||||
}
|
}
|
||||||
|
#profile_info section:first-of-type h3 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
#profile_info input {
|
#profile_info input {
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
}
|
}
|
||||||
#profile_info span {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
#profile_info>div>div p {
|
#profile_info>div>div p {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
@ -275,10 +214,6 @@ label {
|
|||||||
#me>div:hover p {
|
#me>div:hover p {
|
||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
}
|
}
|
||||||
#identity_fingerprint {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
#left_panel ul:last-of-type, #msg_log {
|
#left_panel ul:last-of-type, #msg_log {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
@ -244,32 +244,13 @@ profile_div.onclick = function() {
|
|||||||
labelAvatar.setAttribute("for", "avatar_input");
|
labelAvatar.setAttribute("for", "avatar_input");
|
||||||
let inputAvatar = document.createElement("input");
|
let inputAvatar = document.createElement("input");
|
||||||
inputAvatar.type = "file";
|
inputAvatar.type = "file";
|
||||||
|
inputAvatar.accept = "image/*";
|
||||||
inputAvatar.id = "avatar_input";
|
inputAvatar.id = "avatar_input";
|
||||||
inputAvatar.onchange = function(event) {
|
inputAvatar.onchange = function(event) {
|
||||||
let file = event.target.files[0];
|
uploadAvatar(event, function() {
|
||||||
if (file.size < 10000000) {
|
avatarTimestamps.set("self", Date.now());
|
||||||
let formData = new FormData();
|
refreshSelfAvatar();
|
||||||
formData.append("avatar", file);
|
});
|
||||||
fetch("/set_avatar", {method: "POST", body: formData}).then(response => {
|
|
||||||
if (response.ok) {
|
|
||||||
avatarTimestamps.set("self", Date.now());
|
|
||||||
document.querySelector("#avatarContainer .avatar").src = "/avatar/self?"+avatarTimestamps.get("self");
|
|
||||||
displayProfile();
|
|
||||||
if (currentSessionId != -1) {
|
|
||||||
displayHistory();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log(response);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
let mainDiv = document.createElement("div");
|
|
||||||
mainDiv.appendChild(generatePopupWarningTitle());
|
|
||||||
let p = document.createElement("p");
|
|
||||||
p.textContent = "Avatar cannot be larger than 10MB.";
|
|
||||||
mainDiv.appendChild(p);
|
|
||||||
showPopup(mainDiv);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
labelAvatar.appendChild(inputAvatar);
|
labelAvatar.appendChild(inputAvatar);
|
||||||
labelAvatar.appendChild(generateSelfAvatar(avatarTimestamps.get("self")));
|
labelAvatar.appendChild(generateSelfAvatar(avatarTimestamps.get("self")));
|
||||||
@ -277,11 +258,14 @@ profile_div.onclick = function() {
|
|||||||
uploadP.textContent = "Upload";
|
uploadP.textContent = "Upload";
|
||||||
labelAvatar.appendChild(uploadP);
|
labelAvatar.appendChild(uploadP);
|
||||||
avatarContainer.appendChild(labelAvatar);
|
avatarContainer.appendChild(labelAvatar);
|
||||||
|
let removeAvatar = document.createElement("span");
|
||||||
|
removeAvatar.id = "removeAvatar";
|
||||||
|
removeAvatar.textContent = "Remove";
|
||||||
|
removeAvatar.onclick = function() {
|
||||||
|
socket.send("remove_avatar");
|
||||||
|
};
|
||||||
|
avatarContainer.appendChild(removeAvatar);
|
||||||
mainDiv.appendChild(avatarContainer);
|
mainDiv.appendChild(avatarContainer);
|
||||||
let fingerprint = document.createElement("pre");
|
|
||||||
fingerprint.id = "identity_fingerprint";
|
|
||||||
fingerprint.textContent = beautifyFingerprint(identityFingerprint);
|
|
||||||
mainDiv.appendChild(fingerprint);
|
|
||||||
let sectionName = document.createElement("section");
|
let sectionName = document.createElement("section");
|
||||||
let titleName = document.createElement("h3");
|
let titleName = document.createElement("h3");
|
||||||
titleName.textContent = "Name:";
|
titleName.textContent = "Name:";
|
||||||
@ -299,6 +283,14 @@ profile_div.onclick = function() {
|
|||||||
};
|
};
|
||||||
sectionName.appendChild(saveNameButton);
|
sectionName.appendChild(saveNameButton);
|
||||||
mainDiv.appendChild(sectionName);
|
mainDiv.appendChild(sectionName);
|
||||||
|
let sectionFingerprint = document.createElement("section");
|
||||||
|
let titleFingerprint = document.createElement("h3");
|
||||||
|
titleFingerprint.textContent = "Your fingerprint:";
|
||||||
|
sectionFingerprint.appendChild(titleFingerprint);
|
||||||
|
let fingerprint = document.createElement("pre");
|
||||||
|
fingerprint.textContent = beautifyFingerprint(identityFingerprint);
|
||||||
|
sectionFingerprint.appendChild(fingerprint);
|
||||||
|
mainDiv.appendChild(sectionFingerprint);
|
||||||
let sectionPadding = document.createElement("section");
|
let sectionPadding = document.createElement("section");
|
||||||
sectionPadding.appendChild(generateSwitchPreference("Use PSEC padding", "PSEC padding obfuscates the length of your messages but uses more network bandwidth.", usePadding, function(checked) {
|
sectionPadding.appendChild(generateSwitchPreference("Use PSEC padding", "PSEC padding obfuscates the length of your messages but uses more network bandwidth.", usePadding, function(checked) {
|
||||||
socket.send("set_use_padding "+checked);
|
socket.send("set_use_padding "+checked);
|
||||||
@ -499,8 +491,8 @@ socket.onmessage = function(msg) {
|
|||||||
case "name_told":
|
case "name_told":
|
||||||
onNameTold(args[1], msg.data.slice(args[0].length+args[1].length+2));
|
onNameTold(args[1], msg.data.slice(args[0].length+args[1].length+2));
|
||||||
break;
|
break;
|
||||||
case "avatar_set":
|
case "avatar_changed":
|
||||||
onAvatarSet(args[1]);
|
onAvatarChanged(args[1]);
|
||||||
break;
|
break;
|
||||||
case "is_contact":
|
case "is_contact":
|
||||||
onIsContact(args[1], args[2] === "true", args[3], msg.data.slice(args[0].length+args[1].length+args[2].length+args[3].length+4));
|
onIsContact(args[1], args[2] === "true", args[3], msg.data.slice(args[0].length+args[1].length+args[2].length+args[3].length+4));
|
||||||
@ -554,12 +546,15 @@ function onNameTold(sessionId, name) {
|
|||||||
}
|
}
|
||||||
displaySessions();
|
displaySessions();
|
||||||
}
|
}
|
||||||
function onAvatarSet(sessionId) {
|
function onAvatarChanged(sessionIdOrSelf) {
|
||||||
avatarTimestamps.set(sessionId, Date.now());
|
avatarTimestamps.set(sessionIdOrSelf, Date.now());
|
||||||
displaySessions();
|
displaySessions();
|
||||||
if (sessionId === currentSessionId) {
|
if (sessionIdOrSelf === currentSessionId) {
|
||||||
displayHeader();
|
displayHeader();
|
||||||
displayHistory(false);
|
displayHistory(false);
|
||||||
|
refreshAvatar("#session_info .avatar", sessionIdOrSelf);
|
||||||
|
} else if (sessionIdOrSelf === "self") {
|
||||||
|
refreshSelfAvatar();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function setNotSeen(strSessionIds) {
|
function setNotSeen(strSessionIds) {
|
||||||
@ -818,6 +813,23 @@ function sendNextLargeFile(sessionId) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
function refreshAvatar(selector, sessionId) {
|
||||||
|
let avatar = document.querySelector(selector);
|
||||||
|
if (typeof avatar !== "undefined") {
|
||||||
|
if (typeof sessionId === "undefined") {
|
||||||
|
avatar.src = "/avatar/self?"+avatarTimestamps.get("self");
|
||||||
|
} else {
|
||||||
|
avatar.src = "/avatar/"+sessionId+"/"+sessionsData.get(sessionId).name+"?"+avatarTimestamps.get(sessionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function refreshSelfAvatar() {
|
||||||
|
refreshAvatar("#avatarContainer .avatar");
|
||||||
|
displayProfile();
|
||||||
|
if (currentSessionId != -1) {
|
||||||
|
displayHistory(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
function beautifyFingerprint(f) {
|
function beautifyFingerprint(f) {
|
||||||
for (let i=4; i<f.length; i+=5) {
|
for (let i=4; i<f.length; i+=5) {
|
||||||
f = f.slice(0, i)+" "+f.slice(i);
|
f = f.slice(0, i)+" "+f.slice(i);
|
||||||
@ -937,33 +949,6 @@ function displayHeader() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function showPopup(content, cancelable = true) {
|
|
||||||
let popup_background = document.createElement("div");
|
|
||||||
popup_background.classList.add("popup_background");
|
|
||||||
let popup = document.createElement("div");
|
|
||||||
popup.classList.add("popup");
|
|
||||||
if (cancelable) {
|
|
||||||
popup_background.onclick = function(e) {
|
|
||||||
if (e.target == popup_background) {
|
|
||||||
removePopup();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let close = document.createElement("button");
|
|
||||||
close.classList.add("close");
|
|
||||||
close.onclick = removePopup;
|
|
||||||
popup.appendChild(close);
|
|
||||||
}
|
|
||||||
popup.appendChild(content);
|
|
||||||
popup_background.appendChild(popup);
|
|
||||||
let main = document.querySelector("main");
|
|
||||||
main.appendChild(popup_background);
|
|
||||||
}
|
|
||||||
function removePopup() {
|
|
||||||
let popups = document.querySelectorAll(".popup_background");
|
|
||||||
if (popups.length > 0) {
|
|
||||||
popups[popups.length-1].remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function generatePopupWarningTitle() {
|
function generatePopupWarningTitle() {
|
||||||
let h2 = document.createElement("h2");
|
let h2 = document.createElement("h2");
|
||||||
h2.classList.add("warning");
|
h2.classList.add("warning");
|
||||||
@ -1032,12 +1017,11 @@ function generateMsgHeader(name, sessionId) {
|
|||||||
p.appendChild(document.createTextNode(name));
|
p.appendChild(document.createTextNode(name));
|
||||||
let div = document.createElement("div");
|
let div = document.createElement("div");
|
||||||
div.classList.add("header");
|
div.classList.add("header");
|
||||||
let timestamp = avatarTimestamps.get(sessionId);
|
|
||||||
let avatar;
|
let avatar;
|
||||||
if (typeof sessionId === "undefined") {
|
if (typeof sessionId === "undefined") {
|
||||||
avatar = generateSelfAvatar(timestamp);
|
avatar = generateSelfAvatar(avatarTimestamps.get("self"));
|
||||||
} else {
|
} else {
|
||||||
avatar = generateAvatar(sessionId, name, timestamp);
|
avatar = generateAvatar(sessionId, name, avatarTimestamps.get(sessionId));
|
||||||
}
|
}
|
||||||
div.appendChild(avatar);
|
div.appendChild(avatar);
|
||||||
div.appendChild(p);
|
div.appendChild(p);
|
||||||
|
@ -14,14 +14,6 @@
|
|||||||
background-color: black;
|
background-color: black;
|
||||||
}
|
}
|
||||||
main {
|
main {
|
||||||
max-width: 500px;
|
|
||||||
background-color: #2B2F31;
|
|
||||||
border-radius: 10px;
|
|
||||||
position: absolute;
|
|
||||||
top: 20vh;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
margin: auto;
|
|
||||||
padding-bottom: 20px;
|
padding-bottom: 20px;
|
||||||
}
|
}
|
||||||
h1, main>h3, #error_msg {
|
h1, main>h3, #error_msg {
|
||||||
@ -40,6 +32,9 @@
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
.action_page>h2 {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
input[type="text"], input[type="password"] {
|
input[type="text"], input[type="password"] {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
@ -65,13 +60,11 @@
|
|||||||
.avatar {
|
.avatar {
|
||||||
width: 7em;
|
width: 7em;
|
||||||
height: 7em;
|
height: 7em;
|
||||||
display: block;
|
|
||||||
margin: auto;
|
|
||||||
}
|
}
|
||||||
#identity h2 {
|
#identity h2 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
p {
|
#error_msg {
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
label.checkbox {
|
label.checkbox {
|
||||||
@ -110,24 +103,41 @@
|
|||||||
label.checkbox input:checked ~ .checkmark:after {
|
label.checkbox input:checked ~ .checkmark:after {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
#avatarContainer .avatar.unset {
|
||||||
|
border: 2px solid var(--accent);
|
||||||
|
}
|
||||||
|
#identity .avatar {
|
||||||
|
display: block;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main>
|
<main class="card">
|
||||||
<h1>AIRA</h1>
|
<h1>AIRA</h1>
|
||||||
<h3>Local network secure P2P communications</h3>
|
<h3>Local network secure P2P communications</h3>
|
||||||
<p id="error_msg">ERROR_MSG</p>
|
<p id="error_msg">ERROR_MSG</p>
|
||||||
<div id="login_page" class="action_page">
|
<div id="login_page" class="action_page">
|
||||||
<h2>Login:</h2>
|
<h2>Login:</h2>
|
||||||
<form id="login_form" method="POST" action="/login">
|
<form id="login_form" method="POST" action="/login">
|
||||||
<div id="identity"></div>
|
<div id="identity">
|
||||||
|
<img class="avatar" src="/avatar/self"/>
|
||||||
|
<h2 id="identityName"></h2>
|
||||||
|
</div>
|
||||||
<input name="password" type="password" placeholder="Password">
|
<input name="password" type="password" placeholder="Password">
|
||||||
<button type="submit">Login</button>
|
<button type="submit">Login</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div id="create_page" class="action_page">
|
<div id="create_page" class="action_page">
|
||||||
<h2>Create New Identity:</h2>
|
<h2>Create a new identity:</h2>
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
|
<div id="avatarContainer">
|
||||||
|
<label>
|
||||||
|
<input type="file" accept="image/*">
|
||||||
|
<img class="avatar unset" src="/static/imgs/icons/profile/ACCENT_COLOR"/>
|
||||||
|
<p>Upload</p>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<input type="text" name="name" placeholder="Name" required>
|
<input type="text" name="name" placeholder="Name" required>
|
||||||
<label class="checkbox">
|
<label class="checkbox">
|
||||||
<input id="enable_password" type="checkbox">
|
<input id="enable_password" type="checkbox">
|
||||||
@ -142,12 +152,20 @@
|
|||||||
</main>
|
</main>
|
||||||
<script src="/static/commons/script.js"></script>
|
<script src="/static/commons/script.js"></script>
|
||||||
<script>
|
<script>
|
||||||
let identity_name = IDENTITY_NAME;
|
let identityName = IDENTITY_NAME;
|
||||||
if (identity_name == null) {
|
if (identityName == null) {
|
||||||
document.getElementById("login_page").style.display = "none";
|
document.getElementById("login_page").style.display = "none";
|
||||||
|
document.querySelector("#avatarContainer input").onchange = function(event) {
|
||||||
|
uploadAvatar(event, function() {
|
||||||
|
let img = document.querySelector("#avatarContainer .avatar");
|
||||||
|
img.src = "/avatar/self?"+Date.now();
|
||||||
|
img.classList.remove("unset");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
let passwordInputs = document.querySelectorAll("#create_page input[type=\"password\"]");
|
||||||
let enable_password = document.getElementById("enable_password");
|
let enable_password = document.getElementById("enable_password");
|
||||||
enable_password.onchange = function() {
|
enable_password.onchange = function() {
|
||||||
document.querySelectorAll("#create_page input[type=\"password\"]").forEach(function(i) {
|
passwordInputs.forEach(function(i) {
|
||||||
if (enable_password.checked) {
|
if (enable_password.checked) {
|
||||||
i.style.display = "block";
|
i.style.display = "block";
|
||||||
} else {
|
} else {
|
||||||
@ -156,11 +174,8 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let identity = document.getElementById("identity");
|
let h2Name = document.getElementById("identityName");
|
||||||
identity.appendChild(generateSelfAvatar(Date.now()));
|
h2Name.textContent = identityName;
|
||||||
let h2 = document.createElement("h2");
|
|
||||||
h2.textContent = identity_name;
|
|
||||||
identity.appendChild(h2);
|
|
||||||
document.getElementById("create_page").style.display = "none";
|
document.getElementById("create_page").style.display = "none";
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -14,15 +14,6 @@
|
|||||||
background-color: black;
|
background-color: black;
|
||||||
}
|
}
|
||||||
main {
|
main {
|
||||||
max-width: 500px;
|
|
||||||
background-color: #2B2F31;
|
|
||||||
border-radius: 10px;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
margin: auto;
|
|
||||||
padding: 50px;
|
padding: 50px;
|
||||||
}
|
}
|
||||||
img {
|
img {
|
||||||
@ -37,7 +28,7 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main>
|
<main class="card">
|
||||||
<img src="/static/imgs/frog">
|
<img src="/static/imgs/frog">
|
||||||
<p>You've been successfully logged out.</p>
|
<p>You've been successfully logged out.</p>
|
||||||
</main>
|
</main>
|
||||||
|
@ -113,9 +113,15 @@ impl Identity {
|
|||||||
db.execute(&format!("UPDATE {} SET name=?1 WHERE uuid=?2", CONTACTS_TABLE), [encrypted_name.as_slice(), uuid.as_bytes()])
|
db.execute(&format!("UPDATE {} SET name=?1 WHERE uuid=?2", CONTACTS_TABLE), [encrypted_name.as_slice(), uuid.as_bytes()])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_contact_avatar(&self, contact_uuid: &Uuid, avatar_uuid: &Uuid) -> Result<usize, rusqlite::Error> {
|
pub fn set_contact_avatar(&self, contact_uuid: &Uuid, avatar_uuid: Option<&Uuid>) -> Result<usize, rusqlite::Error> {
|
||||||
let db = Connection::open(get_database_path())?;
|
let db = Connection::open(get_database_path())?;
|
||||||
db.execute(&format!("UPDATE {} SET avatar=?1 WHERE uuid=?2", CONTACTS_TABLE), params![&avatar_uuid.as_bytes()[..], &contact_uuid.as_bytes()[..]])
|
match avatar_uuid {
|
||||||
|
Some(avatar_uuid) => db.execute(&format!("UPDATE {} SET avatar=?1 WHERE uuid=?2", CONTACTS_TABLE), params![&avatar_uuid.as_bytes()[..], &contact_uuid.as_bytes()[..]]),
|
||||||
|
None => {
|
||||||
|
db.execute(&format!("DELETE FROM {} WHERE uuid=(SELECT avatar FROM {} WHERE uuid=?)", AVATARS_TABLE, CONTACTS_TABLE), params![&contact_uuid.as_bytes()[..]])?;
|
||||||
|
db.execute(&format!("UPDATE {} SET avatar=NULL WHERE uuid=?", CONTACTS_TABLE), params![&contact_uuid.as_bytes()[..]])
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_contact_seen(&self, uuid: &Uuid, seen: bool) -> Result<usize, rusqlite::Error> {
|
pub fn set_contact_seen(&self, uuid: &Uuid, seen: bool) -> Result<usize, rusqlite::Error> {
|
||||||
@ -473,6 +479,11 @@ impl Identity {
|
|||||||
db.upsert(DBKeys::AVATAR, avatar)
|
db.upsert(DBKeys::AVATAR, avatar)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn remove_identity_avatar() -> Result<usize, rusqlite::Error> {
|
||||||
|
let db = KeyValueTable::new(&get_database_path(), MAIN_TABLE)?;
|
||||||
|
db.del(DBKeys::AVATAR)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_identity_avatar() -> Result<Vec<u8>, rusqlite::Error> {
|
pub fn get_identity_avatar() -> Result<Vec<u8>, rusqlite::Error> {
|
||||||
let db = KeyValueTable::new(&get_database_path(), MAIN_TABLE)?;
|
let db = KeyValueTable::new(&get_database_path(), MAIN_TABLE)?;
|
||||||
db.get(DBKeys::AVATAR)
|
db.get(DBKeys::AVATAR)
|
||||||
|
@ -22,9 +22,9 @@ impl<'a> KeyValueTable<'a> {
|
|||||||
None => Err(rusqlite::Error::QueryReturnedNoRows)
|
None => Err(rusqlite::Error::QueryReturnedNoRows)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*pub fn del(&self, key: &str) -> Result<usize, Error> {
|
pub fn del(&self, key: &str) -> Result<usize, Error> {
|
||||||
self.db.execute(&format!("DELETE FROM {} WHERE key=\"{}\"", self.table_name, key), NO_PARAMS)
|
self.db.execute(&format!("DELETE FROM {} WHERE key=\"{}\"", self.table_name, key), [])
|
||||||
}*/
|
}
|
||||||
pub fn update(&self, key: &str, value: &[u8]) -> Result<usize, Error> {
|
pub fn update(&self, key: &str, value: &[u8]) -> Result<usize, Error> {
|
||||||
self.db.execute(&format!("UPDATE {} SET value=? WHERE key=\"{}\"", self.table_name, key), params![value])
|
self.db.execute(&format!("UPDATE {} SET value=? WHERE key=\"{}\"", self.table_name, key), params![value])
|
||||||
}
|
}
|
||||||
|
125
src/main.rs
125
src/main.rs
@ -231,6 +231,12 @@ async fn websocket_worker(mut ui_connection: UiConnection, global_vars: Arc<RwLo
|
|||||||
buff: protocol::ask_profile_info()
|
buff: protocol::ask_profile_info()
|
||||||
}).await;
|
}).await;
|
||||||
}
|
}
|
||||||
|
"remove_avatar" => {
|
||||||
|
match session_manager.remove_avatar().await {
|
||||||
|
Ok(_) => ui_connection.on_avatar_changed(None),
|
||||||
|
Err(e) => print_error!(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
"set_use_padding" => {
|
"set_use_padding" => {
|
||||||
let use_padding: bool = args[1].parse().unwrap();
|
let use_padding: bool = args[1].parse().unwrap();
|
||||||
if let Err(e) = session_manager.identity.write().unwrap().as_mut().unwrap().set_use_padding(use_padding) {
|
if let Err(e) = session_manager.identity.write().unwrap().as_mut().unwrap().set_use_padding(use_padding) {
|
||||||
@ -240,9 +246,7 @@ async fn websocket_worker(mut ui_connection: UiConnection, global_vars: Arc<RwLo
|
|||||||
"change_name" => {
|
"change_name" => {
|
||||||
let new_name = &msg[args[0].len()+1..];
|
let new_name = &msg[args[0].len()+1..];
|
||||||
match session_manager.change_name(new_name.to_string()).await {
|
match session_manager.change_name(new_name.to_string()).await {
|
||||||
Ok(_) => {
|
Ok(_) => ui_connection.set_name(new_name),
|
||||||
ui_connection.set_name(new_name)
|
|
||||||
}
|
|
||||||
Err(e) => print_error!(e)
|
Err(e) => print_error!(e)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -305,32 +309,38 @@ fn is_authenticated(req: &HttpRequest) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_set_avatar(req: HttpRequest, mut payload: Multipart) -> HttpResponse {
|
async fn handle_set_avatar(req: HttpRequest, mut payload: Multipart) -> HttpResponse {
|
||||||
if is_authenticated(&req) {
|
if let Ok(Some(mut field)) = payload.try_next().await {
|
||||||
while let Ok(Some(mut field)) = payload.try_next().await {
|
let content_disposition = field.content_disposition().unwrap();
|
||||||
let content_disposition = field.content_disposition().unwrap();
|
if let Some(name) = content_disposition.get_name() {
|
||||||
if let Some(name) = content_disposition.get_name() {
|
if name == "avatar" {
|
||||||
if name == "avatar" {
|
let mut buffer = Vec::new();
|
||||||
let mut buffer = Vec::new();
|
while let Some(Ok(chunk)) = field.next().await {
|
||||||
while let Some(Ok(chunk)) = field.next().await {
|
buffer.extend(chunk);
|
||||||
buffer.extend(chunk);
|
}
|
||||||
}
|
match image::guess_format(&buffer) {
|
||||||
match image::guess_format(&buffer) {
|
Ok(format) => {
|
||||||
Ok(format) => {
|
match image::load_from_memory_with_format(&buffer, format) {
|
||||||
match image::load_from_memory_with_format(&buffer, format) {
|
Ok(image) => {
|
||||||
Ok(image) => {
|
let (width, height) = image.dimensions();
|
||||||
let (width, height) = image.dimensions();
|
let image = if width < height {
|
||||||
let image = if width < height {
|
image.crop_imm(0, (height-width)/2, width, width)
|
||||||
image.crop_imm(0, (height-width)/2, width, width)
|
} else if width > height {
|
||||||
} else if width > height {
|
image.crop_imm((width-height)/2, 0, height, height)
|
||||||
image.crop_imm((width-height)/2, 0, height, height)
|
} else {
|
||||||
} else {
|
image
|
||||||
image
|
};
|
||||||
};
|
let mut avatar = Vec::new();
|
||||||
let mut avatar = Vec::new();
|
image.write_to(&mut avatar, format).unwrap();
|
||||||
image.write_to(&mut avatar, format).unwrap();
|
let global_vars = req.app_data::<Data<Arc<RwLock<GlobalVars>>>>().unwrap();
|
||||||
let global_vars = req.app_data::<Data<Arc<RwLock<GlobalVars>>>>().unwrap();
|
let session_manager = &global_vars.read().unwrap().session_manager;
|
||||||
match global_vars.read().unwrap().session_manager.set_avatar(&avatar).await {
|
let is_authenticated = is_authenticated(&req);
|
||||||
|
let is_running = session_manager.is_identity_loaded();
|
||||||
|
if is_authenticated || !is_running {
|
||||||
|
match Identity::set_identity_avatar(&avatar) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
|
if is_authenticated && is_running {
|
||||||
|
session_manager.send_avatar(&avatar).await;
|
||||||
|
}
|
||||||
return HttpResponse::Ok().finish();
|
return HttpResponse::Ok().finish();
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -339,11 +349,11 @@ async fn handle_set_avatar(req: HttpRequest, mut payload: Multipart) -> HttpResp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => print_error!(e)
|
|
||||||
}
|
}
|
||||||
|
Err(e) => print_error!(e)
|
||||||
}
|
}
|
||||||
Err(e) => print_error!(e)
|
|
||||||
}
|
}
|
||||||
|
Err(e) => print_error!(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -351,15 +361,18 @@ async fn handle_set_avatar(req: HttpRequest, mut payload: Multipart) -> HttpResp
|
|||||||
HttpResponse::BadRequest().finish()
|
HttpResponse::BadRequest().finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reply_with_avatar(avatar: Option<Vec<u8>>, name: &str) -> HttpResponse {
|
fn reply_with_avatar(avatar: Option<Vec<u8>>, name: Option<&str>) -> HttpResponse {
|
||||||
match avatar {
|
match avatar {
|
||||||
Some(avatar) => HttpResponse::Ok().content_type("image/*").body(avatar),
|
Some(avatar) => HttpResponse::Ok().content_type("image/*").body(avatar),
|
||||||
None => {
|
None => match name {
|
||||||
#[cfg(not(debug_assertions))]
|
Some(name) => {
|
||||||
let svg = include_str!(concat!(env!("OUT_DIR"), "/text_avatar.svg"));
|
#[cfg(not(debug_assertions))]
|
||||||
#[cfg(debug_assertions)]
|
let svg = include_str!(concat!(env!("OUT_DIR"), "/text_avatar.svg"));
|
||||||
let svg = replace_fields("src/frontend/imgs/text_avatar.svg");
|
#[cfg(debug_assertions)]
|
||||||
HttpResponse::Ok().content_type("image/svg+xml").body(svg.replace("LETTER", &name.chars().nth(0).unwrap().to_string()))
|
let svg = replace_fields("src/frontend/imgs/text_avatar.svg");
|
||||||
|
HttpResponse::Ok().content_type("image/svg+xml").body(svg.replace("LETTER", &name.chars().nth(0).unwrap().to_string()))
|
||||||
|
}
|
||||||
|
None => HttpResponse::InternalServerError().finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -368,12 +381,12 @@ fn handle_avatar(req: HttpRequest) -> HttpResponse {
|
|||||||
let splits: Vec<&str> = req.path()[1..].split("/").collect();
|
let splits: Vec<&str> = req.path()[1..].split("/").collect();
|
||||||
if splits.len() == 2 {
|
if splits.len() == 2 {
|
||||||
if splits[1] == "self" {
|
if splits[1] == "self" {
|
||||||
return reply_with_avatar(Identity::get_identity_avatar().ok(), &Identity::get_identity_name().unwrap());
|
return reply_with_avatar(Identity::get_identity_avatar().ok(), Identity::get_identity_name().ok().as_deref());
|
||||||
}
|
}
|
||||||
} else if splits.len() == 3 {
|
} else if splits.len() == 3 {
|
||||||
if let Ok(session_id) = splits[1].parse() {
|
if let Ok(session_id) = splits[1].parse() {
|
||||||
let global_vars = req.app_data::<Data<Arc<RwLock<GlobalVars>>>>().unwrap();
|
let global_vars = req.app_data::<Data<Arc<RwLock<GlobalVars>>>>().unwrap();
|
||||||
return reply_with_avatar(global_vars.read().unwrap().session_manager.get_avatar(&session_id), splits[2]);
|
return reply_with_avatar(global_vars.read().unwrap().session_manager.get_avatar(&session_id), Some(splits[2]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HttpResponse::BadRequest().finish()
|
HttpResponse::BadRequest().finish()
|
||||||
@ -443,9 +456,7 @@ async fn handle_send_file(req: HttpRequest, mut payload: Multipart) -> HttpRespo
|
|||||||
loop {
|
loop {
|
||||||
chunk_buffer.extend(&pending_buffer);
|
chunk_buffer.extend(&pending_buffer);
|
||||||
pending_buffer.clear();
|
pending_buffer.clear();
|
||||||
println!("Calling next()");
|
|
||||||
while let Some(Ok(chunk)) = field.next().await {
|
while let Some(Ok(chunk)) = field.next().await {
|
||||||
println!("Not None");
|
|
||||||
if chunk_buffer.len()+chunk.len() <= constants::FILE_CHUNK_SIZE {
|
if chunk_buffer.len()+chunk.len() <= constants::FILE_CHUNK_SIZE {
|
||||||
chunk_buffer.extend(chunk);
|
chunk_buffer.extend(chunk);
|
||||||
} else if chunk_buffer.len() == constants::FILE_CHUNK_SIZE {
|
} else if chunk_buffer.len() == constants::FILE_CHUNK_SIZE {
|
||||||
@ -458,7 +469,6 @@ async fn handle_send_file(req: HttpRequest, mut payload: Multipart) -> HttpRespo
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!("May be None");
|
|
||||||
if !global_vars_read.session_manager.send_command(&session_id, SessionCommand::EncryptFileChunk{
|
if !global_vars_read.session_manager.send_command(&session_id, SessionCommand::EncryptFileChunk{
|
||||||
plain_text: chunk_buffer.clone()
|
plain_text: chunk_buffer.clone()
|
||||||
}).await {
|
}).await {
|
||||||
@ -542,6 +552,10 @@ fn on_identity_loaded(identity: Identity, global_vars: &Arc<RwLock<GlobalVars>>)
|
|||||||
login(identity, global_vars)
|
login(identity, global_vars)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct LoginParams {
|
||||||
|
password: String,
|
||||||
|
}
|
||||||
fn handle_login(req: HttpRequest, mut params: web::Form<LoginParams>) -> HttpResponse {
|
fn handle_login(req: HttpRequest, mut params: web::Form<LoginParams>) -> HttpResponse {
|
||||||
let response = match Identity::load_identity(Some(params.password.as_bytes())) {
|
let response = match Identity::load_identity(Some(params.password.as_bytes())) {
|
||||||
Ok(identity) => {
|
Ok(identity) => {
|
||||||
@ -556,7 +570,7 @@ fn handle_login(req: HttpRequest, mut params: web::Form<LoginParams>) -> HttpRes
|
|||||||
|
|
||||||
fn get_login_body(error_msg: Option<&str>) -> Result<String, rusqlite::Error> {
|
fn get_login_body(error_msg: Option<&str>) -> Result<String, rusqlite::Error> {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
let html = fs::read_to_string("src/frontend/login.html").unwrap();
|
let html = replace_fields("src/frontend/login.html");
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
let html = include_str!(concat!(env!("OUT_DIR"), "/login.html"));
|
let html = include_str!(concat!(env!("OUT_DIR"), "/login.html"));
|
||||||
Ok(html
|
Ok(html
|
||||||
@ -566,8 +580,10 @@ fn get_login_body(error_msg: Option<&str>) -> Result<String, rusqlite::Error> {
|
|||||||
})
|
})
|
||||||
.replace("IDENTITY_NAME", &match Identity::get_identity_name() {
|
.replace("IDENTITY_NAME", &match Identity::get_identity_name() {
|
||||||
Ok(name) => format!("\"{}\"", html_escape::encode_double_quoted_attribute(&name)),
|
Ok(name) => format!("\"{}\"", html_escape::encode_double_quoted_attribute(&name)),
|
||||||
Err(e) => {
|
Err(_) => {
|
||||||
print_error!(e);
|
if let Err(e) = Identity::remove_identity_avatar() {
|
||||||
|
print_error!(e);
|
||||||
|
}
|
||||||
"null".to_owned()
|
"null".to_owned()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -585,6 +601,12 @@ fn generate_login_response(error_msg: Option<&str>) -> HttpResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct CreateParams {
|
||||||
|
name: String,
|
||||||
|
password: String,
|
||||||
|
password_confirm: String,
|
||||||
|
}
|
||||||
async fn handle_create(req: HttpRequest, mut params: web::Form<CreateParams>) -> HttpResponse {
|
async fn handle_create(req: HttpRequest, mut params: web::Form<CreateParams>) -> HttpResponse {
|
||||||
let response = if params.password == params.password_confirm {
|
let response = if params.password == params.password_confirm {
|
||||||
match Identity::create_identidy(
|
match Identity::create_identidy(
|
||||||
@ -697,6 +719,7 @@ fn handle_static(req: HttpRequest) -> HttpResponse {
|
|||||||
"refresh" => Some(include_str!("frontend/imgs/icons/refresh.svg")),
|
"refresh" => Some(include_str!("frontend/imgs/icons/refresh.svg")),
|
||||||
"info" => Some(include_str!("frontend/imgs/icons/info.svg")),
|
"info" => Some(include_str!("frontend/imgs/icons/info.svg")),
|
||||||
"delete_conversation" => Some(include_str!("frontend/imgs/icons/delete_conversation.svg")),
|
"delete_conversation" => Some(include_str!("frontend/imgs/icons/delete_conversation.svg")),
|
||||||
|
"profile" => Some(include_str!("frontend/imgs/icons/profile.svg")),
|
||||||
_ => None
|
_ => None
|
||||||
} {
|
} {
|
||||||
Some(body) => {
|
Some(body) => {
|
||||||
@ -792,18 +815,6 @@ async fn start_http_server(global_vars: Arc<RwLock<GlobalVars>>) -> io::Result<(
|
|||||||
server.run().await
|
server.run().await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
struct LoginParams {
|
|
||||||
password: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
struct CreateParams {
|
|
||||||
name: String,
|
|
||||||
password: String,
|
|
||||||
password_confirm: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct GlobalVars {
|
struct GlobalVars {
|
||||||
session_manager: Arc<SessionManager>,
|
session_manager: Arc<SessionManager>,
|
||||||
websocket_port: u16,
|
websocket_port: u16,
|
||||||
|
@ -9,11 +9,12 @@ impl Headers {
|
|||||||
pub const ASK_PROFILE_INFO: u8 = 0x02;
|
pub const ASK_PROFILE_INFO: u8 = 0x02;
|
||||||
pub const NAME: u8 = 0x03;
|
pub const NAME: u8 = 0x03;
|
||||||
pub const AVATAR: u8 = 0x04;
|
pub const AVATAR: u8 = 0x04;
|
||||||
pub const ASK_LARGE_FILES: u8 = 0x05;
|
pub const REMOVE_AVATAR: u8 = 0x05;
|
||||||
pub const ACCEPT_LARGE_FILES: u8 = 0x06;
|
pub const ASK_LARGE_FILES: u8 = 0x06;
|
||||||
pub const LARGE_FILE_CHUNK: u8 = 0x07;
|
pub const ACCEPT_LARGE_FILES: u8 = 0x07;
|
||||||
pub const ACK_CHUNK: u8 = 0x08;
|
pub const LARGE_FILE_CHUNK: u8 = 0x08;
|
||||||
pub const ABORT_FILES_TRANSFER: u8 = 0x09;
|
pub const ACK_CHUNK: u8 = 0x09;
|
||||||
|
pub const ABORT_FILES_TRANSFER: u8 = 0x0a;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_message(message: String) -> Vec<u8> {
|
pub fn new_message(message: String) -> Vec<u8> {
|
||||||
@ -84,4 +85,8 @@ pub fn parse_ask_files(buffer: &[u8]) -> Option<Vec<(u64, String)>> {
|
|||||||
|
|
||||||
pub fn avatar(avatar: &[u8]) -> Vec<u8> {
|
pub fn avatar(avatar: &[u8]) -> Vec<u8> {
|
||||||
[&[Headers::AVATAR], avatar].concat()
|
[&[Headers::AVATAR], avatar].concat()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_avatar() -> Vec<u8> {
|
||||||
|
vec![Headers::REMOVE_AVATAR]
|
||||||
}
|
}
|
@ -136,6 +136,21 @@ impl SessionManager {
|
|||||||
self.not_seen.write().unwrap().retain(|x| x != session_id);
|
self.not_seen.write().unwrap().retain(|x| x != session_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_avatar_uuid(&self, session_id: &usize, avatar_uuid: Option<Uuid>) {
|
||||||
|
let mut loaded_contacts = self.loaded_contacts.write().unwrap();
|
||||||
|
if let Some(contact) = loaded_contacts.get_mut(session_id) {
|
||||||
|
contact.avatar = avatar_uuid;
|
||||||
|
if let Err(e) = self.identity.read().unwrap().as_ref().unwrap().set_contact_avatar(&contact.uuid, avatar_uuid.as_ref()) {
|
||||||
|
print_error!(e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.sessions.write().unwrap().get_mut(session_id).unwrap().avatar = avatar_uuid;
|
||||||
|
}
|
||||||
|
self.with_ui_connection(|ui_connection| {
|
||||||
|
ui_connection.on_avatar_changed(Some(session_id));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async fn send_msg(&self, session_id: usize, session_write: &mut SessionWriteHalf, buff: &[u8], is_sending: &mut bool, file_ack_sender: &mut Option<Sender<bool>>) -> Result<(), PsecError> {
|
async fn send_msg(&self, session_id: usize, session_write: &mut SessionWriteHalf, buff: &[u8], is_sending: &mut bool, file_ack_sender: &mut Option<Sender<bool>>) -> Result<(), PsecError> {
|
||||||
self.encrypt_and_send(session_write, &buff).await?;
|
self.encrypt_and_send(session_write, &buff).await?;
|
||||||
if buff[0] == protocol::Headers::ACCEPT_LARGE_FILES {
|
if buff[0] == protocol::Headers::ACCEPT_LARGE_FILES {
|
||||||
@ -342,23 +357,8 @@ impl SessionManager {
|
|||||||
match image::load_from_memory(&buffer[1..]) {
|
match image::load_from_memory(&buffer[1..]) {
|
||||||
Ok(image) => {
|
Ok(image) => {
|
||||||
drop(image);
|
drop(image);
|
||||||
let identity_opt = self.identity.read().unwrap();
|
match self.identity.read().unwrap().as_ref().unwrap().store_avatar(&buffer[1..]) {
|
||||||
let identity = identity_opt.as_ref().unwrap();
|
Ok(avatar_uuid) => self.set_avatar_uuid(&session_id, Some(avatar_uuid)),
|
||||||
match identity.store_avatar(&buffer[1..]) {
|
|
||||||
Ok(avatar_uuid) => {
|
|
||||||
let mut loaded_contacts = self.loaded_contacts.write().unwrap();
|
|
||||||
if let Some(contact) = loaded_contacts.get_mut(&session_id) {
|
|
||||||
contact.avatar = Some(avatar_uuid);
|
|
||||||
if let Err(e) = identity.set_contact_avatar(&contact.uuid, &avatar_uuid) {
|
|
||||||
print_error!(e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.sessions.write().unwrap().get_mut(&session_id).unwrap().avatar = Some(avatar_uuid);
|
|
||||||
}
|
|
||||||
self.with_ui_connection(|ui_connection| {
|
|
||||||
ui_connection.on_avatar_set(&session_id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Err(e) => print_error!(e)
|
Err(e) => print_error!(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -366,6 +366,7 @@ impl SessionManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
protocol::Headers::REMOVE_AVATAR => self.set_avatar_uuid(&session_id, None),
|
||||||
_ => {
|
_ => {
|
||||||
let header = buffer[0];
|
let header = buffer[0];
|
||||||
let buffer = match header {
|
let buffer = match header {
|
||||||
@ -680,29 +681,37 @@ impl SessionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_must_use)]
|
#[allow(unused_must_use)]
|
||||||
pub async fn set_avatar(&self, avatar: &[u8]) -> Result<(), rusqlite::Error> {
|
pub async fn send_avatar(&self, avatar: &[u8]) {
|
||||||
Identity::set_identity_avatar(&avatar)?;
|
|
||||||
let avatar_msg = protocol::avatar(&avatar);
|
let avatar_msg = protocol::avatar(&avatar);
|
||||||
for sender in self.get_all_senders().into_iter() {
|
for sender in self.get_all_senders().into_iter() {
|
||||||
sender.send(SessionCommand::Send {
|
sender.send(SessionCommand::Send {
|
||||||
buff: avatar_msg.clone()
|
buff: avatar_msg.clone()
|
||||||
}).await;
|
}).await;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_must_use)]
|
||||||
|
pub async fn remove_avatar(&self) -> Result<(), rusqlite::Error> {
|
||||||
|
Identity::remove_identity_avatar()?;
|
||||||
|
let avatar_msg = protocol::remove_avatar();
|
||||||
|
for sender in self.get_all_senders().into_iter() {
|
||||||
|
sender.send(SessionCommand::Send {
|
||||||
|
buff: avatar_msg.clone()
|
||||||
|
}).await;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_must_use)]
|
#[allow(unused_must_use)]
|
||||||
pub async fn change_name(&self, new_name: String) -> Result<usize, rusqlite::Error> {
|
pub async fn change_name(&self, new_name: String) -> Result<(), rusqlite::Error> {
|
||||||
let telling_name = protocol::name(&new_name);
|
let telling_name = protocol::name(&new_name);
|
||||||
let result = self.identity.write().unwrap().as_mut().unwrap().change_name(new_name);
|
self.identity.write().unwrap().as_mut().unwrap().change_name(new_name)?;
|
||||||
if result.is_ok() {
|
for sender in self.get_all_senders().into_iter() {
|
||||||
for sender in self.get_all_senders().into_iter() {
|
sender.send(SessionCommand::Send {
|
||||||
sender.send(SessionCommand::Send {
|
buff: telling_name.clone()
|
||||||
buff: telling_name.clone()
|
}).await;
|
||||||
}).await;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
result
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_must_use)]
|
#[allow(unused_must_use)]
|
||||||
|
@ -112,8 +112,11 @@ mod ui_messages {
|
|||||||
pub fn on_name_told(session_id: &usize, name: &str) -> Message {
|
pub fn on_name_told(session_id: &usize, name: &str) -> Message {
|
||||||
Message::from(format!("name_told {} {}", session_id, name))
|
Message::from(format!("name_told {} {}", session_id, name))
|
||||||
}
|
}
|
||||||
pub fn on_avatar_set(session_id: &usize) -> Message {
|
pub fn on_avatar_changed(session_id: Option<&usize>) -> Message {
|
||||||
simple_event("avatar_set", session_id)
|
match session_id {
|
||||||
|
Some(session_id) => simple_event("avatar_changed", session_id),
|
||||||
|
None => Message::from("avatar_changed self")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn set_as_contact(session_id: usize, name: &str, verified: bool, fingerprint: &str) -> Message {
|
pub fn set_as_contact(session_id: usize, name: &str, verified: bool, fingerprint: &str) -> Message {
|
||||||
Message::from(format!("is_contact {} {} {} {}", session_id, verified, fingerprint, name))
|
Message::from(format!("is_contact {} {} {} {}", session_id, verified, fingerprint, name))
|
||||||
@ -182,8 +185,8 @@ impl UiConnection {
|
|||||||
pub fn on_name_told(&mut self, session_id: &usize, name: &str) {
|
pub fn on_name_told(&mut self, session_id: &usize, name: &str) {
|
||||||
self.write_message(ui_messages::on_name_told(session_id, name));
|
self.write_message(ui_messages::on_name_told(session_id, name));
|
||||||
}
|
}
|
||||||
pub fn on_avatar_set(&mut self, session_id: &usize) {
|
pub fn on_avatar_changed(&mut self, session_id: Option<&usize>) {
|
||||||
self.write_message(ui_messages::on_avatar_set(session_id));
|
self.write_message(ui_messages::on_avatar_changed(session_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inc_files_transfer(&mut self, session_id: &usize, chunk_size: u64) {
|
pub fn inc_files_transfer(&mut self, session_id: &usize, chunk_size: u64) {
|
||||||
|
Loading…
Reference in New Issue
Block a user