Ability to load archive, remote posts, save archives and code improvements.

This commit is contained in:
pedrocx486 2023-02-12 04:11:26 -03:00
parent eac0aa15eb
commit 14694f7082
4 changed files with 163 additions and 26 deletions

View file

@ -7,10 +7,13 @@ import { generateTimestamp, parseTimestampToUTC, generatePostObj } from './post-
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.
// Ability to delete posts from the archive.
let timestampUpdateInterval: number;
const blogInstallLocation = ref('');
const archive = ref<Archive[]>();
const postTitle = ref('');
const postContent = ref('');
@ -20,21 +23,48 @@ const isDraft = ref(false);
const isEditingExisting = ref(false);
let timestampUpdateInterval: number;
const onlyLoadArchive = ref(false);
const archive = ref<Archive[]>();
const openLoadModal = (archiveOnly = false): void => {
onlyLoadArchive.value = archiveOnly;
(document.getElementById('dialog') as HTMLDialogElement).showModal();
}
const openLoadModal = (): void => {
alert('Not implemented, yet.');
const closeLoadModal = (): void => {
(document.getElementById('dialog') as HTMLDialogElement).close();
}
const reset = (): void => {
archive.value = undefined;
postTitle.value = '';
postContent.value = '';
postTimestamp.value = '';
editedTimestamp.value = '';
isDraft.value = false;
isEditingExisting.value = false;
editedTimestamp.value = '';
clearInterval(timestampUpdateInterval);
timestampUpdateInterval = setInterval(() => {
postTimestamp.value = generateTimestamp();
}, 33);
}
const loadArchive = (baseUrl: string): void => {
getArchive(baseUrl).then(res => {
archive.value = res;
if (onlyLoadArchive.value) {
onlyLoadArchive.value = false;
closeLoadModal();
}
});
}
const loadPostFromFile = (file: any): void => {
archive.value = undefined;
loadFromFile(file.target.files[0]).then((res: Post) => {
postTitle.value = res.postTitle;
postContent.value = res.postContent;
@ -65,10 +95,12 @@ const loadPost = (baseUrl: string, filename: string): void => {
timestampUpdateInterval = setInterval(() => {
editedTimestamp.value = generateTimestamp();
}, 33);
closeLoadModal();
});
}
const savePost = (): void => {
const savePost = (saveArchive?: boolean): void => {
if (!postTitle.value) {
postTitle.value = 'No title.';
}
@ -90,6 +122,17 @@ const savePost = (): void => {
),
computedFilename.value
);
if (saveArchive) {
if (!archive.value?.filter(post => post.filename === computedFilename.value) && !isDraft) {
archive.value?.push({
postTitle: postTitle.value,
timestamp: postTimestamp.value,
filename: computedFilename.value
});
}
downloadFile(archive.value, 'archive');
}
}
onMounted(() => {
@ -105,6 +148,37 @@ const computedEditedTimestamp = computed(() => parseTimestampToUTC(editedTimesta
<template>
<div class="wrapper">
<dialog id="dialog">
<div class="dialog-header">
<div v-if="onlyLoadArchive">
<h4>Load Archive...</h4>
</div>
<div v-else>
<h4>Load post from Archive...</h4>
</div>
<div class="close-btn">
<button class="btn-primary" @click="closeLoadModal()">
X
</button>
</div>
</div>
<div v-if="!archive">
Blog Location:
https://
<input type="text" v-model="blogInstallLocation" placeholder="pedrocx486.club/blog" />
/assets/posts/archive.json
<br />
<button class="btn-primary btn-wide" :disabled="!blogInstallLocation" @click="loadArchive(blogInstallLocation)">
Load Archive
</button>
</div>
<div v-for="post in archive" v-if="!onlyLoadArchive">
<button class="btn-secondary btn-wide" @click="loadPost(blogInstallLocation, post.filename)">
{{ post.postTitle }}
</button>
</div>
</dialog>
<div class="editor-area">
<div class="title-area">
<input class="title" type="text" placeholder="Post title..." v-model="postTitle" />
@ -115,17 +189,22 @@ const computedEditedTimestamp = computed(() => parseTimestampToUTC(editedTimesta
</div>
<div class="preview">
<p class="preview-title">&nbsp; Preview: </p>
<VueShowdown v-bind:markdown="postContent" flavor="github" :options="{ emoji: true }" tag="span" />
<VueShowdown :markdown="postContent" flavor="github" :options="{ emoji: true }" tag="span" />
</div>
</div>
<div class="footer">
Filename: {{ computedFilename }} <br />
Filename: {{ computedFilename }}<span v-if="postTitle">.json</span> <br />
Created on: {{ computedTimestamp }} <br />
<span v-if="isEditingExisting">Edited on: {{ computedEditedTimestamp }} <br /></span>
Is it a draft? {{ isDraft? 'Yes': 'No' }}. <br />
<div class="footer-buttons">
<button class="btn-primary" @click="savePost">Save (ngx-retroblog format)</button> &nbsp;
<button class="btn-primary" @click="openLoadModal">Load Post (from Archive)</button> &nbsp; <br />
<button class="btn-tertiary" @click="reset()">Reset Editor</button> &nbsp;
<button class="btn-primary" @click="openLoadModal(true)">Load Archive</button> &nbsp;
<button class="btn-primary" @click="openLoadModal()">Load Post (from Archive)</button> &nbsp;
<button class="btn-primary" @click="savePost()">Save Post</button> &nbsp;
<button class="btn-primary" @click="savePost(true)" :disabled="!archive || isDraft">Save Post & Archive</button>
&nbsp;
<br />
<input type="file" accept=".json" id="file-input" @change="loadPostFromFile($event)">
<label for="file-input">Load Post (from File)</label>
</div>
@ -153,12 +232,65 @@ button {
background-color: rgb(97, 0, 162);
color: white;
font-size: .8rem;
margin-top: 0.5rem;
}
.btn-primary[disabled] {
background-color: rgb(144, 144, 144);
color: rgb(75, 75, 75);
font-size: .8rem;
}
.btn-primary:hover {
box-shadow: 0px 0px 7px rgb(97, 0, 162);
}
.btn-secondary {
background-color: rgb(0, 241, 221);
color: rgb(78, 78, 78);
font-size: .8rem;
}
.btn-secondary:hover {
box-shadow: 0px 0px 7px rgb(0, 241, 221);
}
.btn-tertiary {
background-color: rgb(241, 8, 0);
color: rgb(233, 233, 233);
font-size: .8rem;
}
.btn-tertiary:hover {
box-shadow: 0px 0px 7px rgb(241, 8, 0);
}
.close-btn {
text-align: end;
margin-bottom: 0.5rem;
}
.btn-wide {
min-width: -webkit-fill-available;
margin-top: 0.5rem;
}
#dialog {
max-width: 90vw;
}
.dialog-header {
display: flex;
justify-content: space-between;
}
.dialog-header>* h4 {
padding: 0;
margin: 0;
margin-top: .5rem;
}
.wrapper {
display: flex;
grid-gap: 1rem;
@ -213,6 +345,7 @@ button {
.footer-buttons {
text-align: center;
padding-bottom: 3rem;
}
#file-input {
@ -236,10 +369,11 @@ button {
background-color: rgb(97, 0, 162);
position: absolute;
font-size: 0.8rem;
margin-top: 0.3rem;
margin-top: 0.5rem;
width: 20.7rem;
text-align: center;
margin-left: -11.2rem;
margin-bottom: 1rem;
}
#file-input:focus+label,

View file

@ -21,13 +21,13 @@ export const parseFilename = (titleToFilename: string): string => {
}
}
return titleToFilename + '.json';
return titleToFilename;
}
export const downloadFile = (postObj: any, fileName: string, archiveFile?: boolean): void => {
const blob = new Blob([JSON.stringify(postObj, null, 2)], { type: 'text/plain;charset=utf-8;' });
export const downloadFile = (dataPbj: any, fileName: string): void => {
const blob = new Blob([JSON.stringify(dataPbj, null, 2)], { type: 'text/plain;charset=utf-8;' });
const a = document.createElement('a');
a.setAttribute('download', archiveFile ? 'archive.json' : fileName);
a.setAttribute('download', `${fileName}.json`);
a.setAttribute('href', window.URL.createObjectURL(blob));
a.click();
}

View file

@ -17,15 +17,18 @@ httpService.interceptors.response.use(
},
);
export async function getArchive(baseUrl: string) {
return await httpService.get<Archive[]>(`https://${baseUrl}/assets/posts/archive.json`).then((res) => {
export async function getArchive(baseUrl: string): Promise<Archive[]> {
return await httpService.get<Archive[]>(`https://${baseUrl}/assets/posts/archive.json`).then((res: any) => {
return res.data;
}).catch(err => {
alert(`Failed to load Archive! We've got a ${err.message}!\nAre you connected to the internet or is the location correct?`)
});
}
export async function getPost(baseUrl: string, post: string) {
return await httpService.get<Post>(`http://${baseUrl}/assets/posts/${post}.json`).then((res) => {
export async function getPost(baseUrl: string, post: string): Promise<Post> {
return await httpService.get<Post>(`https://${baseUrl}/assets/posts/${post}.json`).then((res: any) => {
return res.data;
}).catch(err => {
alert(`Failed to load Post! We've got a ${err.message}!\nAre you connected to the internet?`)
});
}

View file

@ -7,8 +7,8 @@ export type Post = {
draft: boolean
}
export type Archive = {
postTitle: string,
timestamp: string,
export type Archive = {
postTitle: string,
timestamp: string,
filename: string
}