Browse Source
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>feature/brute_force_protection
12 changed files with 246 additions and 79 deletions
@ -0,0 +1,83 @@
@@ -0,0 +1,83 @@
|
||||
<script> |
||||
import { onMount } from 'svelte'; |
||||
import { useTinyRouter } from 'svelte-tiny-router'; |
||||
|
||||
import { api } from '../../urls.svelte.js'; |
||||
import { t } from '../../translations.svelte.js'; |
||||
import { user } from '../../user.svelte.js'; |
||||
|
||||
import List from './List.svelte'; |
||||
|
||||
let authors = $state({}); |
||||
let error = $state(null); |
||||
let loader = { |
||||
offset : 0, |
||||
limit : 5, |
||||
active : true |
||||
} |
||||
let note = $state({source:null,rendered:null}); |
||||
let notes = $state(null); |
||||
let { |
||||
module = null, |
||||
entity_id = null |
||||
} = $props(); |
||||
|
||||
|
||||
|
||||
|
||||
async function loadNotes(){ |
||||
const url = api(`notes?offset=${loader.offset}&limit=${loader.limit}`); |
||||
|
||||
const resp = await fetch(url,{credentials:'include'}); |
||||
if (resp.ok){ |
||||
const data = await resp.json(); |
||||
if (!notes) notes = []; |
||||
notes.push(...Object.values(data.notes).sort((a, b) => b.id - a.id)); |
||||
authors = {...authors, ...data.authors}; |
||||
loader.offset += loader.limit; |
||||
loader.active = false; |
||||
error = null; |
||||
if (Object.keys(data.notes).length) onscroll(null); // when notes were received, check whether they fill up the page |
||||
|
||||
} else { |
||||
error = await resp.text(); |
||||
} |
||||
} |
||||
|
||||
async function saveNote(){ |
||||
const url = api(`notes/${module}/${entity_id}`); |
||||
const resp = await fetch(url,{ |
||||
credentials : 'include', |
||||
method : 'POST', |
||||
body : note.source |
||||
}); |
||||
if (resp.ok){ |
||||
let newNote = await resp.json(); |
||||
authors[user.id] = user; |
||||
notes[newNote.id] = newNote; |
||||
note = {source:'',rendered:''}; |
||||
error = null; |
||||
return true; |
||||
} else { |
||||
error = await resp.text(); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
function onscroll(ev){ |
||||
if (window.innerHeight + window.scrollY >= document.body.offsetHeight && !loader.active) { |
||||
loader.active = true; |
||||
loadNotes(); |
||||
} |
||||
} |
||||
|
||||
loadNotes(loadNotes) |
||||
</script> |
||||
|
||||
<svelte:window {onscroll} /> |
||||
{#if error} |
||||
<span class="error">{error}</span> |
||||
{/if} |
||||
<List {notes} /> |
||||
@ -0,0 +1,99 @@
@@ -0,0 +1,99 @@
|
||||
<script> |
||||
import { onMount } from 'svelte'; |
||||
import { useTinyRouter } from 'svelte-tiny-router'; |
||||
|
||||
import { api } from '../../urls.svelte.js'; |
||||
import { t } from '../../translations.svelte.js'; |
||||
import { user } from '../../user.svelte.js'; |
||||
|
||||
import Editor from '../../Components/MarkdownEditor.svelte'; |
||||
import List from './List.svelte'; |
||||
|
||||
let authors = $state(null); |
||||
let error = $state(null); |
||||
let note = $state({source:null,rendered:null}); |
||||
let notes = $state(null); |
||||
const router = useTinyRouter(); |
||||
let { |
||||
module = null, |
||||
entity_id = null |
||||
} = $props(); |
||||
|
||||
async function drop(nid){ |
||||
if (!confirm(t('confirm_delete',{element:t('note')}))) return; |
||||
const url = api(`notes/${nid}`); |
||||
const resp = await fetch(url,{ |
||||
credentials : 'include', |
||||
method : 'DELETE' |
||||
}); |
||||
if (resp.ok) { |
||||
error = false; |
||||
delete notes[nid]; |
||||
} else { |
||||
error = await resp.text(); |
||||
} |
||||
} |
||||
|
||||
function goToEntity(n){ |
||||
router.navigate(`/${n.module}/${n.entity_id}/view`); |
||||
} |
||||
|
||||
async function load(){ |
||||
const url = api(`notes/${module}/${entity_id}`); |
||||
const resp = await fetch(url,{credentials:'include'}); |
||||
if (resp.ok){ |
||||
const data = await resp.json(); |
||||
notes = Object.values(data.notes).sort((a, b) => a.id - b.id); |
||||
authors = data.authors; |
||||
} else { |
||||
error = await resp.text(); |
||||
} |
||||
} |
||||
|
||||
async function saveNote(){ |
||||
const url = api(`notes/${module}/${entity_id}`); |
||||
const resp = await fetch(url,{ |
||||
credentials : 'include', |
||||
method : 'POST', |
||||
body : note.source |
||||
}); |
||||
if (resp.ok){ |
||||
let newNote = await resp.json(); |
||||
authors[user.id] = user; |
||||
notes.push(newNote); |
||||
note = {source:'',rendered:''}; |
||||
error = null; |
||||
return true; |
||||
} else { |
||||
error = await resp.text(); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
async function update(nid,src){ |
||||
const url = api(`notes/${nid}`); |
||||
const resp = await fetch(url,{ |
||||
credentials : 'include', |
||||
method : 'PATCH', |
||||
body : src |
||||
}); |
||||
if (resp.ok) { |
||||
error = false; |
||||
return true; |
||||
} else { |
||||
error = await resp.text(); |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
onMount(load); |
||||
</script> |
||||
|
||||
{#if error} |
||||
<span class="error">{error}</span> |
||||
{/if} |
||||
<List {authors} {module} {notes} /> |
||||
<div class="editor"> |
||||
<Editor simple={true} bind:value={note} onSet={saveNote} /> |
||||
<button onclick={saveNote}>{t('save_object',{object:t('note')})}</button> |
||||
</div> |
||||
Loading…
Reference in new issue