Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Insert image and video through editor #47

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 152 additions & 0 deletions public/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,155 @@ globalThis.ghConfig = {
},
};

const PATH_TO_UPLOAD = 'assets';

// Shared upload functionality
const handleFileUpload = async (file, editor, fileType, insertTemplate) => {
// Check if file is correct type
if (!file.type.startsWith(fileType + '/')) {
alert(`Please select a ${fileType} file`);
return;
}

// Check file size (10MB = 10 * 1024 * 1024 bytes)
if (file.size > 10 * 1024 * 1024) {
alert('File size must be less than 10MB');
return;
}

// Create a FormData object to hold the file data
const formData = new FormData();
formData.append('file', file);

window.parent.postMessage({
type: 'EDITOR_FILE_UPLOADING'
}, "*");

// Convert file to base64
const reader = new FileReader();
const base64Promise = new Promise((resolve) => {
reader.onload = () => {
const base64 = reader.result.split(',')[1];
resolve(base64);
};
});
reader.readAsDataURL(file);
const base64Content = await base64Promise;

try {
// Get PR number from URL path
const pathParts = window.location.pathname.split('/');
const prNumber = pathParts[1]; // Get PR number like "62"

// Get session details to get branch info
const token = await globalThis.ghConfig.config.auth;
const owner = globalThis.ghConfig.config.username;
const repo = globalThis.ghConfig.config.repo;

// Get PR details to get branch ref
const prResponse = await fetch(`https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}`, {
headers: {
'Authorization': `token ${token}`,
'Accept': 'application/vnd.github.v3+json'
}
});

if (!prResponse.ok) {
throw new Error(`Failed to get PR details`);
}

const prData = await prResponse.json();
const branchRef = prData.head.ref;

const branchOwner = prData.head.repo.owner.login;
const repoName = prData.head.repo.name;

// Upload file to branch
const timestamp = Date.now();
const fileName = file.name.replace(/[^a-zA-Z0-9\s.-]/g, '').replace(/\s+/g, '-').replace(/\.([^.]*)$/, `-${timestamp}.$1`);
const uploadResponse = await fetch(`https://api.github.com/repos/${branchOwner}/${repoName}/contents/${PATH_TO_UPLOAD}/${fileName}`, {
method: 'PUT',
headers: {
'Authorization': `token ${token}`,
'Accept': 'application/vnd.github.v3+json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
message: `Upload ${fileType} ${file.name}`,
content: base64Content,
branch: branchRef
})
});

if (!uploadResponse.ok) {
throw new Error(`Failed to upload ${fileType}`);
}

const uploadData = await uploadResponse.json();
const fileUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${uploadData.commit.sha}/${PATH_TO_UPLOAD}/${fileName}`;

// Insert using provided template
editor.codemirror.getDoc().setValue(
editor.codemirror.getDoc().getValue() + insertTemplate(fileUrl)
);

} catch (error) {
if(fileType === 'image') {
editor.codemirror.getDoc().setValue(
editor.codemirror.getDoc().getValue() + insertTemplate(reader.result)
);
} else {
alert('Failed to upload file');
}
}

window.parent.postMessage({
type: 'EDITOR_FILE_UPLOADED'
}, "*");
};

const insertImageTool = {
name: "Attach Image",
action: customFunction = (editor) => {
let input = document.createElement('input');
input.type = 'file';
input.onchange = async e => {
console.log(globalThis.ghConfig);
const file = e.target.files[0];
await handleFileUpload(
file,
editor,
'image',
(url) => `\n![](${url})`
);
};
input.click();
},
className: "fa fa-image",
title: "Attach Image",
};

const insertVideoTool = {
name: "Attach Video",
action: customFunction = (editor) => {
let input = document.createElement('input');
input.type = 'file';
input.onchange = async e => {
console.log(globalThis.ghConfig);
const file = e.target.files[0];
await handleFileUpload(
file,
editor,
'video',
(url) => `\n<video src="${url}" controls></video>`
);
};
input.click();
},
className: "fa fa-video-camera",
title: "Attach Video",
};

globalThis.schemaMap = [
{
path: "/eo-missions/catalog.json",
Expand Down Expand Up @@ -75,6 +224,9 @@ globalThis.schemaMap = [
"unordered-list",
"ordered-list",
"link",
"|",
insertImageTool,
insertVideoTool
],
spellChecker: false,
},
Expand Down
5 changes: 5 additions & 0 deletions src/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ export async function initOctokit() {
const username = config.githubOwner || import.meta.env.VUE_APP_GITHUB_OWNER;
const repo = config.githubRepo || import.meta.env.VUE_APP_GITHUB_REPO;

globalThis.ghConfig = {
...config,
config: { auth, username, repo },
};

const octokit = new Octokit({ auth });

const { data } = await octokit.rest.users.getAuthenticated();
Expand Down
91 changes: 44 additions & 47 deletions src/components/file/CreateFile.vue
Original file line number Diff line number Diff line change
Expand Up @@ -186,60 +186,57 @@ const getSelectedFileFolder = (name) => {
</script>

<template>
<div
v-if="props.open"
class="d-flex justify-center border-b create-file"
>
<div v-if="props.open" class="d-flex justify-center border-b create-file">
<v-row>
<v-col cols="12" class="d-flex">
<div
class="px-6 py-6 border-b-thin session-create-field d-flex w-100 align-center justify-center"
>
<v-combobox
v-model="filePath"
@paste="onPastePathName"
@keydown="onKeyDownPathName"
label="File Name"
placeholder="my/new/file.txt"
:items="map(currPathDirStructure, 'name')"
hide-details
color="primary"
variant="outlined"
>
<template #prepend-inner>
<span
class="prepend text-mono font-weight-bold text-primary opacity-80"
>(root){{ updatedFilePath }}</span
>
</template>
<template v-slot:item="{ props, item, index }">
<v-list-item
:prepend-icon="`mdi-${getSelectedFileFolder(item.raw).icon}-outline`"
v-bind="props"
:title="item.raw"
@click="() => onSelectFile(getSelectedFileFolder(item.raw))"
>
<template
v-if="getSelectedFileFolder(item.raw).type === 'file'"
v-slot:append
<v-combobox
v-model="filePath"
@paste="onPastePathName"
@keydown="onKeyDownPathName"
label="File Name"
placeholder="my/new/file.txt"
:items="map(currPathDirStructure, 'name')"
hide-details
color="primary"
variant="outlined"
>
<template #prepend-inner>
<span
class="prepend text-mono font-weight-bold text-primary opacity-80"
>(root){{ updatedFilePath }}</span
>
</template>
<template v-slot:item="{ props, item, index }">
<v-list-item
:prepend-icon="`mdi-${getSelectedFileFolder(item.raw).icon}-outline`"
v-bind="props"
:title="item.raw"
@click="() => onSelectFile(getSelectedFileFolder(item.raw))"
>
<div
class="text-blue-accent-4 d-flex align-center ga-2 text-sm-body-2 font-weight-bold"
<template
v-if="getSelectedFileFolder(item.raw).type === 'file'"
v-slot:append
>
<p class="text-uppercase">Edit File</p>
<v-icon>mdi-open-in-new</v-icon>
</div>
</template>
</v-list-item>
</template>
</v-combobox>
<v-btn
@click="updateSchema"
icon="mdi-plus"
variant="flat"
color="primary"
size="large"
></v-btn>
<div
class="text-blue-accent-4 d-flex align-center ga-2 text-sm-body-2 font-weight-bold"
>
<p class="text-uppercase">Edit File</p>
<v-icon>mdi-open-in-new</v-icon>
</div>
</template>
</v-list-item>
</template>
</v-combobox>
<v-btn
@click="updateSchema"
icon="mdi-plus"
variant="flat"
color="primary"
size="large"
></v-btn>
</div>
</v-col>
</v-row>
Expand Down
8 changes: 7 additions & 1 deletion src/methods/file-edit-view/post-message.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { updateSchemaDefaults } from "@/helpers/index.js";
import { updateSchemaDefaults, useLoader } from "@/helpers/index.js";
import isEqual from "lodash.isequal";
import { initEOXJSONFormMethod } from "@/methods/file-edit-view/init-eox-jsonform.js";

Expand All @@ -18,6 +18,8 @@ export function addPostMessageEventMethod({
jsonFormInstance,
isSchemaBased,
}) {
const loader = useLoader();
let loaderInstance = null;
window.addEventListener("message", function (event) {
if (
event.data &&
Expand All @@ -31,5 +33,9 @@ export function addPostMessageEventMethod({
}
}
}
if (event.data && event.data.type === "EDITOR_FILE_UPLOADING")
loaderInstance = loader.show();
if (event.data && event.data.type === "EDITOR_FILE_UPLOADED")
loaderInstance?.hide();
});
}