Skip to content

Commit 8071952

Browse files
Saving and loading maps (#7)
* Created a FileManager class to include file access * Created a HTTP-client to handle basic save/load requests * Reversed the default options dictionary to be assigned if passed one is null or undefined * Added ListMap() for retrieving available maps and updated docstrings of HTTPClient * Extended performFetch() to differ between GET and PUT requests as well as error handling to differ between connection and response failures * Provided some minor comments to functions' parameters and URLs * Removed FileManager class since it's functionality has been fully taken by HTTPClient class * added shortcuts * included a NULL_MAP as default return value for PUT and failed requests * simplified default paths in save/loadMap() * integrated http client into GUI * fixed http client class export * added comments and a POST function to create a file through the server * add modal with placeholders * add dialog with libraries to choose from --------- Co-authored-by: iloveskittles <40355179+iloveskittles82@users.noreply.github.com>
1 parent 9128a65 commit 8071952

File tree

3 files changed

+187
-5
lines changed

3 files changed

+187
-5
lines changed

http/HTTPClient.js

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/**
2+
* Provides HTTP-connection functionality.
3+
*/
4+
export class HTTPClient {
5+
#host = "http://localhost:6050/";
6+
7+
constructor() {
8+
this.NULL_MAP = { map: {} };
9+
}
10+
11+
/**
12+
* Sends a HTTP-request to the JabRef's server.
13+
* @param { string } url - The server's URL to make a request to.
14+
* @param { any } options - Optional request's options.
15+
* @returns { object }
16+
* - An **object** in case of a `GET request` or
17+
* - A **object** { map: {} } in case of a `PUT request`
18+
* if any request failed.
19+
*/
20+
async #performFetch(url, options = null) {
21+
let fetchResult = this.NULL_MAP;
22+
let logMessage = "";
23+
24+
const requestUrl = this.#host.concat(url);
25+
// Setting default options if none's been provided
26+
// * Note: this includes only cases when options are null or undefined
27+
options = options ??
28+
{
29+
method: "PUT",
30+
headers: { "Content-Type": "application/json" }
31+
};
32+
33+
// Sending the request and waiting for the response
34+
try {
35+
const response = await fetch(requestUrl, options);
36+
if (!response.ok) {
37+
throw new Error("Request's result is not ok ( -.-)");
38+
}
39+
40+
// Defining default resulting output
41+
fetchResult = "No data received back.";
42+
// If some output is awaited, save it instead
43+
if (options.method !== "PUT") {
44+
fetchResult = await response.json();
45+
}
46+
47+
// Providing infos about the request
48+
logMessage =
49+
`${options.method} ${url} Request succeeded (~ UwU)~(${response.status}).\n` +
50+
`Output:\n` +
51+
`${JSON.stringify(fetchResult, null, 2)}`;
52+
} catch (e) {
53+
// Logging basic information about the error
54+
console.error(e);
55+
logMessage = `${options.method} ${url} Request failed (.'T_T)`;
56+
57+
// If connection was present, provide more details
58+
if (typeof(response) !== "undefined") {
59+
logMessage +=
60+
`-(${response.status}).\n` +
61+
`Options:\n` +
62+
`${options}`;
63+
}
64+
}
65+
66+
// Finally showing the resulting log
67+
console.log(logMessage);
68+
69+
return fetchResult;
70+
}
71+
72+
/**
73+
* Requests a mind map (.jmp file) from JabRef's server.
74+
* @param { string } path - The path to the requested map.
75+
* @returns { object } - The requested mind map object.
76+
*/
77+
async loadMap(path = "libraries/demo/map") {
78+
// The path will probably be included into url definition (coming in sprint 2)
79+
const url = path;
80+
const options = {
81+
method: "GET",
82+
headers: { "Content-Type": "application/json" }
83+
}
84+
85+
return this.#performFetch(url, options);
86+
}
87+
88+
/**
89+
* Sends a mind map to JabRef's server to save.
90+
* @param { map } mindMap - The mind map to save.
91+
* @param { string } path - The path to save mind map into.
92+
* @returns { string } - A string of the request's result.
93+
*/
94+
async saveMap(mindMap, path = "libraries/demo/map") {
95+
// The url will provably be modified according to mindMap's properties (name, id, whatsoever)
96+
// (coming in sprint 2)
97+
const url = path;
98+
const options = {
99+
method: "PUT",
100+
headers: { "Content-Type": "application/json" },
101+
body: JSON.stringify({ map: mindMap })
102+
}
103+
104+
return this.#performFetch(url, options)
105+
}
106+
107+
async saveNewMap(mindMap, path = "libraries/demo/map") {
108+
const url = path;
109+
const options = {
110+
method: "POST",
111+
headers: {"Content-Type": "application/json"},
112+
body: JSON.stringify({map: mindMap})
113+
}
114+
115+
return this.#performFetch(url, options)
116+
}
117+
118+
/**
119+
* Requests a list of stored mind maps saved on the server.
120+
* @returns { object } - A list of available mind maps stored on the server.
121+
*/
122+
async listMaps() {
123+
const url = "libraries";
124+
const options = {
125+
method: "GET",
126+
headers: { "Content-Type": "application/json" }
127+
}
128+
129+
return this.#performFetch(url, options)
130+
}
131+
}

