diff --git a/Website/apiConnector.js b/Website/apiConnector.js
new file mode 100644
index 0000000..0a94b9e
--- /dev/null
+++ b/Website/apiConnector.js
@@ -0,0 +1,95 @@
+const apiUri = "http://localhost:5177";
+
+async function GetData(uri){
+ let request = await fetch(uri, {
+ method: 'GET',
+ headers: {
+ 'Accept': 'application/json'
+ }
+ });
+ let json = await request.json();
+ return json;
+}
+
+function PostData(uri){
+ fetch(uri, {
+ method: 'POST'
+ });
+}
+
+function DeleteData(uri){
+ fetch(uri, {
+ method: 'DELETE'
+ });
+}
+
+async function GetAvailableControllers(){
+ var uri = apiUri + "/Tranga/GetAvailableControllers";
+ let json = await GetData(uri);
+ return json;
+}
+
+async function GetPublication(connectorName, title){
+ var uri = apiUri + `/Tranga/GetPublicationsFromConnector?connectorName=${connectorName}&title=${title}`;
+ let json = await GetData(uri);
+ return json;
+}
+
+async function GetKnownPublications(){
+ var uri = apiUri + "/Tranga/GetKnownPublications";
+ let json = await GetData(uri);
+ return json;
+}
+
+async function GetTaskTypes(){
+ var uri = apiUri + "/Tasks/GetTaskTypes";
+ let json = await GetData(uri);
+ return json;
+}
+async function GetRunningTasks(){
+ var uri = apiUri + "/Tranga/GetRunningTasks";
+ let json = await GetData(uri);
+ return json;
+}
+
+async function GetTasks(){
+ var uri = apiUri + "/Tasks/GetList";
+ let json = await GetData(uri);
+ return json;
+}
+
+async function GetSettings(){
+ var uri = apiUri + "/Settings/Get";
+ let json = await GetData(uri);
+ return json;
+}
+
+function CreateTask(taskType, reoccurrence, connectorName, publicationId, language){
+ var uri = apiUri + `/Tasks/Create?taskType=${taskType}&connectorName=${connectorName}&publicationId=${publicationId}&reoccurrenceTime=${reoccurrence}&language=${language}`;
+ PostData(uri);
+}
+
+function StartTask(taskType, connectorName, publicationId){
+ var uri = apiUri + `/Tasks/Start?taskType=${taskType}&connectorName=${connectorName}&publicationId=${publicationId}`;
+ PostData(uri);
+}
+
+function EnqueueTask(taskType, connectorName, publicationId){
+ var uri = apiUri + `/Queue/Enqueue?taskType=${taskType}&connectorName=${connectorName}&publicationId=${publicationId}`;
+ PostData(uri);
+}
+
+function UpdateSettings(downloadLocation, komgaUrl, komgaAuth){
+ var uri = apiUri + `/Settings/Update?downloadLocation=${downloadLocation}&komgaUrl=${komgaAuth}&komgaAuth=${komgaAuth}`;
+ PostData(uri);
+}
+
+function DeleteTask(taskType, connectorName, publicationId){
+ var uri = apiUri + `/Tasks/Delete?taskType=${taskType}&connectorName=${connectorName}&publicationId=${publicationId}`;
+ DeleteData(uri);
+}
+
+function DequeueTask(taskType, connectorName, publicationId){
+ var uri = apiUri + `/Queue/Dequeue?taskType=${taskType}&connectorName=${connectorName}&publicationId=${publicationId}`;
+ DeleteData(uri);
+}
\ No newline at end of file
diff --git a/Website/index.html b/Website/index.html
new file mode 100644
index 0000000..44b8c62
--- /dev/null
+++ b/Website/index.html
@@ -0,0 +1,88 @@
+
+
+
+
+ Tranga
+
+
+
+
+
+
+ Tranga
+
+
+
+
+
+
+
+
+
+
+
+
+ Made with Blåhaj 🦈
+
+
+
+
+
+
+ MangaDex
+ Tensei Pandemic
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Website/interaction.js b/Website/interaction.js
new file mode 100644
index 0000000..3d93d40
--- /dev/null
+++ b/Website/interaction.js
@@ -0,0 +1,230 @@
+const slideInRight = [
+ { right: "-20rem" },
+ { right: "0" }
+];
+
+const slideInRightTiming = {
+ duration: 200,
+ iterations: 1,
+ fill: "forwards",
+ easing: "ease-out"
+}
+
+const slideOutRightTiming = {
+ direction: "reverse",
+ duration: 200,
+ iterations: 1,
+ fill: "forwards",
+ easing: "ease-in"
+}
+
+let publications = [];
+let tasks = [];
+let toEditId;
+
+const taskTypesSelect = document.querySelector("#taskTypes")
+const searchPublicationQuery = document.querySelector("#searchPublicationQuery");
+const selectPublication = document.querySelector("#taskSelectOutput");
+const connectorSelect = document.querySelector("#connectors");
+const settingsTab = document.querySelector("#settingstab");
+const settingsCog = document.querySelector("#settingscog");
+const selectRecurrence = document.querySelector("#selectReccurrence");
+const tasksContent = document.querySelector("content");
+const addTaskPopup = document.querySelector("#addTaskPopup");
+const publicationViewerPopup = document.querySelector("#publicationViewerPopup");
+const publicationViewerWindow = document.querySelector("publication-viewer");
+const publicationViewerDescription = document.querySelector("#publicationViewerDescription");
+const publicationViewerName = document.querySelector("#publicationViewerName");
+const publicationViewerAuthor = document.querySelector("#publicationViewerAuthor");
+const pubviewcover = document.querySelector("#pubviewcover");
+const publicationDelete = document.querySelector("publication-delete");
+const publicationAdd = document.querySelector("publication-add");
+const closetaskpopup = document.querySelector("#closePopupImg");
+
+settingsCog.addEventListener("click", () => slide());
+closetaskpopup.addEventListener("click", () => HideAddTaskPopup());
+document.querySelector("#blurBackgroundTaskPopup").addEventListener("click", () => HideAddTaskPopup());
+document.querySelector("#blurBackgroundPublicationPopup").addEventListener("click", () => HidePublicationPopup());
+publicationDelete.addEventListener("click", () => DeleteTaskClick());
+publicationAdd.addEventListener("click", () => AddTaskClick());
+
+/*
+let availableTaskTypes;
+GetTaskTypes()
+ .then(json => availableTaskTypes = json)
+ .then(json =>
+ json.forEach(taskType => {
+ var option = document.createElement('option');
+ option.value = taskType;
+ option.innerText = taskType;
+ taskTypesSelect.appendChild(option);
+ }));*/
+
+let availableConnectors;
+GetAvailableControllers()
+ .then(json => availableConnectors = json)
+ //.then(json => console.log(json))
+ .then(json =>
+ json.forEach(connector => {
+ var option = document.createElement('option');
+ option.value = connector;
+ option.innerText = connector;
+ connectorSelect.appendChild(option);
+ })
+ );
+
+searchPublicationQuery.addEventListener("keypress", (event) => {
+ if(event.key === "Enter"){
+ selectRecurrence.disabled = true;
+ connectorSelect.disabled = true;
+ searchPublicationQuery.disabled = true;
+
+ selectPublication.replaceChildren();
+ GetPublication(connectorSelect.value, searchPublicationQuery.value)
+ //.then(json => console.log(json));
+ .then(json =>
+ json.forEach(publication => {
+ var option = CreatePublication(publication, connectorSelect.value);
+ option.addEventListener("click", (mouseEvent) => {
+ ShowPublicationViewerWindow(publication.internalId, mouseEvent, true);
+ });
+ selectPublication.appendChild(option);
+ }
+ ))
+ .then(() => {
+ selectRecurrence.disabled = false;
+ connectorSelect.disabled = false;
+ searchPublicationQuery.disabled = false;
+ });
+ }
+});
+
+function CreatePublication(publication, connector){
+ var publicationElement = document.createElement('publication');
+ publicationElement.setAttribute("id", publication.internalId);
+ var img = document.createElement('img');
+ img.src = publication.posterUrl;
+ publicationElement.appendChild(img);
+ var info = document.createElement('publication-information');
+ var connectorName = document.createElement('connector-name');
+ connectorName.innerText = connector;
+ connectorName.className = "pill";
+ info.appendChild(connectorName);
+ var publicationName = document.createElement('publication-name');
+ publicationName.innerText = publication.sortName;
+ info.appendChild(publicationName);
+ publicationElement.appendChild(info);
+ if(publications.filter(pub => pub.internalId === publication.internalId) < 1)
+ publications.push(publication);
+ return publicationElement;
+}
+
+function DeleteTaskClick(){
+ taskToDelete = tasks.filter(tTask => tTask.publication.internalId === toEditId)[0];
+ DeleteTask("DownloadNewChapters", taskToDelete.connectorName, toEditId);
+ HidePublicationPopup();
+}
+
+function AddTaskClick(){
+ CreateTask("DownloadNewChapters", selectRecurrence.value, connectorSelect.value, toEditId, "en")
+ HideAddTaskPopup();
+ HidePublicationPopup();
+}
+
+var slideIn = true;
+function slide() {
+ if (slideIn)
+ settingsTab.animate(slideInRight, slideInRightTiming);
+ else
+ settingsTab.animate(slideInRight, slideOutRightTiming);
+ slideIn = !slideIn;
+}
+
+function ResetContent(){
+ tasksContent.replaceChildren();
+ var add = document.createElement("div");
+ add.setAttribute("id", "addPublication")
+ var plus = document.createElement("p");
+ plus.innerText = "+";
+ add.appendChild(plus);
+ add.addEventListener("click", () => ShowNewTaskWindow());
+ tasksContent.appendChild(add);
+}
+function ShowPublicationViewerWindow(publicationId, event, add){
+ publicationViewerWindow.style.top = `${event.clientY - 60}px`;
+ publicationViewerWindow.style.left = `${event.clientX}px`;
+ var publication = publications.filter(pub => pub.internalId === publicationId)[0];
+
+ publicationViewerName.innerText = publication.sortName;
+ publicationViewerDescription.innerText = publication.description;
+ publicationViewerAuthor.innerText = publication.author;
+ pubviewcover.src = publication.posterUrl;
+ toEditId = publicationId;
+
+ if(add){
+ publicationAdd.style.display = "block";
+ publicationDelete.style.display = "none";
+ }
+ else{
+ publicationAdd.style.display = "none";
+ publicationDelete.style.display = "block";
+ }
+
+ toEditId = publicationId;
+ publicationViewerPopup.style.display = "block";
+}
+
+function ShowNewTaskWindow(){
+ selectPublication.replaceChildren();
+ addTaskPopup.style.display = "block";
+}
+function HideAddTaskPopup(){
+ addTaskPopup.style.display = "none";
+}
+
+function HidePublicationPopup(){
+ publicationViewerPopup.style.display = "none";
+}
+
+const fadeIn = [
+ { opacity: "0" },
+ { opacity: "1" }
+];
+
+const fadeInTiming = {
+ duration: 50,
+ iterations: 1,
+ fill: "forwards"
+}
+
+ResetContent();
+GetTasks()
+ //.then(json => console.log(json))
+ .then(json => json.forEach(task => {
+ var publication = CreatePublication(task.publication, task.connectorName);
+ publication.addEventListener("click", (event) => ShowPublicationViewerWindow(task.publication.internalId, event, false));
+ tasksContent.appendChild(publication);
+ tasks.push(task);
+ }));
+
+setInterval(() => {
+ var cTasks = [];
+ GetTasks()
+ //.then(json => console.log(json))
+ .then(json => json.forEach(task => cTasks.push(task)))
+ .then(() => {
+ if(tasks.length != cTasks.length) {
+ ResetContent();
+ cTasks.forEach(task => {
+ var publication = CreatePublication(task.publication, task.connectorName);
+ publication.addEventListener("click", (event) => ShowPublicationViewerWindow(task.publication.internalId, event, false));
+ tasksContent.appendChild(publication);
+ })
+
+ tasks = cTasks;
+ }
+ }
+ );
+
+
+}, 1000);
\ No newline at end of file
diff --git a/Website/media/blahaj.png b/Website/media/blahaj.png
new file mode 100644
index 0000000..dbbff93
Binary files /dev/null and b/Website/media/blahaj.png differ
diff --git a/Website/media/close-x.svg b/Website/media/close-x.svg
new file mode 100644
index 0000000..fc8cc4d
--- /dev/null
+++ b/Website/media/close-x.svg
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/Website/media/settings-cogwheel.svg b/Website/media/settings-cogwheel.svg
new file mode 100644
index 0000000..7e61388
--- /dev/null
+++ b/Website/media/settings-cogwheel.svg
@@ -0,0 +1,21 @@
+
+
+
+
\ No newline at end of file
diff --git a/Website/style.css b/Website/style.css
new file mode 100644
index 0000000..e9220bf
--- /dev/null
+++ b/Website/style.css
@@ -0,0 +1,391 @@
+:root{
+ --background-color: #eee;
+ --second-background-color: #fff;
+ --primary-color: #f5a9b8;
+ --secondary-color: #5bcefa;
+ --accent-color: #fff;
+ --topbar-height: 60px;
+ box-sizing: border-box;
+}
+
+body{
+ padding: 0;
+ margin: 0;
+ display: flex;
+ flex-flow: column;
+ flex-wrap: nowrap;
+ height: 100vh;
+ background-color: var(--background-color);
+ font-family: "Inter", sans-serif;
+ overflow-x: hidden;
+}
+
+background-placeholder{
+ background-color: var(--second-background-color);
+ opacity: 1;
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ border-radius: 0 0 5px 0;
+ z-index: -1;
+}
+
+topbar {
+ display: flex;
+ align-items: center;
+ height: var(--topbar-height);
+ background-color: var(--secondary-color);
+}
+
+titlebox {
+ position: relative;
+ display: flex;
+ margin: 0 0 0 40px;
+ height: 100%;
+ align-items:center;
+ justify-content:center;
+}
+
+titlebox span{
+ font-size: 24pt;
+ font-weight: bold;
+ background: linear-gradient(150deg, var(--primary-color), var(--accent-color));
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ margin-left: 20px;
+}
+
+titlebox img {
+ height: 100%;
+ margin-right: 10px;
+}
+
+spacer{
+ flex-grow: 1;
+}
+
+searchdiv{
+ display: block;
+ margin: 0 10px 0 0;
+}
+
+#searchbox {
+ padding: 6px 8px;
+ border: 0;
+ border-radius: 3px;
+ font-size: 14pt;
+ width: 250px;
+}
+
+#settingscog {
+ cursor: pointer;
+ margin: 0px 30px;
+ height: calc(100% - 40px);
+ filter: invert(100%) sepia(0%) saturate(7465%) hue-rotate(115deg) brightness(116%) contrast(101%);
+}
+
+viewport {
+ position: relative;
+ display: flex;
+ flex-flow: row;
+ flex-wrap: nowrap;
+ flex-grow: 1;
+}
+
+sidebar{
+ position: relative;
+ width: 20rem;
+ margin-bottom: 20px;
+ border-radius: 0 0 5px 0;
+ display: flex;
+ flex-direction: column;
+
+}
+
+content {
+ position: relative;
+ flex-grow: 1;
+ margin: 0 10px 10px 10px;
+ border-radius: 5px;
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ justify-content: start;
+ align-content: start;
+}
+
+settingstab{
+ position: absolute;
+ right: -20rem;
+ bottom: 0;
+ background-color: rgba(0,0,0,0.5);
+ width: 20rem;
+ height: calc(100% - var(--topbar-height) - 40px);
+ margin-bottom: 10px;
+ border-radius: 5px 0 0 5px;
+}
+
+#addPublication {
+ cursor: pointer;
+ background-color: var(--secondary-color);
+ width: 180px;
+ height: 300px;
+ border-radius: 5px;
+ margin: 10px 10px;
+ padding: 15px 20px;
+ position: relative;
+}
+
+#addPublication p{
+ width: 100%;
+ text-align: center;
+ font-size: 150pt;
+ vertical-align: middle;
+ line-height: 300px;
+ margin: 0;
+ color: var(--accent-color);
+}
+
+.pill {
+ flex-grow: 0;
+ height: 14pt;
+ font-size: 12pt;
+ border-radius: 9pt;
+ background-color: var(--primary-color);
+ padding: 2pt 20px;
+}
+
+publication{
+ cursor: pointer;
+ background-color: var(--secondary-color);
+ width: 180px;
+ height: 300px;
+ border-radius: 5px;
+ margin: 10px 10px;
+ padding: 15px 20px;
+ position: relative;
+}
+
+publication::after{
+ content: '';
+ position: absolute;
+ left: 0; top: 0;
+ border-radius: 5px;
+ width: 100%; height: 100%;
+ background: linear-gradient(rgba(0, 0, 0, 0.5),rgba(0, 0, 0, 0.1));
+}
+
+publication-information {
+ display: flex;
+ flex-direction: column;
+ justify-content: start;
+}
+
+publication-information * {
+ z-index: 1;
+ color: var(--accent-color);
+}
+
+connector-name{
+ width: fit-content;
+ margin: 10px 0;
+}
+
+publication-name{
+ width: fit-content;
+ font-size: 16pt;
+ font-weight: bold;
+}
+
+publication img {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 0;
+}
+
+popup{
+ display: none;
+ width: 100%;
+ min-height: 100%;
+ top: 0;
+ left: 0;
+ position: absolute;
+ z-index: 2;
+}
+
+blur-background {
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ left: 0;
+ background-color: black;
+ opacity: 0.5;
+}
+
+addtask-window {
+ display: flex;
+ flex-direction: column;
+ flex-wrap: nowrap;
+ position: absolute;
+ left: 12.5%;
+ top: 15%;
+ width: 75%;
+ min-height: 70%;
+ max-height: 80%;
+ padding: 0;
+ background-color: var(--accent-color);
+ z-index: 5;
+ border-radius: 5px;
+}
+
+window-titlebar {
+ width: 100%;
+ height: 60px;
+ background-color: var(--primary-color);
+ border-radius: 5px 5px 0 0;
+ color: var(--accent-color);
+ display: flex block;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+}
+
+window-titlebar p {
+ margin: 0 30px;
+ font-size: 14pt;
+ font-weight: bolder;
+ letter-spacing: 1px;
+}
+
+window-titlebar #closePopupImg {
+ height: 70%;
+ cursor: pointer;
+ margin-right: 20px;
+ filter: invert(100%) sepia(0%) saturate(100%) hue-rotate(115deg) brightness(116%) contrast(101%);
+}
+
+window-content {
+ display: flex;
+ flex-direction: column;
+ padding: 20px 5%;
+ overflow-x: scroll;
+}
+
+addtask-settings{
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+addtask-settings select, addtask-settings input{
+ padding: 5px;
+ font-size: 10pt;
+ border: 1px solid rgba(0,0,0,0.2);
+ border-radius: 3px;
+ background-color: transparent;
+ margin: 10px 0;
+ width: 150px;
+}
+
+addtask-settings label {
+ font-weight: bolder;
+ margin: 0 5px;
+}
+
+addtask-settings addtask-setting{
+ margin: 0 15px;
+}
+
+#taskSelectOutput{
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ justify-content: start;
+ align-content: start;
+}
+
+#publicationViewerPopup{
+ z-index: 6;
+}
+
+publication-viewer{
+ display: block;
+ width: 500px;
+ height: 300px;
+ position: absolute;
+ top: 200px;
+ left: 400px;
+ background-color: var(--accent-color);
+ border-radius: 5px;
+ overflow: hidden;
+}
+
+publication-viewer{
+ padding: 30px;
+}
+
+publication-viewer::after{
+ content: '';
+ position: absolute;
+ left: 0; top: 0;
+ border-radius: 5px;
+ width: 100%; height: 100%;
+ background: rgba(0,0,0,0.8);
+ backdrop-filter: blur(3px);
+}
+
+publication-viewer img {
+ position: absolute;
+ left: 0;
+ top: 0;
+ min-height: 100%;
+ max-width: 100%;
+ border-radius: 5px;
+ z-index: 0;
+}
+
+publication-viewer publication-information publication-name{
+ margin: 5px 0;
+}
+
+publication-viewer publication-information publication-author {
+ margin: 5px 0;
+}
+
+publication-viewer publication-information publication-author::before {
+ content: "Author: ";
+}
+
+publication-viewer publication-information publication-description::before {
+ content: "Description";
+ display: block;
+ font-weight: bolder;
+}
+
+publication-viewer publication-information publication-description {
+ font-size: 12pt;
+ margin: 5px 0;
+ max-height: 200px;
+ overflow-x: scroll;
+}
+
+publication-viewer publication-information publication-delete {
+ position: absolute;
+ bottom: 0px;
+ right: 0px;
+ color: red;
+ margin: 20px;
+ font-size: 16pt;
+}
+
+publication-viewer publication-information publication-add {
+ position: absolute;
+ bottom: 0px;
+ right: 0px;
+ color: limegreen;
+ margin: 20px;
+ font-size: 16pt;
+}
\ No newline at end of file