diff --git a/android/.gitignore b/android/.gitignore
index 34acf79f..be945cd7 100644
--- a/android/.gitignore
+++ b/android/.gitignore
@@ -94,9 +94,10 @@ lint/tmp/
#need this for actions
# Copied web assets
-app/src/main/assets/public
+#app/src/main/assets/public
+#need this for actions
# Generated Config files
-app/src/main/assets/capacitor.config.json
-app/src/main/assets/capacitor.plugins.json
-app/src/main/res/xml/config.xml
+#app/src/main/assets/capacitor.config.json
+#app/src/main/assets/capacitor.plugins.json
+#app/src/main/res/xml/config.xml
diff --git a/android/app/src/main/assets/capacitor.config.json b/android/app/src/main/assets/capacitor.config.json
new file mode 100644
index 00000000..f55049e2
--- /dev/null
+++ b/android/app/src/main/assets/capacitor.config.json
@@ -0,0 +1,13 @@
+{
+ "appId": "olcxja.miarven",
+ "appName": "Miarven",
+ "webDir": "webroot",
+ "plugins": {
+ "SystemBars": {
+ "insetsHandling": "css",
+ "style": "DARK",
+ "hidden": false,
+ "animation": "FADE"
+ }
+ }
+}
diff --git a/android/app/src/main/assets/capacitor.plugins.json b/android/app/src/main/assets/capacitor.plugins.json
new file mode 100644
index 00000000..d717d5dc
--- /dev/null
+++ b/android/app/src/main/assets/capacitor.plugins.json
@@ -0,0 +1,6 @@
+[
+ {
+ "pkg": "@capacitor/status-bar",
+ "classpath": "com.capacitorjs.plugins.statusbar.StatusBarPlugin"
+ }
+]
diff --git a/android/app/src/main/assets/public/Nunito-Regular.ttf b/android/app/src/main/assets/public/Nunito-Regular.ttf
new file mode 100644
index 00000000..be80c3f0
Binary files /dev/null and b/android/app/src/main/assets/public/Nunito-Regular.ttf differ
diff --git a/android/app/src/main/assets/public/cordova.js b/android/app/src/main/assets/public/cordova.js
new file mode 100644
index 00000000..e69de29b
diff --git a/android/app/src/main/assets/public/cordova_plugins.js b/android/app/src/main/assets/public/cordova_plugins.js
new file mode 100644
index 00000000..e69de29b
diff --git a/android/app/src/main/assets/public/favicon.svg b/android/app/src/main/assets/public/favicon.svg
new file mode 100644
index 00000000..d70db7a4
--- /dev/null
+++ b/android/app/src/main/assets/public/favicon.svg
@@ -0,0 +1,29 @@
+
\ No newline at end of file
diff --git a/android/app/src/main/assets/public/index.html b/android/app/src/main/assets/public/index.html
new file mode 100644
index 00000000..125026b0
--- /dev/null
+++ b/android/app/src/main/assets/public/index.html
@@ -0,0 +1,139 @@
+
+
+
+
+
+ Larpix Client
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Home
+
+
+
+
+
+
+
+
+ Splash
+
+
+
+

