diff --git a/resources/icons/alert.svg b/resources/icons/alert.svg
new file mode 100644
index 0000000..babed01
--- /dev/null
+++ b/resources/icons/alert.svg
@@ -0,0 +1,12 @@
+
\ No newline at end of file
diff --git a/resources/index.html b/resources/index.html
index ea5ffcd..906230d 100644
--- a/resources/index.html
+++ b/resources/index.html
@@ -11,6 +11,7 @@
+
@@ -66,6 +67,12 @@
Launch without Authentication
+
+
+
+
+
+
@@ -98,6 +105,12 @@
+
+
+
+
+
+
diff --git a/resources/js/authAlert.js b/resources/js/authAlert.js
new file mode 100644
index 0000000..9b3f462
--- /dev/null
+++ b/resources/js/authAlert.js
@@ -0,0 +1,50 @@
+let alertTimeout, alertCooldown = 3000
+
+async function displayLoginAlert(message, type) {
+ const elm = document.getElementById('loginAlert');
+ const text = document.getElementById('loginAlertText');
+
+ elm.style.removeProperty('display');
+
+ switch(type) {
+ case 'error':
+ elm.classList.add('error');
+ break;
+
+ case 'warn':
+ default:
+ elm.classList.add('warn');
+ break;
+ }
+
+ text.innerText = message;
+
+ // Disappear after 5 seconds
+ alertTimeout = setTimeout(() => {
+ elm.style.display = 'none';
+ }, alertCooldown)
+}
+
+async function displayRegisterAlert(message, type) {
+ const elm = document.getElementById('registerAlert');
+
+ elm.style.removeProperty('display');
+
+ switch(type) {
+ case 'error':
+ elm.classList.add('error');
+ break;
+
+ case 'warn':
+ default:
+ elm.classList.add('warn');
+ break;
+ }
+
+ text.innerText = message;
+
+ // Disappear after 5 seconds
+ alertTimeout = setTimeout(() => {
+ elm.style.display = 'none';
+ }, alertCooldown)
+}
\ No newline at end of file
diff --git a/resources/js/login.js b/resources/js/login.js
index 224c507..1b13d1b 100644
--- a/resources/js/login.js
+++ b/resources/js/login.js
@@ -63,13 +63,12 @@ async function login() {
}
const { data } = await axios.post(url + '/grasscutter/login', reqBody)
- const tkData = parseJwt(data.jwt)
console.log(data)
switch(data.message) {
case 'INVALID_ACCOUNT':
- // Username or password invalid
+ displayLoginAlert('Invalid username or password', 'error');
break;
case 'NO_PASSWORD':
@@ -86,6 +85,7 @@ async function login() {
default:
// Success! Copy the JWT token to their clipboard
+ const tkData = parseJwt(data.jwt)
await Neutralino.clipboard.writeText(tkData.token)
break;
}
diff --git a/resources/style/index.css b/resources/style/index.css
index c330a35..a164105 100644
--- a/resources/style/index.css
+++ b/resources/style/index.css
@@ -117,6 +117,36 @@ body {
margin-right: 6px;
}
+.error {
+ background: #e90000;
+}
+
+#registerAlert .warn,
+#loginAlert .warn {
+
+}
+
+#registerAlert,
+#loginAlert {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ border-radius: 5px;
+ color: #fff;
+}
+
+#registerAlert img,
+#loginAlert img {
+ height: 20px;
+ margin: 10px;
+ filter: invert(100%) sepia(0%) saturate(179%) hue-rotate(253deg) brightness(105%) contrast(101%);
+}
+
+#registerAlert span,
+#loginAlert span {
+ margin: 10px;
+}
+
#firstTimeNotice,
#loginPanel,
#settingsPanel {