Skip to content

Commit b5db863

Browse files
author
Chris Warren-Smith
committed
ANDROID: replace MUI based portal with native js implementation
1 parent 3f3b7dc commit b5db863

7 files changed

Lines changed: 264 additions & 113 deletions

File tree

src/platform/android/app/src/main/assets/webui/index.html

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,21 @@
1414
<h1>SmallBASIC</h1>
1515
<a href="https://smallbasic.github.io" target="_blank">smallbasic.github.io</a>
1616
</div>
17-
<div class="toolbar hidden" id="toolbar">
17+
18+
<div id="view-login" class="login-container">
19+
<div class="login-form">
20+
<div class="form-group">
21+
<label for="token-input">Enter your access token</label>
22+
<input type="text" id="token-input" autofocus>
23+
<div class="helper-text" id="helper-text">
24+
Enter the access token displayed on the SmallBASIC [About] screen.
25+
</div>
26+
</div>
27+
<button class="btn" id="submit-btn">Submit</button>
28+
</div>
29+
</div>
30+
31+
<div id="view-toolbar" class="toolbar hidden">
1832
<button class="btn btn-small btn-icon" id="upload-btn">
1933
📤 UPLOAD
2034
</button>
@@ -26,7 +40,32 @@ <h1>SmallBASIC</h1>
2640
</button>
2741
<div id="selection-info" style="margin-left: auto; color: #666;"></div>
2842
</div>
29-
<div class="main-content" id="main-content"></div>
43+
44+
<div id="view-content" class="main-content hidden">
45+
<div class="table-container">
46+
<table>
47+
<thead>
48+
<tr>
49+
<th>
50+
<input type="checkbox" class="checkbox" id="select-all">
51+
</th>
52+
<th data-field="fileName" class="sortable" style="cursor: pointer;">
53+
Name <span class="sort-indicator active"></span>
54+
</th>
55+
<th data-field="size" class="sortable" style="cursor: pointer;">
56+
Size <span class="sort-indicator"></span>
57+
</th>
58+
<th data-field="date" class="sortable" style="cursor: pointer;">
59+
Modified <span class="sort-indicator"></span>
60+
</th>
61+
</tr>
62+
</thead>
63+
<tbody id="file-table-body">
64+
<!-- Rows will be inserted here -->
65+
</tbody>
66+
</table>
67+
</div>
68+
</div>
3069
<input type="file" id="file-upload" multiple class="hidden">
3170
<a class="hidden" id="file-download"></a>
3271
</body>

src/platform/android/app/src/main/assets/webui/main.js

Lines changed: 66 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -10,33 +10,19 @@
1010
setupEventListeners();
1111

1212
function renderContent() {
13-
const mainContent = document.getElementById('main-content');
1413
if (token) {
15-
renderFileList(mainContent);
14+
renderFileList();
1615
} else {
17-
renderTokenInput(mainContent);
16+
renderTokenInput();
1817
}
1918
}
2019

