Implement EEPROM client directly into the chat.

This commit is contained in:
flash 2025-01-04 01:35:56 +00:00
parent 7f0428a4ef
commit 12bb564613
3 changed files with 183 additions and 138 deletions

View file

@ -1,37 +1,105 @@
#include eeprom/script.jsx
#include utility.js
const MamiEEPROM = function(baseUrl, getAuthLine) {
if(typeof baseUrl !== 'string')
throw 'baseUrl must be a string';
const MamiEEPROM = function(endPoint, getAuthLine) {
if(typeof endPoint !== 'string')
throw 'endPoint must be a string';
if(typeof getAuthLine !== 'function')
throw 'getAuthLine must be a function';
// when the pools rewrite happen, retrieve this from futami common
const appId = '1';
let client;
return {
get client() {
return client;
},
init: async () => {
await MamiEEPROMLoadScript(baseUrl);
client = new EEPROM(appId, baseUrl, getAuthLine);
},
create: fileInput => {
if(client === undefined)
throw 'eeprom client is uninitialised';
if(!(fileInput instanceof File))
throw 'fileInput must be an instance of window.File';
return client.create(fileInput);
let userAborted = false;
let abortHandler;
let progressHandler;
const reportProgress = ev => {
if(progressHandler !== undefined)
progressHandler({
loaded: ev.loaded,
total: ev.total,
progress: ev.total <= 0 ? 0 : ev.loaded / ev.total,
});
};
return {
abort: () => {
userAborted = true;
if(typeof abortHandler === 'function')
abortHandler();
},
onProgress: handler => {
if(typeof handler !== 'function')
throw 'handler must be a function';
progressHandler = handler;
},
start: async () => {
if(userAborted)
throw 'File upload was cancelled by the user, it cannot be restarted.';
try {
const formData = new FormData;
formData.append('src', appId);
formData.append('file', fileInput);
const result = await $x.post(`${endPoint}/uploads`, {
type: 'json',
headers: {
Authorization: getAuthLine(),
},
upload: reportProgress,
abort: handler => abortHandler = handler,
}, formData);
const body = result.body();
console.log(body);
if(body === null)
throw "The upload server didn't return the metadata for some reason.";
if(result.status !== 201)
throw body.english ?? body.error ?? `Upload failed with status code ${result.status}`;
body.isImage = () => body.type.startsWith('image/');
body.isVideo = () => body.type.startsWith('video/');
body.isAudio = () => body.type.startsWith('audio/');
body.isMedia = () => body.isImage() || body.isAudio() || body.isVideo();
return Object.freeze(body);
} catch(ex) {
if(userAborted)
throw 'Upload has been cancelled by the user.';
console.error(ex);
throw ex;
}
},
};
},
delete: async fileInfo => {
if(client === undefined)
throw 'eeprom client is uninitialised';
if(typeof fileInfo !== 'object')
throw 'fileInfo must be an object';
if(typeof fileInfo.urlf !== 'string')
throw 'fileInfo.urlf must be a string';
await client.delete(fileInfo);
const result = await $x.delete(fileInfo.urlf, {
type: 'json',
headers: {
Authorization: getAuthLine(),
},
});
if(result.status !== 204) {
const body = result.body();
if(body === null)
throw `Delete failed with status code ${result.status}`;
throw body.english ?? body.error ?? `Delete failed with status code ${result.status}`;
}
},
};
};

View file

@ -1,19 +0,0 @@
#include utility.js
const MamiEEPROMLoadScript = baseUrl => {
return new Promise((resolve, reject) => {
if(typeof baseUrl !== 'string')
throw 'baseUrl must be a string';
const src = `${baseUrl}/scripts/eepromv1a.js`;
let script = $q(`script[src="${src}"]`);
if(script instanceof Element) {
resolve();
return;
}
script = <script src={src} onload={() => { resolve(); }} onerror={() => { $r(script); reject(); }} />;
document.body.appendChild(script);
});
};

View file

@ -716,112 +716,108 @@ const MamiInit = async args => {
});
let doUpload;
ctx.eeprom = new MamiEEPROM(futami.get('eeprom2'), MamiMisuzuAuth.getLine);
ctx.eeprom.init()
.catch(ex => { console.error('Failed to initialise EEPROM.', ex); })
.then(() => {
sbUploads.addOption({
name: 'view',
text: 'View upload',
condition: entry => entry.uploadInfo !== undefined,
onclick: entry => window.open(entry.uploadInfo.url),
});
sbUploads.addOption({
name: 'insert',
text: 'Insert into message',
condition: entry => entry.uploadInfo !== undefined,
onclick: entry => {
const upload = entry.uploadInfo;
let text;
if(upload.isImage()) {
text = `[img]${upload.url}[/img]`;
} else if(upload.isAudio()) {
text = `[audio]${upload.url}[/audio]`;
} else if(upload.isVideo()) {
text = `[video]${upload.url}[/video]`;
} else
text = location.protocol + upload.url;
sbUploads.addOption({
name: 'view',
text: 'View upload',
condition: entry => entry.uploadInfo !== undefined,
onclick: entry => window.open(entry.uploadInfo.url),
});
sbUploads.addOption({
name: 'insert',
text: 'Insert into message',
condition: entry => entry.uploadInfo !== undefined,
onclick: entry => {
const upload = entry.uploadInfo;
chatForm.input.insertAtCursor(text);
},
});
sbUploads.addOption({
name: 'delete',
text: 'Delete upload',
condition: entry => entry.uploadInfo !== undefined,
onclick: async entry => {
try {
await ctx.eeprom.delete(entry.uploadInfo);
sbUploads.deleteEntry(entry);
} catch(ex) {
console.error(ex);
await ctx.msgbox.show({ body: ['An error occurred while trying to delete an uploaded file:', ex] });
}
},
});
let text;
if(upload.isImage()) {
text = `[img]${upload.url}[/img]`;
} else if(upload.isAudio()) {
text = `[audio]${upload.url}[/audio]`;
} else if(upload.isVideo()) {
text = `[video]${upload.url}[/video]`;
} else
text = location.protocol + upload.url;
doUpload = async file => {
const entry = sbUploads.createEntry(file);
chatForm.input.insertAtCursor(text);
},
});
sbUploads.addOption({
name: 'delete',
text: 'Delete upload',
condition: entry => entry.uploadInfo !== undefined,
onclick: async entry => {
try {
await ctx.eeprom.delete(entry.uploadInfo);
sbUploads.deleteEntry(entry);
} catch(ex) {
console.error(ex);
await ctx.msgbox.show({ body: ['An error occurred while trying to delete an uploaded file:', ex] });
}
},
});
const task = ctx.eeprom.create(file);
task.onProgress(prog => {
entry.progress = prog.progress;
});
entry.addOption({
name: 'cancel',
text: 'Cancel upload',
onclick: () => { task.abort(); },
});
const doUpload = async file => {
const entry = sbUploads.createEntry(file);
try {
const fileInfo = await task.start();
entry.optionsVisible = false;
entry.uploadInfo = fileInfo;
entry.removeOption('cancel');
entry.nukeProgress();
sbUploads.reloadOptionsFor(entry);
if(settings.get('eepromAutoInsert'))
entry.clickOption('insert');
} catch(ex) {
if(!ex.aborted) {
console.error(ex);
ctx.msgbox.show({ body: ['An error occurred while trying to upload a file:', ex] });
}
sbUploads.deleteEntry(entry);
}
};
const uploadForm = $e({
tag: 'input',
attrs: {
type: 'file',
multiple: true,
style: { display: 'none' },
onchange: ev => {
for(const file of ev.target.files)
doUpload(file);
},
},
});
args.parent.appendChild(uploadForm);
chatForm.input.createButton({
title: 'Upload',
icon: 'fas fa-file-upload',
onclick: () => { uploadForm.click(); },
});
ctx.events.watch('form:upload', ev => {
console.info(ev);
for(const file of ev.detail.files)
doUpload(file);
});
const task = ctx.eeprom.create(file);
task.onProgress(prog => {
entry.progress = prog.progress;
});
entry.addOption({
name: 'cancel',
text: 'Cancel upload',
onclick: () => { task.abort(); },
});
try {
const fileInfo = await task.start();
entry.optionsVisible = false;
entry.uploadInfo = fileInfo;
entry.removeOption('cancel');
entry.nukeProgress();
sbUploads.reloadOptionsFor(entry);
if(settings.get('eepromAutoInsert'))
entry.clickOption('insert');
} catch(ex) {
if(!ex.aborted) {
console.error(ex);
ctx.msgbox.show({ body: ['An error occurred while trying to upload a file:', ex] });
}
sbUploads.deleteEntry(entry);
}
};
const uploadForm = $e({
tag: 'input',
attrs: {
type: 'file',
multiple: true,
style: { display: 'none' },
onchange: ev => {
for(const file of ev.target.files)
doUpload(file);
},
},
});
args.parent.appendChild(uploadForm);
chatForm.input.createButton({
title: 'Upload',
icon: 'fas fa-file-upload',
onclick: () => { uploadForm.click(); },
});
ctx.events.watch('form:upload', ev => {
console.info(ev);
for(const file of ev.detail.files)
doUpload(file);
});
// figure out how to display a UI for this someday
//args.parent.addEventListener('dragenter', ev => { console.info('dragenter', ev); });