Post editing (from file!)
This commit is contained in:
parent
ccce6a3e20
commit
6ce1bb2a48
3 changed files with 129 additions and 19 deletions
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"postTitle": "Hello World!",
|
||||
"timestamp": "1654062776",
|
||||
"editedTimestamp": "",
|
||||
"postContent": "Welcome to my humble blog! This blog is hosted using a blog platform loosely based on my [ngx-dumblog](http:\/\/github.com\/pedroCX486\/ngx-dumblog) project. My intent is to make this look more and more retro with time.\r\n \r\nAnd before you ask, it's availabe [here](http:\/\/git.pedrocx486.club\/pedrocx486\/ngx-retroblog).\r\n \r\nFor more info (maybe), check [The Club](https:\/\/pedrocx486.club)!",
|
||||
"filename": "hello-world",
|
||||
"draft": false
|
||||
}
|
88
src/App.vue
88
src/App.vue
|
@ -1,11 +1,17 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { VueShowdown } from 'vue-showdown';
|
||||
import { downloadFile, parseFilename } from './file-manager.service';
|
||||
import { Archive } from './types';
|
||||
import { downloadFile, parseFilename, loadFromFile } from './file-manager.service';
|
||||
import { Archive, Post } from './types';
|
||||
import { generateTimestamp, parseTimestampToUTC, generatePostObj } from './post-utilities.service';
|
||||
import { getArchive, getPost } from './http-helper.service';
|
||||
|
||||
// TODO:
|
||||
// Modal requesting blog install base url.
|
||||
// Fetch archive from install and show posts in a list.
|
||||
// Allow selection of posts form list and load them.
|
||||
// Save post and archive.
|
||||
|
||||
const postTitle = ref('');
|
||||
const postContent = ref('');
|
||||
const postTimestamp = ref('');
|
||||
|
@ -18,12 +24,33 @@ let timestampUpdateInterval: number;
|
|||
|
||||
const archive = ref<Archive[]>();
|
||||
|
||||
const loadArchive = (baseUrl: string) => {
|
||||
const openLoadModal = (): void => {
|
||||
alert('Not implemented, yet.');
|
||||
}
|
||||
|
||||
const loadArchive = (baseUrl: string): void => {
|
||||
getArchive(baseUrl).then(res => {
|
||||
archive.value = res;
|
||||
});
|
||||
}
|
||||
|
||||
const loadPostFromFile = (file: any): void => {
|
||||
loadFromFile(file.target.files[0]).then((res: Post) => {
|
||||
postTitle.value = res.postTitle;
|
||||
postContent.value = res.postContent;
|
||||
postTimestamp.value = res.timestamp;
|
||||
isDraft.value = res.draft;
|
||||
|
||||
isEditingExisting.value = true;
|
||||
|
||||
clearInterval(timestampUpdateInterval);
|
||||
|
||||
timestampUpdateInterval = setInterval(() => {
|
||||
editedTimestamp.value = generateTimestamp();
|
||||
}, 33);
|
||||
});
|
||||
}
|
||||
|
||||
const loadPost = (baseUrl: string, filename: string): void => {
|
||||
getPost(baseUrl, filename).then(res => {
|
||||
postTitle.value = res.postTitle;
|
||||
|
@ -69,9 +96,6 @@ onMounted(() => {
|
|||
timestampUpdateInterval = setInterval(() => {
|
||||
postTimestamp.value = generateTimestamp();
|
||||
}, 33);
|
||||
|
||||
// For debugging.
|
||||
// loadPost('localhost:3000', 'hello-world');
|
||||
});
|
||||
|
||||
const computedFilename = computed(() => parseFilename(postTitle.value))
|
||||
|
@ -99,7 +123,12 @@ const computedEditedTimestamp = computed(() => parseTimestampToUTC(editedTimesta
|
|||
Created on: {{ computedTimestamp }} <br />
|
||||
<span v-if="isEditingExisting">Edited on: {{ computedEditedTimestamp }} <br /></span>
|
||||
Is it a draft? {{ isDraft? 'Yes': 'No' }}. <br />
|
||||
<button class="btn-primary" @click="savePost">Save (ngx-retroblog format)</button>
|
||||
<div class="footer-buttons">
|
||||
<button class="btn-primary" @click="savePost">Save (ngx-retroblog format)</button>
|
||||
<button class="btn-primary" @click="openLoadModal">Load Post (from Archive)</button> <br />
|
||||
<input type="file" accept=".json" id="file-input" @change="loadPostFromFile($event)">
|
||||
<label for="file-input">Load Post (from File)</label>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -173,15 +202,56 @@ button {
|
|||
border-bottom: solid 1px;
|
||||
}
|
||||
|
||||
.preview > * {
|
||||
.preview>* {
|
||||
max-width: 50vw;
|
||||
line-break: normal;
|
||||
}
|
||||
|
||||
.preview > * p {
|
||||
.preview>* p {
|
||||
padding: 0.2rem;
|
||||
}
|
||||
|
||||
.footer-buttons {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#file-input {
|
||||
width: 0.1px;
|
||||
height: 0.1px;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
#file-input+label {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease-in-out;
|
||||
text-decoration: none;
|
||||
padding: 0.55rem;
|
||||
border: none;
|
||||
box-shadow: #000a 4px 4px;
|
||||
color: white;
|
||||
background-color: rgb(97, 0, 162);
|
||||
position: absolute;
|
||||
font-size: 0.8rem;
|
||||
margin-top: 0.3rem;
|
||||
width: 20.7rem;
|
||||
text-align: center;
|
||||
margin-left: -11.2rem;
|
||||
}
|
||||
|
||||
#file-input:focus+label,
|
||||
#file-input+label:hover {
|
||||
box-shadow: 0px 0px 7px rgb(97, 0, 162);
|
||||
color: white;
|
||||
}
|
||||
|
||||
label {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
@media only screen and (hover: none) {
|
||||
.wrapper {
|
||||
flex-direction: column;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { Post } from "./types";
|
||||
|
||||
export const parseFilename = (titleToFilename: string): string => {
|
||||
if (!titleToFilename) {
|
||||
return 'None yet.'
|
||||
|
@ -9,11 +11,13 @@ export const parseFilename = (titleToFilename: string): string => {
|
|||
titleToFilename = titleToFilename.slice(0, -1);
|
||||
}
|
||||
|
||||
if (titleToFilename.length > 50) { // Limit filename size
|
||||
// Limit filename size
|
||||
if (titleToFilename.length > 50) {
|
||||
titleToFilename = titleToFilename.substring(0, 50);
|
||||
|
||||
if (titleToFilename.includes('-')) {
|
||||
titleToFilename = titleToFilename.substring(0, Math.min(titleToFilename.length, titleToFilename.lastIndexOf('-'))); // Re-trim to avoid cutting a word in half.
|
||||
// Re-trim to avoid cutting a word in half.
|
||||
titleToFilename = titleToFilename.substring(0, Math.min(titleToFilename.length, titleToFilename.lastIndexOf('-')));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,4 +30,48 @@ export const downloadFile = (postObj: any, fileName: string, archiveFile?: boole
|
|||
a.setAttribute('download', archiveFile ? 'archive.json' : fileName);
|
||||
a.setAttribute('href', window.URL.createObjectURL(blob));
|
||||
a.click();
|
||||
}
|
||||
|
||||
|
||||
// Bad code ahead. Sail with caution!
|
||||
export const loadFromFile = (file: any): Promise<Post> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
|
||||
if (!file) {
|
||||
reject('No file!');
|
||||
}
|
||||
|
||||
reader.onload = (event => {
|
||||
const contents: any = (event.target as FileReader).result;
|
||||
|
||||
// Yes, this is extremely hacky.
|
||||
const objCompare: Post = {
|
||||
postTitle: "",
|
||||
timestamp: "",
|
||||
editedTimestamp: "",
|
||||
postContent: "",
|
||||
filename: "",
|
||||
draft: false
|
||||
};
|
||||
|
||||
// And yes, I know this is crazy but it works.
|
||||
try {
|
||||
if (Object.keys(JSON.parse(contents.toString())).toString() === Object.keys(objCompare).toString()) {
|
||||
resolve(JSON.parse(contents.toString()));
|
||||
} else {
|
||||
alert('Invalid file! Are you sure it\'s an Dumblog compatible JSON?');
|
||||
reject('Invalid file!');
|
||||
}
|
||||
} catch (e) {
|
||||
alert('Error loading file! Check the console for more info.');
|
||||
console.log(e);
|
||||
reject('Error loading file!');
|
||||
}
|
||||
});
|
||||
|
||||
reader.onerror = reject;
|
||||
|
||||
reader.readAsText(file);
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue