Add support for language and name hints

This commit is contained in:
Edward Shen 2022-01-15 22:47:56 -08:00
parent 9b2f53bddf
commit b7d5425a73
Signed by: edward
GPG key ID: 19182661E818369F
5 changed files with 147 additions and 72 deletions

View file

@ -52,6 +52,8 @@ pub struct ParsedUrl {
pub struct PartialParsedUrl {
pub decryption_key: Option<Secret<Key>>,
pub needs_password: bool,
pub name: Option<String>,
pub language: Option<String>,
}
#[cfg(test)]
@ -91,7 +93,7 @@ impl TryFrom<&str> for PartialParsedUrl {
return Ok(Self {
decryption_key,
needs_password: false,
..Self::default()
});
}
@ -106,6 +108,8 @@ impl TryFrom<&str> for PartialParsedUrl {
let mut decryption_key = None;
let mut needs_password = false;
let mut name = None;
let mut language = None;
for (key, value) in args {
match (key, value) {
@ -117,6 +121,8 @@ impl TryFrom<&str> for PartialParsedUrl {
("pw", _) => {
needs_password = true;
}
("name", Some(provided_name)) => name = Some(provided_name.to_owned()),
("lang", Some(provided_lang)) => language = Some(provided_lang.to_owned()),
_ => (),
}
}
@ -124,6 +130,8 @@ impl TryFrom<&str> for PartialParsedUrl {
Ok(Self {
decryption_key,
needs_password,
name,
language,
})
}
}
@ -159,6 +167,7 @@ impl FromStr for ParsedUrl {
let PartialParsedUrl {
mut decryption_key,
needs_password,
..
} = PartialParsedUrl::try_from(fragment)?;
url.set_fragment(None);
@ -338,7 +347,7 @@ mod partial_parsed_url_parsing {
DECRYPTION_KEY_STRING.parse(),
Ok(PartialParsedUrl {
decryption_key: decryption_key(),
needs_password: false
..Default::default()
})
);
}
@ -350,7 +359,7 @@ mod partial_parsed_url_parsing {
input.parse(),
Ok(PartialParsedUrl {
decryption_key: decryption_key(),
needs_password: false
..Default::default()
})
);
}
@ -362,7 +371,34 @@ mod partial_parsed_url_parsing {
input.parse(),
Ok(PartialParsedUrl {
decryption_key: decryption_key(),
needs_password: true
needs_password: true,
..Default::default()
})
);
}
#[test]
fn with_name() {
let input = "key:ddLod7sGy_EjFDjWqZoH4i5n_XU8bIpEuEo3-pjfAIE=!name:test_file.rs";
assert_eq!(
input.parse(),
Ok(PartialParsedUrl {
decryption_key: decryption_key(),
name: Some("test_file.rs".to_owned()),
..Default::default()
})
);
}
#[test]
fn with_lang() {
let input = "key:ddLod7sGy_EjFDjWqZoH4i5n_XU8bIpEuEo3-pjfAIE=!lang:rust";
assert_eq!(
input.parse(),
Ok(PartialParsedUrl {
decryption_key: decryption_key(),
language: Some("rust".to_owned()),
..Default::default()
})
);
}
@ -374,7 +410,8 @@ mod partial_parsed_url_parsing {
input.parse(),
Ok(PartialParsedUrl {
decryption_key: decryption_key(),
needs_password: true
needs_password: true,
..Default::default()
})
);
}
@ -386,7 +423,7 @@ mod partial_parsed_url_parsing {
input.parse(),
Ok(PartialParsedUrl {
decryption_key: decryption_key(),
needs_password: false
..Default::default()
})
);
}

View file

@ -12,7 +12,7 @@
<script src="static/highlightjs-line-numbers.min.js" defer></script>
<link data-trunk rel="rust" data-wasm-opt="0" data-keep-debug="true" data-no-mangle="true" />
<link data-trunk rel="copy-file" href="vendor/MPLUS_FONTS/fonts/ttf/MplusCodeLatin[wdth,wght].ttf" dest="/" />
<link data-trunk rel="copy-file" href="vendor/MPLUS_FONTS/fonts/ttf/MPLUSCodeLatin[wdth,wght].ttf" dest="/" />
<link data-trunk rel="copy-file" href="vendor/highlight.min.js" dest="/" />
<link data-trunk rel="copy-file" href="vendor/highlightjs-line-numbers.js/dist/highlightjs-line-numbers.min.js"
dest="/" />

View file