+
Welcome to Miarven
+
First Larpix client. v1.0
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/assets/public/login/index.html b/android/app/src/main/assets/public/login/index.html
new file mode 100644
index 00000000..9a34169e
--- /dev/null
+++ b/android/app/src/main/assets/public/login/index.html
@@ -0,0 +1,498 @@
+
+
+
+
+
+ Larpix - Authentication
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
![Captcha]()
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/assets/public/main.js b/android/app/src/main/assets/public/main.js
new file mode 100644
index 00000000..efc5d22a
--- /dev/null
+++ b/android/app/src/main/assets/public/main.js
@@ -0,0 +1,460 @@
+console.log(window.location.protocol);
+
+var url = `${window.location.protocol}//${window.location.hostname}/_larpix`;
+var params = new URLSearchParams(window.location.search);
+
+const collapseDmsBtn = document.getElementById("collapse-dms");
+const collapseGroupsBtn = document.getElementById("collapse-groups");
+
+const addDmBtn = document.getElementById("add-dm-btn");
+const addGroupBtn = document.getElementById("add-group-btn");
+
+const sidebarHome = document.getElementById("sidebar-home");
+const sidebarHomeButton = sidebarHome.children.item(1);
+const sidebarHomeIndicator = sidebarHome.children.item(0);
+
+const sidebarAdd = document.getElementById("sidebar-add");
+const sidebarAddButton = sidebarAdd.children.item(1);
+const sidebarAddIndicator = sidebarAdd.children.item(0);
+
+const roomDetailsBar = document.getElementById("roomdetailsbar");
+const roomContent = document.getElementsByTagName("roomcontent")[0];
+const roomsBar = document.getElementById("roomsbar");
+const sideBar = document.getElementsByTagName("sidebar")[0];
+
+const roomContentMain = document.getElementsByTagName("roomcontent2")[0];
+
+const roomContentBar = roomContent.children[0];
+
+
+
+function delay(time) {
+ return new Promise(resolve => setTimeout(resolve, time));
+}
+async function packetEncPass(pass, key, username) {
+ return await encryptWithNonce(pass, key, getNonce(username, key));
+}
+
+async function getNonce(username, key) {
+
+ let nonce;
+ let fetchRes = await (await fetch(`${url}/nextnonce?u=${username}`)).text();
+
+ try {
+ nonce = await decryptString(fetchRes, key);
+ }
+ catch(err) {
+ nonce = await decryptString(fetchRes, "");
+ }
+ return nonce;
+}
+
+async function encryptWithNonce(value, key, nonce) {
+ return await encryptString(value, nonce + key);
+}
+
+async function calcCommunicationKeyClient(p, g, pubServer) {
+ const secretClientBytes = window.crypto.getRandomValues(new Uint8Array(512));
+ const secretClient = BigInt('0x' + Array.from(secretClientBytes).map(b => b.toString(16).padStart(2, '0')).join(''));
+ const pubClient = power(BigInt(g), secretClient, BigInt(p));
+ const sharedSecret = power(BigInt(pubServer), secretClient, BigInt(p));
+
+ let sharedSecretStr = sharedSecret.toString(16);
+
+ if (sharedSecretStr.length % 2 !== 0) {
+ sharedSecretStr = '0' + sharedSecretStr;
+ }
+
+ const sharedSecretBytes = new Uint8Array(sharedSecretStr.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
+
+ const hashBuffer = await window.crypto.subtle.digest('SHA-256', sharedSecretBytes);
+ const aesKey = new Uint8Array(hashBuffer);
+
+ return [pubClient.toString(), aesKey];
+}
+
+function keyDataFromServerJson(jsonFromServer) {
+ const data = JSON.parse(jsonFromServer);
+
+ const p = BigInt(data.p);
+ const g = BigInt(data.g);
+ const pubServer = BigInt(data.pubServer);
+
+ return [p, g, pubServer, data.idKey]
+}
+
+function base64ToUint8(base64) {
+ return Uint8Array.from(atob(base64), c => c.charCodeAt(0));
+}
+function uint8ToBase64(uint8) {
+ return fixBase64Padding(btoa(String.fromCharCode(...uint8)));
+}
+
+async function encrypt(plainText, keyBytes) {
+ const iv = window.crypto.getRandomValues(new Uint8Array(16));
+ const encoder = new TextEncoder();
+ const data = encoder.encode(plainText);
+
+ const cryptoKey = await window.crypto.subtle.importKey(
+ "raw", keyBytes, "AES-CBC", false, ["encrypt"]
+ );
+
+ const encryptedContent = await window.crypto.subtle.encrypt(
+ {name: "AES-CBC", iv: iv},
+ cryptoKey,
+ data
+ );
+ const result = new Uint8Array(iv.length + encryptedContent.byteLength);
+ result.set(iv);
+ result.set(new Uint8Array(encryptedContent), iv.length);
+
+ return uint8ToBase64(result);
+}
+
+async function decrypt(cipherTextBase64, keyBytes) {
+ const fullData = base64ToUint8(cipherTextBase64);
+ const iv = fullData.slice(0, 16);
+ const cipherData = fullData.slice(16);
+
+ const cryptoKey = await window.crypto.subtle.importKey(
+ "raw", keyBytes, "AES-CBC", false, ["decrypt"]
+ );
+
+ const decrypted = await window.crypto.subtle.decrypt(
+ {name: "AES-CBC", iv: iv},
+ cryptoKey,
+ cipherData
+ );
+
+ return new TextDecoder().decode(decrypted);
+}
+
+
+async function getCryptoKey(passphrase) {
+ const encoder = new TextEncoder();
+ const data = encoder.encode(passphrase);
+ const hash = await crypto.subtle.digest('SHA-256', data);
+ return await crypto.subtle.importKey('raw', hash, {name: 'AES-CBC'}, false, ['encrypt', 'decrypt']);
+}
+
+async function encryptBytes(plainBytes, passphrase) {
+ const key = await getCryptoKey(passphrase);
+ const iv = crypto.getRandomValues(new Uint8Array(16));
+
+ const encryptedContent = await crypto.subtle.encrypt(
+ {name: 'AES-CBC', iv: iv},
+ key,
+ plainBytes
+ );
+
+ const result = new Uint8Array(iv.length + encryptedContent.byteLength);
+ result.set(iv);
+ result.set(new Uint8Array(encryptedContent), iv.length);
+ return result;
+}
+
+async function decryptBytes(combinedBytes, passphrase) {
+ const key = await getCryptoKey(passphrase);
+ const iv = combinedBytes.slice(0, 16);
+ const data = combinedBytes.slice(16);
+
+ const decryptedContent = await crypto.subtle.decrypt(
+ {name: 'AES-CBC', iv: iv},
+ key,
+ data
+ );
+
+ return new Uint8Array(decryptedContent);
+}
+
+async function encryptString(plainText, passphrase) {
+ const encoder = new TextEncoder();
+ const data = encoder.encode(plainText);
+ const pwHash = await crypto.subtle.digest('SHA-256', encoder.encode(passphrase));
+
+ const key = await crypto.subtle.importKey(
+ 'raw', pwHash, {name: 'AES-CBC'}, false, ['encrypt']
+ );
+
+ const iv = crypto.getRandomValues(new Uint8Array(16));
+ const encrypted = await crypto.subtle.encrypt(
+ {name: 'AES-CBC', iv: iv},
+ key,
+ data
+ );
+
+ const combined = new Uint8Array(iv.length + encrypted.byteLength);
+ combined.set(iv);
+ combined.set(new Uint8Array(encrypted), iv.length);
+
+ return fixBase64Padding(btoa(String.fromCharCode(...combined)));
+}
+
+async function decryptString(base64Text, passphrase) {
+ const encoder = new TextEncoder();
+ const combined = new Uint8Array(atob(base64Text).split("").map(c => c.charCodeAt(0)));
+
+ const pwHash = await crypto.subtle.digest('SHA-256', encoder.encode(passphrase));
+ const key = await crypto.subtle.importKey(
+ 'raw', pwHash, {name: 'AES-CBC'}, false, ['decrypt']
+ );
+
+ const iv = combined.slice(0, 16);
+ const data = combined.slice(16);
+
+ const decrypted = await crypto.subtle.decrypt(
+ {name: 'AES-CBC', iv: iv},
+ key,
+ data
+ );
+
+ return new TextDecoder().decode(decrypted);
+}
+
+
+async function fetchPost(url, value) {
+ let response = await fetch(url, {
+ method: "POST",
+ credentials: "omit",
+ body: value,
+ headers: {
+ "secret": await encryptWithNonce(passwordHash, passwordHash, await getNonce(username, passwordHash))
+ }
+ });
+ let data = await response.text();
+ return data;
+}
+
+async function fetchPostEnc(url, value) {
+ let nonce = await getNonce(username, passwordHash);
+ let response = await fetch(url, {
+ method: "POST",
+ credentials: "omit",
+ body: await encryptWithNonce(value, passwordHash, nonce),
+ headers: {
+ "secret": await encryptWithNonce(passwordHash, passwordHash, nonce)
+ }
+ });
+ let data = await response.text();
+ return data;
+}
+
+async function fetchAsync(url) {
+ let response = await fetch(url, {
+ method: "GET",
+ credentials: "omit"
+ });
+
+ let data = await response.text();
+ return data;
+}
+
+async function fetchAsyncWAuth(url) {
+ let response = await fetch(url, {
+ method: "GET",
+ credentials: "omit",
+ headers: {
+ "secret": await encryptWithNonce(passwordHash, passwordHash, await getNonce(username, passwordHash))
+ }
+ });
+
+ let data = await response.text();
+ return data;
+}
+
+async function getServerInfo(host){
+ console.log(`${window.location.protocol}//${host}/_larpix/serverinfo`)
+ return JSON.parse(await fetchAsync(`${window.location.protocol}//${host}/_larpix/serverinfo`));
+}
+
+async function Auth(username, password) {
+
+
+ let passwordHash = await hashSHA3_512(password);
+ let response = await fetch(`${url}/auth?u=${username}`, {
+ method: "GET",
+ credentials: "omit",
+ headers: {
+ "secret": await encryptWithNonce(passwordHash, passwordHash, await getNonce(username, passwordHash))
+ }
+ });
+
+ let data = await response.text();
+ return data;
+
+}
+
+
+async function fetchEncrypted(request, body)
+{
+
+ let nonce = await getNonce(username, passwordHash);
+
+ let response = await fetch(`${url}/encryptedrequest?u=${username}`, {
+ method: "POST",
+ credentials: "omit",
+ body: await encryptWithNonce(
+
+ JSON.stringify({
+ string1: request,
+ string2: body
+ })
+
+ , passwordHash, nonce),
+ headers: {
+ "secret": await encryptWithNonce(passwordHash, passwordHash, nonce)
+ }
+ });
+ let data = await response.text();
+ return decryptString(data, passwordHash);
+}
+
+function power(base, exponent, mod) {
+ let res = 1n;
+ base = base % mod;
+ while (exponent > 0n) {
+ if (exponent % 2n === 1n) res = (res * base) % mod;
+ base = (base * base) % mod;
+ exponent = exponent / 2n;
+ }
+ return res;
+}
+
+function fixBase64Padding(base64String) {
+ let str = base64String.replace(/-/g, '+').replace(/_/g, '/');
+ const pad = str.length % 4;
+
+ if (pad) {
+ if (pad === 1) {
+ throw new Error("");
+ }
+ str += '='.repeat(4 - pad);
+ }
+
+ return str;
+}
+
+function showNotification(message, type = 'info', duration = 3500) {
+ let container = document.getElementById('notification-container');
+ if (!container) {
+ container = document.createElement('div');
+ container.id = 'notification-container';
+ document.body.appendChild(container);
+ }
+
+ const notif = document.createElement('div');
+ notif.className = `notification ${type}`;
+ notif.textContent = message;
+
+ container.appendChild(notif);
+
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ notif.classList.add('show');
+ });
+ });
+
+ setTimeout(() => {
+ notif.classList.remove('show');
+
+ notif.addEventListener('transitionend', () => {
+ notif.remove();
+ });
+ }, duration);
+}
+
+
+function showAction(message, actionid) {
+ let container = document.getElementById('notification-container');
+ if (!container) {
+ container = document.createElement('div');
+ container.id = 'notification-container';
+ document.body.appendChild(container);
+ }
+
+ const notif = document.createElement('div');
+ notif.className = `notification action`;
+ notif.textContent = message;
+ notif.id = `notification-${actionid}`;
+
+ container.appendChild(notif);
+
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ notif.classList.add('show');
+ });
+ });
+}
+function clearAction(actionid) {
+
+ let notif = document.getElementById(`notification-${actionid}`);
+ notif.classList.remove('show');
+
+ notif.addEventListener('transitionend', () => {
+ notif.remove();
+ });
+}
+
+
+async function hashSHA3_512(input) {
+ const encoder = new TextEncoder();
+ const data = encoder.encode(input);
+ const hashBuffer = await crypto.subtle.digest('SHA-512', data); //-3 kiedys xddddddddddddddddd
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
+ const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
+ return hashHex;
+}
+
+
+
+var password = "";
+var username = "";
+var passwordHash = "";
+var host = "";
+async function mainJS()
+{
+ username = localStorage.getItem('username');
+ password = localStorage.getItem('password');
+ host = localStorage.getItem('host');
+ passwordHash = await hashSHA3_512(password);
+ url = `${window.location.protocol}//${host}/_larpix`;
+}
+mainJS();
+
+collapseDmsBtn.addEventListener("click", () => {
+ collapseDmsBtn.classList.toggle("collapsed");
+});
+collapseGroupsBtn.addEventListener("click", () => {
+ collapseGroupsBtn.classList.toggle("collapsed");
+});
+
+addDmBtn.addEventListener("click", () => {
+ roomContentMain.innerHTML =
+ `
+
+
+
Add Chat
+
Add a private, encrypted chat by entering a username
+
+ `
+});
+addGroupBtn.addEventListener("click", () => {
+
+});
+
+
+
+sidebarHomeButton.addEventListener("mouseenter", () => {
+ sidebarHomeIndicator.classList.add("hover");
+});
+sidebarHomeButton.addEventListener("mouseleave", () => {
+ sidebarHomeIndicator.classList.remove("hover");
+});
+
+sidebarAddButton.addEventListener("mouseenter", () => {
+ sidebarAddIndicator.classList.add("hover");
+});
+sidebarAddButton.addEventListener("mouseleave", () => {
+ sidebarAddIndicator.classList.remove("hover");
+});
+
diff --git a/android/app/src/main/assets/public/style.css b/android/app/src/main/assets/public/style.css
new file mode 100644
index 00000000..15f1613f
--- /dev/null
+++ b/android/app/src/main/assets/public/style.css
@@ -0,0 +1,300 @@
+@font-face {
+ font-family: 'Nunito';
+ src: URL('Nunito-Regular.ttf') format('truetype');
+}
+:root {
+ --main-bg-color: rgb(20, 20, 20);
+ --text-color: rgb(240, 240, 245);
+ --icon-button-size: 2rem;
+ --press-scale: scale(0.92);
+ --light-border-color: rgba(255, 255, 255, 0.08);
+ --button-margin: 0.4rem;
+ --icon-button-height: 3.4rem;
+ --button-height: 2.4rem;
+ --border-width: 0.09rem;
+
+ --big-default: rgb(207 207 207);
+ --big-red: rgb(202 0 0);
+ --big-green: rgb(25 189 0);
+}
+.red {
+ color: var(--big-red);
+}
+.green {
+ color: var(--big-green);
+}
+.mininote {
+ font-size: 80%;
+ padding: 0 0.5rem;
+}
+.aqua {
+ color: rgb(0 139 200);
+}
+html {
+ font-size: max(16px, calc(100vw / 120));
+}
+body {
+ padding-top: env(safe-area-inset-top);
+ background-color: var(--main-bg-color);
+ display: flex;
+ height: 100dvh;
+ overflow: hidden;
+}
+* {
+ font-size: 1rem;
+ scrollbar-width: none;
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+ transition: all 0.2s ease;
+ color: var(--text-color);
+ font-family: "Nunito", sans-serif;
+ -webkit-tap-highlight-color: transparent;
+}
+
+button, input {
+ border-radius: 0.8rem;
+ background-color: rgba(255, 255, 255, 0.05);
+ margin: var(--button-margin);
+ padding: var(--button-margin);
+
+ height: var(--button-height);
+ display: flex;
+ cursor: pointer;
+ align-items: center;
+ justify-content: flex-start;
+ border: var(--border-width) solid var(--light-border-color);
+}
+input {
+ padding-left: calc(var(--button-margin) * 2);
+}
+
+
+indicator {
+ content: "";
+ position: absolute;
+ width: 0;
+ height: 0;
+ left: -0.08rem;
+ border: 0.15rem solid transparent;
+ border-radius: 0.8rem;
+ transform: translateY(calc( ( var(--icon-button-height) + (var(--button-margin) * 2) ) / 2 - 0.25rem));
+}
+indicator.notification {
+ content: "";
+ position: absolute;
+ width: 0;
+ height: 0.5rem;
+ left: -0.08rem;
+ border: 0.15rem solid var(--text-color);
+ border-radius: 0.8rem;
+ transform: translateY(calc( ( var(--icon-button-height) + (var(--button-margin) * 2) ) / 2 - 0.25rem));
+}
+indicator.hover {
+ content: "";
+ position: absolute;
+ width: 0;
+ height: 1.2rem;
+ left: -0.08rem;
+ border: 0.15rem solid var(--text-color);
+ border-radius: 0.8rem;
+ transform: translateY(calc( ( var(--icon-button-height) + (var(--button-margin) * 2) ) / 2 - 0.6rem));
+}
+indicator.active {
+ content: "";
+ position: absolute;
+ width: 0;
+ height: 2rem;
+ left: -0.08rem;
+ border: 0.15rem solid var(--text-color);
+ border-radius: 0.8rem;
+ transform: translateY(calc( ( var(--icon-button-height) + (var(--button-margin) * 2) ) / 2 - 1rem));
+}
+
+
+.icon-button {
+ height: var(--icon-button-height);
+ width: var(--icon-button-height);
+ justify-content: center;
+}
+.icon-button svg {
+ width: var(--icon-button-size);
+ height: var(--icon-button-size);
+}
+button:hover, input:hover {
+ background-color: rgba(255, 255, 255, 0.1);
+}
+button:active, input:active {
+ transform: var(--press-scale);
+}
+roomcontent {
+ display: flex;
+ flex-direction: column;
+
+ height: 100dvh;
+ width: calc(100vw - (var(--icon-button-height) + (var(--button-margin) * 2) + var(--border-width)) - 17rem);
+ border-right: var(--border-width) solid var(--light-border-color);
+}
+roomcontent2 {
+ display: flex;
+ flex-direction: column;
+
+ height: calc(100dvh - (var(--button-height) * 1.5));
+ width: 100%;
+}
+
+
+sidebar {
+ display: flex;
+ flex-direction: column;
+
+ height: 100dvh;
+ width: calc(var(--icon-button-height) + (var(--button-margin) * 2) + var(--border-width));
+ border-right: var(--border-width) solid var(--light-border-color);
+}
+sidebar.second {
+ width: 17rem;
+ min-width: 17rem;
+ max-width: 17rem;
+}
+.sidebar-section-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.collapse-text-button {
+ padding-left: calc(var(--button-margin) + 0.2rem);
+ flex-shrink: 0;
+ flex-grow: 1;
+ border: none;
+ background-color: transparent;
+ height: 2rem;
+ font-weight: 600;
+ gap: 0.3rem;
+}
+
+.collapse-text-button .chevron {
+ width: 1.25rem;
+ height: 1.25rem;
+}
+
+.collapse-text-button.collapsed .chevron {
+ transform: rotate(-90deg);
+}
+
+.add-action-button {
+ border: none;
+ background-color: transparent;
+ height: 2rem;
+ width: 2rem;
+ padding: 0;
+ flex-shrink: 0;
+ justify-content: center;
+ color: rgba(255, 255, 255, 0.5);
+}
+
+.add-action-button:hover {
+ background-color: rgba(255, 255, 255, 0.1);
+ color: var(--text-color);
+}
+
+.add-action-button svg {
+ width: 1.25rem;
+ height: 1.25rem;
+}
+hr {
+ border: none;
+ height: 0;
+ background-color: transparent;
+ border-bottom: var(--border-width) solid var(--light-border-color);
+ width: 60%;
+ margin-left: 20%;
+}
+
+
+#notification-container {
+ padding-top: env(safe-area-inset-top);
+ position: fixed;
+ top: 1.5rem;
+ left: 50%;
+ transform: translateX(-50%);
+ z-index: 1500;
+ display: flex;
+ flex-direction: column;
+ gap: 0.8rem;
+ pointer-events: none;
+ align-items: center;
+}
+
+.notification {
+ background: rgba(255, 255, 255, 0.015);
+ backdrop-filter: blur(1.2rem);
+ -webkit-backdrop-filter: blur(1.2rem);
+
+ border: var(--border-width) solid var(--light-border-color);
+ border-radius: 0.9rem;
+
+ padding: 0.8rem 1.3rem;
+ color: var(--text-color);
+ font-size: 1rem;
+ font-weight: 500;
+ text-align: center;
+
+ box-shadow: 0 0.5rem 2rem rgba(0, 0, 0, 0.4);
+
+ opacity: 0;
+ transform: translateY(-1rem);
+ transition:
+ opacity 0.3s ease,
+ transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275),
+ background-color 0.2s ease;
+}
+
+.notification.show {
+ opacity: 1;
+ transform: translateY(0);
+}
+
+.notification.info {
+ border-color: var(--big-default);
+}
+
+.notification.error {
+ border-color: var(--big-red);
+}
+
+.notification.success {
+ border-color: var(--big-green);
+}
+
+roomtopbar {
+ height: calc(var(--button-height) * 1.5);
+
+ display: flex;
+ flex-direction: row;
+
+ width: 100%;
+ border-bottom: var(--border-width) solid var(--light-border-color);
+
+ line-height: calc(var(--button-height) * 1.5);
+ font-weight: bold;
+ padding-left: calc(var(--button-margin) * 3);
+ font-size: 1.5rem;
+}
+sidebar.second#roomdetailsbar {
+ height: calc(var(--button-height) * 1.5);
+
+ display: flex;
+ flex-direction: row;
+
+ border-bottom: var(--border-width) solid var(--light-border-color);
+}
+fullcontainer {
+ width: 100%;
+ height: 100%;
+}
+herotitle {
+ font-weight: bold;
+ font-size: 1.6rem;
+}
\ No newline at end of file
diff --git a/android/app/src/main/res/xml/config.xml b/android/app/src/main/res/xml/config.xml
new file mode 100644
index 00000000..1b1b0e0d
--- /dev/null
+++ b/android/app/src/main/res/xml/config.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file