working (feature complete) news_fetch
This commit is contained in:
@@ -2,10 +2,15 @@
|
||||
import PDFView from './PDFView.svelte';
|
||||
import ArticleStatus from './ArticleStatus.svelte';
|
||||
import ArticleOperations from './ArticleOperations.svelte';
|
||||
|
||||
import Toast from './Toast.svelte';
|
||||
|
||||
|
||||
let current_id = 0;
|
||||
|
||||
const updateInterface = (async () => {
|
||||
let interfaceState = updateInterface()
|
||||
|
||||
async function updateInterface () {
|
||||
let url = '';
|
||||
if (current_id == 0) {
|
||||
url = '/api/article/first';
|
||||
@@ -19,12 +24,14 @@
|
||||
const article_response = await fetch(article_url);
|
||||
const article_data = await article_response.json();
|
||||
return article_data;
|
||||
})()
|
||||
|
||||
|
||||
}
|
||||
|
||||
function triggerUpdate () {
|
||||
interfaceState = updateInterface();
|
||||
}
|
||||
</script>
|
||||
|
||||
{#await updateInterface}
|
||||
{#await interfaceState}
|
||||
...
|
||||
{:then article_data}
|
||||
<div class="flex w-full h-screen gap-5 p-5">
|
||||
@@ -33,7 +40,9 @@
|
||||
<div class="w-2/5">
|
||||
<ArticleStatus article_data={article_data}/>
|
||||
<div class="divider divider-vertical"></div>
|
||||
<ArticleOperations article_data={article_data}/>
|
||||
<ArticleOperations article_data={article_data} callback={triggerUpdate}/>
|
||||
</div>
|
||||
</div>
|
||||
{/await}
|
||||
|
||||
<Toast/>
|
@@ -1,55 +1,85 @@
|
||||
<script>
|
||||
import {fade} from 'svelte/transition';
|
||||
|
||||
export let article_data;
|
||||
export let callback;
|
||||
window.focus()
|
||||
import { addToast } from './Toast.svelte';
|
||||
|
||||
const actions = [
|
||||
{name: 'Mark as good (and skip to next)', kbd: 'A'},
|
||||
{name: 'Mark as bad (and skip to next)', kbd: 'B'},
|
||||
{name: 'Upload related file', kbd: 'R'},
|
||||
{name: 'Skip', kbd: 'ctrl'},
|
||||
{name: 'Upload related file', kbd: 'R', comment: "can be used multiple times"},
|
||||
{name: 'Skip', kbd: 'S'},
|
||||
]
|
||||
|
||||
const toast_states = {
|
||||
'success' : {class: 'alert-success', text: 'Article updated successfully'},
|
||||
'error' : {class: 'alert-error', text: 'Article update failed'},
|
||||
}
|
||||
let toast_state = {};
|
||||
let toast_visible = false;
|
||||
|
||||
|
||||
let fileInput = document.createElement('input');
|
||||
fileInput.type = 'file';
|
||||
|
||||
fileInput.onchange = e => {
|
||||
let result = (async () => {
|
||||
uploadRelatedFile(e.target.files[0]);
|
||||
})()
|
||||
}
|
||||
|
||||
|
||||
function onKeyDown(e) {apiAction(e.key)}
|
||||
function apiAction(key) {
|
||||
if (actions.map(d => d.kbd.toLowerCase()).includes(key.toLowerCase())){ // ignore other keypresses
|
||||
|
||||
const updateArticle = (async() => {
|
||||
const response = await fetch('/api/article/' + article_data.id + '/set', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
'action': key.toLowerCase(),
|
||||
})
|
||||
})
|
||||
const success = response.status == 200;
|
||||
|
||||
if (success){
|
||||
showToast('success');
|
||||
} else {
|
||||
showToast('error');
|
||||
}
|
||||
let success
|
||||
|
||||
if (key.toLowerCase() == "s") {
|
||||
addToast('success', "Article skipped")
|
||||
callback()
|
||||
return
|
||||
} else if (key.toLowerCase() == "r") {
|
||||
fileInput.click() // this will trigger a change in fileInput,
|
||||
return
|
||||
} else {
|
||||
const response = await fetch('/api/article/' + article_data.id + '/set', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
'action': key.toLowerCase(),
|
||||
})
|
||||
})
|
||||
success = response.status == 200
|
||||
}
|
||||
if (success){
|
||||
addToast('success')
|
||||
callback()
|
||||
} else {
|
||||
addToast('error')
|
||||
}
|
||||
})()
|
||||
}
|
||||
}
|
||||
|
||||
function showToast(state){
|
||||
toast_visible = true;
|
||||
toast_state = toast_states[state];
|
||||
setTimeout(() => {
|
||||
toast_visible = false;
|
||||
}, 1000)
|
||||
|
||||
|
||||
async function uploadRelatedFile(file) {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
|
||||
const response = await fetch('/api/article/' + article_data.id + '/set', {
|
||||
method: 'POST',
|
||||
body : formData,
|
||||
})
|
||||
|
||||
const success = response.status == 200;
|
||||
if (success){
|
||||
const data = await response.json()
|
||||
let fname = data.file_path
|
||||
addToast('success', "File uploaded as " + fname)
|
||||
} else {
|
||||
addToast('error', "File upload failed")
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@@ -58,21 +88,22 @@
|
||||
<h2 class="card-title">Your options: (click on action or use keyboard)</h2>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table w-full table-compact">
|
||||
<!-- head -->
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Action</th>
|
||||
<th>Keyboard shortcut</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{#each actions as action}
|
||||
|
||||
{#each actions as action}
|
||||
<tr>
|
||||
<td><button on:click={() => apiAction(action.kbd)}>{ action.name }</button></td>
|
||||
<td><kbd class="kbd">{ action.kbd }</kbd></td>
|
||||
<td><kbd class="kbd">
|
||||
{ action.kbd }</kbd>
|
||||
{#if action.comment}({ action.comment }){/if}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -80,14 +111,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Listen for keypresses -->
|
||||
<svelte:window on:keydown|preventDefault={onKeyDown} />
|
||||
|
||||
{#if toast_visible}
|
||||
<div class="toast" transition:fade>
|
||||
<div class="alert { toast_state.class }">
|
||||
<div>
|
||||
<span>{ toast_state.text }.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
@@ -2,13 +2,21 @@
|
||||
export let article_data;
|
||||
const status_items = [
|
||||
{name: 'Title', value: article_data.title},
|
||||
{name: 'Url', value: article_data.article_url},
|
||||
{name: 'Source', value: article_data.source_name},
|
||||
{name: 'Filename', value: article_data.file_name},
|
||||
{name: 'Location', value: article_data.save_path},
|
||||
{name: 'Language', value: article_data.language},
|
||||
{name: 'Authors', value: article_data.authors},
|
||||
{name: "Related", value: article_data.related},
|
||||
]
|
||||
</script>
|
||||
|
||||
<style>
|
||||
a {
|
||||
word-break: break-all;
|
||||
}
|
||||
</style>
|
||||
<div class="card bg-neutral-300 shadow-xl overflow-x-auto">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">Article overview:</h2>
|
||||
@@ -23,16 +31,18 @@
|
||||
{#each status_items as item}
|
||||
<tr>
|
||||
<td>{ item.name }</td>
|
||||
<!-- <td>Quality Control Specialist</td> -->
|
||||
{#if item.value != ""}
|
||||
{#if item.name == "Url"}
|
||||
<td class='bg-emerald-200'><a href="{ item.value }">{ item.value }</a></td>
|
||||
{:else}
|
||||
<td class='bg-emerald-200' style="white-space: normal; width:70%">{ item.value }</td>
|
||||
{/if}
|
||||
{:else}
|
||||
<td class='bg-red-200'>{ item.value }</td>
|
||||
<td class='bg-red-200'>not set</td>
|
||||
{/if}
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
34
news_check/client/src/Toast.svelte
Normal file
34
news_check/client/src/Toast.svelte
Normal file
@@ -0,0 +1,34 @@
|
||||
<script context="module">
|
||||
import {fade} from 'svelte/transition';
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
|
||||
let toasts = writable([])
|
||||
|
||||
export function addToast (type, message="") {
|
||||
if (message == "") {
|
||||
message = toast_states[type]["text"]
|
||||
}
|
||||
toasts.update((all) => [{"class" : toast_states[type]["class"], "text": message}, ...all]);
|
||||
toasts = toasts;
|
||||
setTimeout(() => {
|
||||
toasts.update((all) => all.slice(0, -1));
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
|
||||
|
||||
const toast_states = {
|
||||
'success' : {class: 'alert-success', text: 'Article updated successfully'},
|
||||
'error' : {class: 'alert-error', text: 'Article update failed'},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="toast">
|
||||
{#each $toasts as toast}
|
||||
<div class="alert { toast.class }" transition:fade>
|
||||
<div> <span>{ toast.text }.</span> </div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
@@ -2,9 +2,6 @@ import App from './App.svelte';
|
||||
|
||||
const app = new App({
|
||||
target: document.body,
|
||||
props: {
|
||||
name: 'world'
|
||||
}
|
||||
});
|
||||
|
||||
export default app;
|
Reference in New Issue
Block a user