Skip to content

Commit

Permalink
Infer file type for embeds for previews
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastinez committed Dec 19, 2024
1 parent bec0404 commit 45516be
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 21 deletions.
26 changes: 25 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions crates/radicle-tauri/src/commands/cob.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ pub mod patch;
pub async fn get_embed(
ctx: tauri::State<'_, AppState>,
rid: identity::RepoId,
name: Option<String>,
oid: git::Oid,
) -> Result<Vec<u8>, Error> {
ctx.get_embed(rid, oid)
) -> Result<types::cobs::EmbedWithMimeType, Error> {
ctx.get_embed(rid, name, oid)
}

#[tauri::command]
Expand Down
2 changes: 2 additions & 0 deletions crates/radicle-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ edition = "2021"
anyhow = { version = "1.0.90" }
axum = { version = "0.7.5", default-features = false, features = ["json"] }
base64 = { version = "0.22.1" }
infer = { version = "0.3" }
mime-infer = { version = "3.0.0" }
radicle = { version = "0.14.0", features = ["test"] }
radicle-surf = { version = "0.22.1", features = ["serde"] }
serde = { version = "1.0.210", features = ["derive"] }
Expand Down
3 changes: 3 additions & 0 deletions crates/radicle-types/bindings/cob/Embed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.

export type Embed = { content: Array<number>; mimeType: string | null };
6 changes: 6 additions & 0 deletions crates/radicle-types/bindings/cob/EmbedWithMimeType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.

export type EmbedWithMimeType = {
content: Array<number>;
mimeType: string | null;
};
9 changes: 9 additions & 0 deletions crates/radicle-types/src/cobs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ impl Author {
}
}

#[derive(Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export)]
#[ts(export_to = "cob/")]
pub struct EmbedWithMimeType {
pub content: Vec<u8>,
pub mime_type: Option<String>,
}

#[derive(TS, Serialize)]
#[doc = "A type alias for the TS type `never`."]
#[ts(export)]
Expand Down
23 changes: 21 additions & 2 deletions crates/radicle-types/src/traits/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,31 @@ use crate::error::Error;
use crate::traits::Profile;

pub trait Thread: Profile {
fn get_embed(&self, rid: identity::RepoId, oid: git::Oid) -> Result<Vec<u8>, Error> {
fn get_embed(
&self,
rid: identity::RepoId,
name: Option<String>,
oid: git::Oid,
) -> Result<cobs::EmbedWithMimeType, Error> {
let profile = self.profile();
let repo = profile.storage.repository(rid)?;
let blob = repo.blob(oid)?;
let content = blob.content();
let mime_type = match infer::get(content).map(|i| i.mime_type().to_string()) {
Some(mime_type) => Some(mime_type),
None if name.is_some() => {
let filename = name.unwrap();
mime_infer::from_path(&filename)
.first()
.map(|m| m.as_ref().to_string())
}
_ => None,
};

Ok::<_, Error>(blob.content().to_vec())
Ok::<_, Error>(cobs::EmbedWithMimeType {
content: content.to_vec(),
mime_type,
})
}

fn save_embed_to_disk(
Expand Down
5 changes: 3 additions & 2 deletions crates/test-http-api/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,14 +244,15 @@ async fn activity_handler(
#[derive(Serialize, Deserialize)]
struct EmbedBody {
pub rid: identity::RepoId,
pub name: Option<String>,
pub oid: git::Oid,
}

async fn get_embeds_handler(
State(ctx): State<Context>,
Json(EmbedBody { rid, oid }): Json<EmbedBody>,
Json(EmbedBody { rid, name, oid }): Json<EmbedBody>,
) -> impl IntoResponse {
let embed = ctx.get_embed(rid, oid)?;
let embed = ctx.get_embed(rid, name, oid)?;

Ok::<_, Error>(Json(embed))
}
Expand Down
33 changes: 19 additions & 14 deletions src/components/Markdown.svelte
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<script lang="ts">
import type { Embed } from "@bindings/cob/Embed";
import dompurify from "dompurify";
import matter from "@radicle/gray-matter";
import { toDom } from "hast-util-to-dom";
Expand Down Expand Up @@ -68,7 +70,6 @@
// If the markdown link is an oid embed
if (href && isCommit(href)) {
e.style.display = "block";
e.onclick = event => {
event.preventDefault();
invoke("save_embed_to_disk", {
Expand All @@ -77,47 +78,51 @@
name: e.innerText,
}).catch(console.error);
};
void invoke<Uint8Array>("get_embed", {
void invoke<Embed>("get_embed", {
rid,
name: e.innerText,
oid: href,
})
.then(byteArray => {
const buffer = Buffer.from(byteArray);
.then(({ mimeType, content }) => {
const buffer = Buffer.from(content);
const blob = new Blob([buffer]);
const url = URL.createObjectURL(blob);
const ext = e.innerHTML.split(".").at(-1);
// Embed an img element below the link
if (ext?.match(/(gif|jpe?g|tiff?|png|webp|bmp)/)) {
if (mimeType?.startsWith("image")) {
const element = document.createElement("img");
element.setAttribute("src", url);
element.style.display = "block";
e.style.display = "block";
e.insertAdjacentElement("afterend", element);
// Embed an iframe to display pdf correctly element below the link
} else if (ext?.match(/(pdf)/)) {
} else if (mimeType?.startsWith("application")) {
const element = document.createElement("embed");
element.setAttribute("src", url);
element.type = "application/pdf";
element.type = mimeType;
element.style.overflow = "scroll";
element.style.height = "40rem";
element.style.overscrollBehavior = "contain";
e.style.display = "block";
e.insertAdjacentElement("afterend", element);
} else if (ext?.match(/(mp4|mov)/)) {
} else if (mimeType?.startsWith("video")) {
const element = document.createElement("video");
const node = document.createElement("source");
node.src = url;
element.controls = true;
node.type = `video/mp4`;
node.type = mimeType;
element.style.width = "100%";
e.style.display = "block";
element.appendChild(node);
e.insertAdjacentElement("afterend", element);
} else if (ext?.match(/(mp3)/)) {
} else if (mimeType?.startsWith("audio")) {
const element = document.createElement("audio");
element.style.display = "block";
element.src = url;
element.controls = true;
e.style.display = "block";
e.insertAdjacentElement("afterend", element);
} else {
console.warn(
`Not able to provide a preview for ${e.innerHTML}`,
);
console.warn(`Not able to provide a preview for this file.`);
}
})
.catch(console.error);
Expand Down

0 comments on commit 45516be

Please sign in to comment.