@ -47,7 +47,7 @@ const DOWNLOAD_SIZE_LIMIT: u128 = n_mib_bytes!(500);
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_name = loadFromDb)]
pub fn load_from_db(mimetype: JsString);
pub fn load_from_db(mime_type: JsString, name: Option<JsString>, language: Option<JsString>);
#[wasm_bindgen(js_name = renderMessage)]
pub fn render_message(message: JsString);
}
@ -88,7 +88,15 @@ fn main() {
Uri::from_parts(uri_parts).unwrap()
};
let (key, needs_pw) = {
let (
key,
PartialParsedUrl {
needs_password,
name,
language,
..
},
) = {
let fragment = if let Some(fragment) = url.split_once('#').map(|(_, fragment)| fragment) {
if fragment.is_empty() {
error!("Key is missing in url; bailing.");
@ -102,7 +110,7 @@ fn main() {
return;
};
let partial_parsed_url = match PartialParsedUrl::try_from(fragment) {
let mut partial_parsed_url = match PartialParsedUrl::try_from(fragment) {
Ok(partial_parsed_url) => partial_parsed_url,
Err(e) => {
error!("Failed to parse text fragment; bailing.");
@ -111,7 +119,7 @@ fn main() {
}
};
let key = if let Some(key) = partial_parsed_url.decryption_key {
let key = if let Some(key) = partial_parsed_url.decryption_key.take() {
key
} else {
error!("Key is missing in url; bailing.");
@ -119,10 +127,10 @@ fn main() {
return;
};
(key, partial_parsed_url.needs_password)
(key, partial_parsed_url)
};
let password = if needs_pw {
let password = if needs_password {
loop {
let pw = window().prompt_with_message("A password is required to decrypt this paste:");
@ -150,7 +158,7 @@ fn main() {
};
spawn_local(async move {
if let Err(e) = fetch_resources(request_uri, key, password).await {
if let Err(e) = fetch_resources(request_uri, key, password, name, language).await {
log!(e.to_string());
}
});
@ -161,6 +169,8 @@ async fn fetch_resources(
request_uri: Uri,
key: Secret<Key>,
password: Option<SecretVec<u8>>,
name: Option<String>,
language: Option<String>,
) -> Result<()> {
match Request::get(&request_uri.to_string()).send().await {
Ok(resp) if resp.status() == StatusCode::OK => {
@ -213,7 +223,7 @@ async fn fetch_resources(
let db_open_req = open_idb()?;
let on_success = Closure::once(Box::new(move |event| {
on_success(&event, &decrypted, mimetype, &expires);
on_success(&event, &decrypted, mimetype, &expires, name, language);
}));
db_open_req.set_onsuccess(Some(on_success.into_js_value().unchecked_ref()));
@ -245,7 +255,14 @@ async fn fetch_resources(
Ok(())
}
fn on_success(event: &Event, decrypted: &DecryptedData, mimetype: MimeType, expires: &str) {
fn on_success(
event: &Event,
decrypted: &DecryptedData,
mimetype: MimeType,
expires: &str,
name: Option<String>,
language: Option<String>,
) {
let transaction: IdbObjectStore = as_idb_db(event)
.transaction_with_str_and_mode("decrypted data", IdbTransactionMode::Readwrite)
.unwrap()
@ -294,7 +311,9 @@ fn on_success(event: &Event, decrypted: &DecryptedData, mimetype: MimeType, expi
put_action.set_onsuccess(Some(
Closure::once(Box::new(|| {
log!("success");
load_from_db(JsString::from(mimetype.0));
let name = name.map(JsString::from);
let language = language.map(JsString::from);
load_from_db(JsString::from(mimetype.0), name, language);
}))
.into_js_value()
.unchecked_ref(),

View file

@ -20,7 +20,7 @@ $padding: 1em;
@font-face {
font-family: "Mplus Code";
src: url("./MplusCodeLatin[wdth,wght].ttf") format("truetype");
src: url("./MPLUSCodeLatin[wdth,wght].ttf") format("truetype");
}
body {

View file

@ -15,35 +15,38 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// Exported to main.rs
function loadFromDb(mimetype: string) {
console.log("Got mime type:", mimetype);
function loadFromDb(mimeType: string, name?: string, language?: string) {
console.log("[js] Got name:", name);
console.log("[js] Got language:", language);
console.log("[js] Got mime type:", mimeType);
const dbReq = window.indexedDB.open("omegaupload", 1);
dbReq.onsuccess = (evt) => {
const db = (evt.target as IDBRequest).result;
const obj_store = db
.transaction("decrypted data")
.objectStore("decrypted data");
let fetchReq = obj_store.get(window.location.pathname);
const fetchReq = obj_store.get(window.location.pathname);
fetchReq.onsuccess = (evt) => {
const data = (evt.target as IDBRequest).result;
switch (data.type) {
case "string":
createStringPasteUi(data, mimetype);
createStringPasteUi(data, mimeType, name, language);
break;
case "blob":
createBlobPasteUi(data);
createBlobPasteUi(data, name);
break;
case "image":
createImagePasteUi(data);
createImagePasteUi(data, name);
break;
case "audio":
createAudioPasteUi(data);
createAudioPasteUi(data, name);
break;
case "video":
createVideoPasteUi(data);
createVideoPasteUi(data, name);
break;
case "archive":
createArchivePasteUi(data);
createArchivePasteUi(data, name);
break;
default:
renderMessage("Something went wrong. Try clearing local data.");
@ -71,23 +74,24 @@ function loadFromDb(mimetype: string) {
};
}
function createStringPasteUi(data, type: string) {
let bodyEle = document.getElementsByTagName("body")[0];
function createStringPasteUi(data, mimeType: string, name?: string, lang?: string) {
const bodyEle = document.getElementsByTagName("body")[0];
bodyEle.textContent = '';
let mainEle = document.createElement("main");
let preEle = document.createElement("pre");
const mainEle = document.createElement("main");
const preEle = document.createElement("pre");
preEle.classList.add("paste");
let headerEle = document.createElement("p");
const headerEle = document.createElement("p");
headerEle.classList.add("unselectable");
headerEle.classList.add("centered");
headerEle.textContent = data.expiration;
preEle.appendChild(headerEle);
let downloadEle = document.createElement("a");
downloadEle.href = URL.createObjectURL(new Blob([data.data], { type }));
downloadEle.download = window.location.pathname;
const downloadEle = document.createElement("a");
downloadEle.href = getObjectUrl([data.data], mimeType);
downloadEle.download = name;
downloadEle.classList.add("hljs-meta");
downloadEle.classList.add("centered");
downloadEle.textContent = "Download file.";
@ -95,43 +99,52 @@ function createStringPasteUi(data, type: string) {
preEle.appendChild(document.createElement("hr"));
let codeEle = document.createElement("code");
const codeEle = document.createElement("code");
codeEle.textContent = data.data;
preEle.appendChild(codeEle);
mainEle.appendChild(preEle);
bodyEle.appendChild(mainEle);
if (!hljs.getLanguage(lang)) {
console.warn(`[js] User provided language (${lang}) is not known. Ignoring.`);
} else {
console.log(`[js] Selecting user provided language ${lang} for highlighting.`);
hljs.configure({
languages: [lang],
});
}
hljs.highlightAll();
hljs.initLineNumbersOnLoad();
}
function createBlobPasteUi(data) {
let bodyEle = document.getElementsByTagName("body")[0];
function createBlobPasteUi(data, name?: string) {
const bodyEle = document.getElementsByTagName("body")[0];
bodyEle.textContent = '';
let mainEle = document.createElement("main");
const mainEle = document.createElement("main");
mainEle.classList.add("hljs");
mainEle.classList.add("centered");
mainEle.classList.add("fullscreen");
let divEle = document.createElement("div");
const divEle = document.createElement("div");
divEle.classList.add("centered");
let expirationEle = document.createElement("p");
const expirationEle = document.createElement("p");
expirationEle.textContent = data.expiration;
divEle.appendChild(expirationEle);
let downloadEle = document.createElement("a");
downloadEle.href = URL.createObjectURL(data.data);
downloadEle.download = window.location.pathname;
const downloadEle = document.createElement("a");
downloadEle.href = getObjectUrl(data.data, name);
downloadEle.download = name;
downloadEle.classList.add("hljs-meta");
downloadEle.textContent = "Download binary file.";
divEle.appendChild(downloadEle);
mainEle.appendChild(divEle);
let displayAnywayEle = document.createElement("p");
const displayAnywayEle = document.createElement("p");
displayAnywayEle.classList.add("display-anyways");
displayAnywayEle.classList.add("hljs-comment");
displayAnywayEle.textContent = "Display anyways?";
@ -145,41 +158,41 @@ function createBlobPasteUi(data) {
bodyEle.appendChild(mainEle);
}
function createImagePasteUi({ expiration, data, file_size }) {
createMultiMediaPasteUi("img", expiration, data, (downloadEle, imgEle) => {
function createImagePasteUi({ expiration, data, file_size }, name?: string) {
createMultiMediaPasteUi("img", expiration, data, name, (downloadEle, imgEle) => {
imgEle.onload = () => {
let width = imgEle.naturalWidth || imgEle.width;
let height = imgEle.naturalHeight || imgEle.height;
const width = imgEle.naturalWidth || imgEle.width;
const height = imgEle.naturalHeight || imgEle.height;
downloadEle.textContent = "Download " + file_size + " \u2014 " + width + " by " + height;
}
});
}
function createAudioPasteUi({ expiration, data }) {
createMultiMediaPasteUi("audio", expiration, data, "Download");
function createAudioPasteUi({ expiration, data }, name?: string) {
createMultiMediaPasteUi("audio", expiration, data, name, "Download");
}
function createVideoPasteUi({ expiration, data }) {
createMultiMediaPasteUi("video", expiration, data, "Download");
function createVideoPasteUi({ expiration, data }, name?: string) {
createMultiMediaPasteUi("video", expiration, data, name, "Download");
}
function createArchivePasteUi({ expiration, data, entries }) {
let bodyEle = document.getElementsByTagName("body")[0];
function createArchivePasteUi({ expiration, data, entries }, name?: string) {
const bodyEle = document.getElementsByTagName("body")[0];
bodyEle.textContent = '';
let mainEle = document.createElement("main");
const mainEle = document.createElement("main");
let sectionEle = document.createElement("section");
const sectionEle = document.createElement("section");
sectionEle.classList.add("paste");
let expirationEle = document.createElement("p");
const expirationEle = document.createElement("p");
expirationEle.textContent = expiration;
expirationEle.classList.add("centered");
sectionEle.appendChild(expirationEle);
let downloadEle = document.createElement("a");
downloadEle.href = URL.createObjectURL(data);
downloadEle.download = window.location.pathname;
const downloadEle = document.createElement("a");
downloadEle.href = getObjectUrl(data);
downloadEle.download = name;
downloadEle.textContent = "Download";
downloadEle.classList.add("hljs-meta");
downloadEle.classList.add("centered");
@ -187,7 +200,7 @@ function createArchivePasteUi({ expiration, data, entries }) {
sectionEle.appendChild(document.createElement("hr"));
let mediaEle = document.createElement("table");
const mediaEle = document.createElement("table");
mediaEle.classList.add("archive-table");
const tr = mediaEle.insertRow();
tr.classList.add("hljs-title");
@ -224,30 +237,30 @@ function createArchivePasteUi({ expiration, data, entries }) {
bodyEle.appendChild(mainEle);
}
function createMultiMediaPasteUi(tag, expiration, data, on_create?) {
let bodyEle = document.getElementsByTagName("body")[0];
function createMultiMediaPasteUi(tag, expiration, data, name?: string, on_create?) {
const bodyEle = document.getElementsByTagName("body")[0];
bodyEle.textContent = '';
let mainEle = document.createElement("main");
const mainEle = document.createElement("main");
mainEle.classList.add("hljs");
mainEle.classList.add("centered");
mainEle.classList.add("fullscreen");
const downloadLink = URL.createObjectURL(data);
const downloadLink = getObjectUrl(data, name);
let expirationEle = document.createElement("p");
const expirationEle = document.createElement("p");
expirationEle.textContent = expiration;
mainEle.appendChild(expirationEle);
let mediaEle = document.createElement(tag);
const mediaEle = document.createElement(tag);
mediaEle.src = downloadLink;
mediaEle.controls = true;
mainEle.appendChild(mediaEle);
let downloadEle = document.createElement("a");
const downloadEle = document.createElement("a");
downloadEle.href = downloadLink;
downloadEle.download = window.location.pathname;
downloadEle.download = name;
downloadEle.classList.add("hljs-meta");
mainEle.appendChild(downloadEle);
@ -261,9 +274,9 @@ function createMultiMediaPasteUi(tag, expiration, data, on_create?) {
}
function renderMessage(message) {
let body = document.getElementsByTagName("body")[0];
const body = document.getElementsByTagName("body")[0];
body.textContent = '';
let mainEle = document.createElement("main");
const mainEle = document.createElement("main");
mainEle.classList.add("hljs");
mainEle.classList.add("centered");
mainEle.classList.add("fullscreen");
@ -271,4 +284,10 @@ function renderMessage(message) {
body.appendChild(mainEle);
}
function getObjectUrl(data, mimeType?: string) {
return URL.createObjectURL(new Blob(data, {
type: mimeType,
}));
}
window.addEventListener("hashchange", () => location.reload());