21-
function renderTokenInput(container) {
22-
container.innerHTML = `
23-
<div class="login-container">
24-
<div class="login-form">
25-
<div class="form-group">
26-
<label for="token-input">Enter your access token</label>
27-
<input type="text" id="token-input" autofocus>
28-
<div class="helper-text" id="helper-text">
29-
Enter the access token displayed on the SmallBASIC [About] screen.
30-
</div>
31-
</div>
32-
<button class="btn" id="submit-btn">Submit</button>
33-
</div>
34-
</div>
35-
`;
36-
20+
function renderTokenInput() {
3721
const tokenInput = document.getElementById('token-input');
3822
const submitBtn = document.getElementById('submit-btn');
3923

24+
showLoginView(true);
25+
4026
tokenInput.addEventListener('keypress', (e) => {
4127
if (e.key === 'Enter') {
4228
handleLogin();
@@ -49,43 +35,18 @@
4935
}
5036

5137
function renderFileList(container) {
52-
container.innerHTML = `
53-
<div class="table-container">
54-
<table>
55-
<thead>
56-
<tr>
57-
<th>
58-
<input type="checkbox" class="checkbox" id="select-all">
59-
</th>
60-
<th data-field="fileName" class="sortable" style="cursor: pointer;">
61-
Name <span class="sort-indicator active">↑</span>
62-
</th>
63-
<th data-field="size" class="sortable" style="cursor: pointer;">
64-
Size <span class="sort-indicator">↕</span>
65-
</th>
66-
<th data-field="date" class="sortable" style="cursor: pointer;">
67-
Modified <span class="sort-indicator">↕</span>
68-
</th>
69-
</tr>
70-
</thead>
71-
<tbody id="file-table-body">
72-
<!-- Rows will be inserted here -->
73-
</tbody>
74-
</table>
75-
</div>
76-
`;
77-
78-
const toolbar = document.getElementById('toolbar');
79-
toolbar.classList.remove('hidden');
80-
38+
showLoginView(false);
8139
renderTableRows();
8240
updateToolbarState();
8341
setupFileListEventListeners();
8442
}
8543

8644
function renderTableRows() {
8745
const tbody = document.getElementById('file-table-body');
88-
if (!tbody) return;
46+
if (!tbody) {
47+
console.err("file-table-body not found");
48+
return;
49+
}
8950

9051
// Sort rows
9152
const sortedRows = [...rows].sort((a, b) => {
@@ -274,6 +235,7 @@
274235
if (!editingCell) return;
275236

276237
const newValue = input.value.trim();
238+
// this overwrites 'input' thus removing from the DOM and GCd
277239
editingCell.textContent = save ? newValue : currentValue;
278240

279241
if (save && newValue !== currentValue && newValue) {
@@ -327,38 +289,59 @@
327289
async function handleFileUpload(event) {
328290
const files = Array.from(event.target.files);
329291
if (files.length === 0) return;
292+
let result = null;
330293

331294
showSnackbar('Uploading files...', 'info');
332295

333296
try {
334297
for (let i = 0; i < files.length; i++) {
335298
const file = files[i];
336-
const dataUrl = await fileToDataURL(file);
337-
await callApi('/api/upload', `fileName=${encodeURIComponent(file.name)}&data=${dataUrl}`);
299+
300+
// Send raw file bytes
301+
const response = await fetch('/api/upload', {
302+
method: 'POST',
303+
headers: {
304+
'Content-Type': 'application/octet-stream',
305+
'X-File-Name': encodeURIComponent(file.name),
306+
'X-File-Size': file.size
307+
},
308+
body: file
309+
});
310+
311+
if (!response.ok) {
312+
throw new Error('Upload failed');
313+
}
314+
315+
result = await response.json()
316+
if (result.error) {
317+
throw new Error(result.error);
318+
}
338319

339320
if (files.length > 1) {
340321
showSnackbar(`Uploaded ${i + 1}/${files.length} files`, 'info');
341322
}
342323
}
343324

344-
const data = await callApi('/api/files', '');
345-
rows = data;
346-
selectedRows.clear();
347-
renderTableRows();
348-
updateToolbarState();
349-
showSnackbar(`Successfully uploaded ${files.length} file(s)`, 'success');
325+
// refresh file list
326+
rows = await callApi('/api/files', '');
327+
328+
if (result?.success && files.length === 1) {
329+
showSnackbar(result.success, 'success');
330+
} else {
331+
showSnackbar(`Successfully uploaded ${files.length} file(s)`, 'success');
332+
}
350333
} catch (error) {
351-
showSnackbar(error, 'error');
352-
// Refresh to show any partial uploads
334+
// refresh to show any partial uploads
353335
try {
354-
const data = await callApi('/api/files', '');
355-
rows = data;
356-
renderTableRows();
336+
rows = await callApi('/api/files', '');
357337
} catch (e) {}
338+
showSnackbar(error.message, 'error');
358339
}
359-
360-
// Reset file input
361340
event.target.value = '';
341+
342+
selectedRows.clear();
343+
renderTableRows();
344+
updateToolbarState();
362345
}
363346

364347
function handleDownload() {
@@ -404,8 +387,7 @@
404387
`Are you sure you want to permanently delete ${selectedRow.fileName}? You cannot undo `
405388
)) {
406389
try {
407-
const data = await callApi('/api/delete', `fileName=${encodeURIComponent(selectedRow.fileName)}`);
408-
rows = data;
390+
rows = await callApi('/api/delete', `fileName=${encodeURIComponent(selectedRow.fileName)}`);
409391
selectedRows.clear();
410392
renderTableRows();
411393
updateToolbarState();
@@ -443,9 +425,10 @@
443425

444426
const data = await response.json();
445427
if (data.error) {
446-
console.log(data);
428+
console.error(data);
447429
throw new Error(data.error);
448430
}
431+
449432
return data;
450433
}
451434

@@ -520,4 +503,20 @@
520503
div.textContent = text;
521504
return div.innerHTML;
522505
}
506+
507+
function showLoginView(show) {
508+
showView('login', show);
509+
showView('toolbar', !show);
510+
showView('content', !show);
511+
}
512+
513+
function showView(name, show = true) {
514+
const view = document.getElementById(`view-${name}`);
515+
if (show) {
516+
view.classList.remove('hidden');
517+
} else {
518+
view.classList.add('hidden');
519+
}
520+
}
521+
523522
}());

0 commit comments

Comments
 (0)