Implement EEPROM client directly into the chat.
This commit is contained in:
parent
7f0428a4ef
commit
12bb564613
3 changed files with 183 additions and 138 deletions
src/mami.js
|
@ -1,37 +1,105 @@
|
||||||
#include eeprom/script.jsx
|
#include utility.js
|
||||||
|
|
||||||
const MamiEEPROM = function(baseUrl, getAuthLine) {
|
const MamiEEPROM = function(endPoint, getAuthLine) {
|
||||||
if(typeof baseUrl !== 'string')
|
if(typeof endPoint !== 'string')
|
||||||
throw 'baseUrl must be a string';
|
throw 'endPoint must be a string';
|
||||||
if(typeof getAuthLine !== 'function')
|
if(typeof getAuthLine !== 'function')
|
||||||
throw 'getAuthLine must be a function';
|
throw 'getAuthLine must be a function';
|
||||||
|
|
||||||
// when the pools rewrite happen, retrieve this from futami common
|
// when the pools rewrite happen, retrieve this from futami common
|
||||||
const appId = '1';
|
const appId = '1';
|
||||||
let client;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
get client() {
|
|
||||||
return client;
|
|
||||||
},
|
|
||||||
|
|
||||||
init: async () => {
|
|
||||||
await MamiEEPROMLoadScript(baseUrl);
|
|
||||||
client = new EEPROM(appId, baseUrl, getAuthLine);
|
|
||||||
},
|
|
||||||
|
|
||||||
create: fileInput => {
|
create: fileInput => {
|
||||||
if(client === undefined)
|
if(!(fileInput instanceof File))
|
||||||
throw 'eeprom client is uninitialised';
|
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 => {
|
delete: async fileInfo => {
|
||||||
if(client === undefined)
|
if(typeof fileInfo !== 'object')
|
||||||
throw 'eeprom client is uninitialised';
|
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}`;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -716,112 +716,108 @@ const MamiInit = async args => {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
let doUpload;
|
|
||||||
ctx.eeprom = new MamiEEPROM(futami.get('eeprom2'), MamiMisuzuAuth.getLine);
|
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;
|
sbUploads.addOption({
|
||||||
if(upload.isImage()) {
|
name: 'view',
|
||||||
text = `[img]${upload.url}[/img]`;
|
text: 'View upload',
|
||||||
} else if(upload.isAudio()) {
|
condition: entry => entry.uploadInfo !== undefined,
|
||||||
text = `[audio]${upload.url}[/audio]`;
|
onclick: entry => window.open(entry.uploadInfo.url),
|
||||||
} else if(upload.isVideo()) {
|
});
|
||||||
text = `[video]${upload.url}[/video]`;
|
sbUploads.addOption({
|
||||||
} else
|
name: 'insert',
|
||||||
text = location.protocol + upload.url;
|
text: 'Insert into message',
|
||||||
|
condition: entry => entry.uploadInfo !== undefined,
|
||||||
|
onclick: entry => {
|
||||||
|
const upload = entry.uploadInfo;
|
||||||
|
|
||||||
chatForm.input.insertAtCursor(text);
|
let text;
|
||||||
},
|
if(upload.isImage()) {
|
||||||
});
|
text = `[img]${upload.url}[/img]`;
|
||||||
sbUploads.addOption({
|
} else if(upload.isAudio()) {
|
||||||
name: 'delete',
|
text = `[audio]${upload.url}[/audio]`;
|
||||||
text: 'Delete upload',
|
} else if(upload.isVideo()) {
|
||||||
condition: entry => entry.uploadInfo !== undefined,
|
text = `[video]${upload.url}[/video]`;
|
||||||
onclick: async entry => {
|
} else
|
||||||
try {
|
text = location.protocol + upload.url;
|
||||||
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] });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
doUpload = async file => {
|
chatForm.input.insertAtCursor(text);
|
||||||
const entry = sbUploads.createEntry(file);
|
},
|
||||||
|
});
|
||||||
|
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);
|
const doUpload = async file => {
|
||||||
task.onProgress(prog => {
|
const entry = sbUploads.createEntry(file);
|
||||||
entry.progress = prog.progress;
|
|
||||||
});
|
|
||||||
entry.addOption({
|
|
||||||
name: 'cancel',
|
|
||||||
text: 'Cancel upload',
|
|
||||||
onclick: () => { task.abort(); },
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
const task = ctx.eeprom.create(file);
|
||||||
const fileInfo = await task.start();
|
task.onProgress(prog => {
|
||||||
|
entry.progress = prog.progress;
|
||||||
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);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
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
|
// figure out how to display a UI for this someday
|
||||||
//args.parent.addEventListener('dragenter', ev => { console.info('dragenter', ev); });
|
//args.parent.addEventListener('dragenter', ev => { console.info('dragenter', ev); });
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue