209 lines
No EOL
8.9 KiB
HTML
209 lines
No EOL
8.9 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
|
<title>Miarven</title>
|
|
<link rel="stylesheet" href="style.css">
|
|
<link rel="icon" type="image/svg+xml" href="favicon.svg">
|
|
<script src="crypto-pq.js"></script>
|
|
|
|
<!-- debug
|
|
<script src="https://cdn.jsdelivr.net/npm/eruda"></script>
|
|
<script>eruda.init();</script> -->
|
|
|
|
</head>
|
|
<body>
|
|
<loading>
|
|
<div style="display: flex;justify-content: center;align-items: center;height:100%;flex-direction: column;text-align: center;">
|
|
<img src="favicon.svg" style="width: 6rem">
|
|
<p id="loadingstatus"></p>
|
|
</div>
|
|
</loading>
|
|
<main style="display: none;">
|
|
<sidebar>
|
|
<sidebarelement id="sidebar-home">
|
|
<indicator class="active">
|
|
</indicator>
|
|
<button class="icon-button" onclick="gotoHome()">
|
|
<svg viewBox="-1 -1 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<path d="M1 6V15H6V11C6 9.89543 6.89543 9 8 9C9.10457 9 10 9.89543 10 11V15H15V6L8 0L1 6Z" stroke="var(--text-color)" stroke-width="1.28" stroke-linejoin="round" stroke-linecap="round" transform="translate(0 0.5)"/>
|
|
</svg>
|
|
</button>
|
|
</sidebarelement>
|
|
<sidebarelement id="sidebar-add">
|
|
<indicator>
|
|
</indicator>
|
|
<button class="icon-button">
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="var(--text-color)"
|
|
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<line x1="12" y1="5" x2="12" y2="19"></line>
|
|
<line x1="5" y1="12" x2="19" y2="12"></line>
|
|
</svg>
|
|
</button>
|
|
</sidebarelement>
|
|
<hr>
|
|
<hr class="bottom-element">
|
|
<sidebarelement id="sidebar-inbox">
|
|
<indicator>
|
|
</indicator>
|
|
<button class="icon-button" onclick="gotoInbox()">
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="var(--text-color)"><path d="M216-144q-29 0-50.5-21.5T144-216v-528q0-29.7 21.5-50.85Q187-816 216-816h528q29.7 0 50.85 21.15Q816-773.7 816-744v528q0 29-21.15 50.5T744-144H216Zm0-72h528v-144H632q-23 43-63.5 69.5T480-264q-49 0-89.5-26T328-360H216v144Zm332-148q28-28 28-68h168v-312H216v312h168q0 40 28 68t68 28q40 0 68-28ZM216-216h528-528Z"/></svg>
|
|
</button>
|
|
</sidebarelement>
|
|
<sidebarelement id="sidebar-profile">
|
|
<indicator>
|
|
</indicator>
|
|
<button class="icon-button">
|
|
<img id="sidebar-pfp">
|
|
</button>
|
|
</sidebarelement>
|
|
</sidebar>
|
|
<sidebar class="second" id="roomsbar">
|
|
<roomtopbar>
|
|
<inherit style="display: flex;"></inherit>
|
|
</roomtopbar>
|
|
<roomcontent2>
|
|
</roomcontent2>
|
|
</sidebar>
|
|
<roomcontent>
|
|
<roomtopbar>
|
|
<inherit style="display: flex;"></inherit>
|
|
</roomtopbar>
|
|
<roomcontent2>
|
|
</roomcontent2>
|
|
</roomcontent>
|
|
<sidebar class="second" id="roomdetailsbar" style="display: none;">
|
|
<roomtopbar>
|
|
<inherit style="display: flex;"></inherit>
|
|
</roomtopbar>
|
|
<roomcontent2>
|
|
</roomcontent2>
|
|
</sidebar>
|
|
</main>
|
|
<div id="fixed-context-menu" class="context-menu">
|
|
|
|
</div>
|
|
|
|
<div id="profile-popup-container" class="profile-popup-container" style="display: none;">
|
|
<div class="profile-popup-bg" onclick="closeProfile()"></div>
|
|
<div class="profile-popup-content">
|
|
<button class="profile-popup-close-btn" onclick="closeProfile()">
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="var(--text-color)"><path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z"/></svg>
|
|
</button>
|
|
<div id="profile-popup-inner"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="cropper-popup-container" class="profile-popup-container" style="z-index: 9999;">
|
|
<div class="profile-popup-bg" onclick="closeCropper()"></div>
|
|
<div class="profile-popup-content" style="max-width: 40rem; padding: 2rem; display: flex; flex-direction: column; align-items: center; gap: 1rem; height: auto;">
|
|
<h2>Crop Image</h2>
|
|
<div id="cropper-viewport-container" style="position: relative; overflow: hidden; background: #000; width: 100%; max-width: 30rem; border-radius: 0.8rem; border: var(--border-width) solid rgba(255,255,255,0.2);">
|
|
<img id="cropper-image" src="" style="position: absolute; transform-origin: 0 0; cursor: move;">
|
|
</div>
|
|
<div style="display: flex; align-items: center; gap: 1rem; width: 100%; max-width: 30rem;">
|
|
<span>Zoom</span>
|
|
<input type="range" id="cropper-zoom" min="0.1" max="5" step="0.01" value="1" style="flex: 1;" oninput="updateCropperZoom()">
|
|
</div>
|
|
<div style="display: flex; gap: 1rem; margin-top: 1rem; width: 100%; max-width: 30rem;">
|
|
<button class="submit-button" style="background: rgba(255,255,255,0.1); color: var(--text-color); flex: 1;" onclick="closeCropper()">Cancel</button>
|
|
<button class="submit-button" style="flex: 1;" onclick="confirmCropper()">Save</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
<script src="screens.js"></script>
|
|
<script>
|
|
async function start() {
|
|
updateLoadingStatus("loading.loading");
|
|
try {
|
|
if (host != null)
|
|
{
|
|
await updateProtocolAndUrl(host);
|
|
}
|
|
else
|
|
{
|
|
await updateProtocolAndUrl(window.location.hostname);
|
|
}
|
|
let localIdRes = await fetchAsync(`${url}/nametoid?u=${username}`);
|
|
let localId = localIdRes.trim();
|
|
sidebarPfp.src = await getAvatarUrl(localId, `${username}:${host}`);
|
|
|
|
showAction("action.auth", "startauth");
|
|
let res = await Auth(username, password);
|
|
clearAction("startauth");
|
|
if (res.startsWith("success:")) {
|
|
try {
|
|
await ensureUserKeys();
|
|
} catch (e) {
|
|
//if fails continue loading encryption may be broken
|
|
console.error(e);
|
|
}
|
|
gotoHome();
|
|
} else {
|
|
showBlahNotification("error:auth.failed.redirect.to.login");
|
|
await delay(2000);
|
|
window.location.href = "login/index.html";
|
|
}
|
|
updateLoadingStatus("loading.done");
|
|
await loadingFadeOut();
|
|
} catch (e) {
|
|
clearAction("startauth");
|
|
showBlahNotification("error:auth.failed.redirect.to.login");
|
|
await delay(2000);
|
|
window.location.href = "login/index.html";
|
|
}
|
|
}
|
|
|
|
async function refreshDms(waittime = 0, showLoading = true) {
|
|
try {
|
|
if (showLoading) showAction("action.dm.fetch", "dmrefresh");
|
|
await delay(waittime);
|
|
if (window.cachedDms && typeof renderDms === 'function') {
|
|
await renderDms(window.cachedDms);
|
|
}
|
|
let res = await fetchEncrypted("user/dm/list");
|
|
window.cachedDms = res;
|
|
if (typeof renderDms === 'function') {
|
|
await renderDms(res);
|
|
}
|
|
if (showLoading) clearAction("dmrefresh");
|
|
}
|
|
catch (e) {
|
|
if (showLoading) clearAction("dmrefresh");
|
|
showBlahNotification("error:dm.refresh.failed");
|
|
}
|
|
}
|
|
|
|
async function addDm() {
|
|
try {
|
|
showAction("action.dm.adding", "dmadd");
|
|
let username = document.getElementById("addchat-username").value;
|
|
let idRes = await fetchAsync(`${url}/nametoid?u=${username}`);
|
|
if (idRes.startsWith("error") || idRes.trim() === "0" || idRes.trim() === "") {
|
|
clearAction("dmadd");
|
|
showBlahNotification("error:user.not.found");
|
|
return;
|
|
}
|
|
let targetId = idRes.trim();
|
|
let res = await fetchEncrypted("user/dm/invite", targetId);
|
|
|
|
clearAction("dmadd");
|
|
showBlahNotification(res);
|
|
}
|
|
catch (e) {
|
|
console.log(e);
|
|
clearAction("dmadd");
|
|
showBlahNotification("error:chat.add.failed");
|
|
}
|
|
}
|
|
|
|
async function createGroup() {
|
|
|
|
}
|
|
</script>
|
|
<script src="gifuct.js"></script>
|
|
<script src="main.js"></script>
|
|
<script src="userscript.js"></script> |