index.html

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
<button id = "saveBtn" class="btn btn-light me-2" type="button" data-bs-toggle="tooltip" title="Save">
3939
<i class="bi bi-save"></i> Save
4040
</button>
41-
<button id = "openBtn" class="btn btn-light me-2" type="button" data-bs-toggle="tooltip" title="Open">
41+
<button id = "openBtn" class="btn btn-light me-2" type="button" data-bs-toggle="modal" title="Open" data-bs-target="#selectMindmapModal">
4242
<i class="bi bi-folder2-open"></i> Open
4343
</button>
4444
</div>
@@ -69,7 +69,29 @@
6969
</div>
7070
</div>
7171
</nav>
72+
73+
<!--mindmap environment-->
7274
<div id="jsmind_container" style="width:100%;height:100vh;background:#f4f4f4;"></div>
75+
76+
<!-- Modal for the "open" dialog-->
77+
<div class="modal fade" id="selectMindmapModal" tabindex="-1" role="dialog">
78+
<div class="modal-dialog" role="document">
79+
<div class="modal-content">
80+
<div class="modal-header">
81+
<h5 class="modal-title" id="exampleModalLabel">Choose a JabMap to open</h5>
82+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
83+
</div>
84+
<div class="modal-body">
85+
<select class="form-select" id="openMindmapSelect" size="10">
86+
</select>
87+
</div>
88+
<div class="modal-footer">
89+
<button type="button" id = "openSelectedMapBtn" class="btn btn-outline-secondary" data-bs-dismiss="modal">Open</button>
90+
</div>
91+
</div>
92+
</div>
93+
</div>
94+
7395
<script type="module" src="/src/main.js"></script>
7496
</body>
7597
</html>

src/main.js

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import 'bootstrap-icons/font/bootstrap-icons.css';
44
import jsMind from './jsmind/src/jsmind.js';
55
import './jsmind/src/plugins/jsmind.draggable-node.js';
66
import "./ActionStack.js";
7+
import { HTTPClient } from "../http/HTTPClient";
8+
9+
'../http/HTTPClient.js';
710

811
// "load" mindmap data
912
const mind = {
@@ -98,17 +101,43 @@ const jm = new jsMind(options);
98101
jm.show(mind);
99102
// add initial state to action stack
100103
jm.actionStack.add(mind);
104+
// create a HTTP client instance
105+
let httpClient = new HTTPClient();
101106

102107
//--- Button click handlers ---
103108

104109
// saving
105110
saveBtn.onclick = function(){
106-
// saving jabmaps magic here..
111+
// sends mindmap content to JabRef's http server to save
112+
httpClient.saveMap(jm.get_data());
113+
}
114+
115+
// open - opens a dialog to select available mindmaps
116+
openBtn.onclick = async function(){
117+
// request list of available mindmaps from JabRef's http server
118+
let response = await httpClient.listMaps();
119+
120+
let select = document.getElementById('openMindmapSelect');
121+
// reset select options
122+
select.innerHTML = '';
123+
124+
// populate select element with options
125+
for (let i = 0; i < response.length; i++) {
126+
select.innerHTML += '<option value="' + response[i] + '">' + response[i] + '</option>';
127+
}
107128
}
108129

109-
// open
110-
openBtn.onclick = function(){
111-
// opening jabmaps magic here..
130+
// Modal dialog confirmation button
131+
openSelectedMapBtn.onclick = async function(){
132+
// get selected mindmap name
133+
let select = document.getElementById('openMindmapSelect');
134+
let selectedMindmap = select.options[select.selectedIndex].value;
135+
136+
// get mindmap data from server
137+
let responseMindmap = await httpClient.loadMap("libraries/" + selectedMindmap + "/map");
138+
139+
// display mindmap
140+
jm.show(responseMindmap.map);
112141
}
113142

114143
// undo

0 commit comments

Comments
 (0)