172 lines
4.6 KiB
Svelte
172 lines
4.6 KiB
Svelte
<script>
|
|
import { onMount } from 'svelte';
|
|
import { useTinyRouter } from 'svelte-tiny-router';
|
|
import { api, get, patch } from '../../urls.svelte';
|
|
import { error, yikes } from '../../warn.svelte';
|
|
import { t } from '../../translations.svelte';
|
|
|
|
import Detail from './EasyListDetail.svelte';
|
|
|
|
let { tag } = $props();
|
|
let filter = $state(null);
|
|
let highlight = $state(null);
|
|
let search = $derived(filter ? filter.toLowerCase() : null);
|
|
//let input;
|
|
let tasks = $state(null);
|
|
let router = useTinyRouter();
|
|
let sorted = $derived(tasks ? Object.values(tasks).filter(noNoIndex).sort(byName) : null);
|
|
|
|
let start = 0;
|
|
let x = 0;
|
|
let y = 0;
|
|
|
|
function byName(a,b){
|
|
return a.name.localeCompare(b.name);
|
|
}
|
|
|
|
function extend(e,task){
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
highlight = task;
|
|
return false;
|
|
}
|
|
|
|
function getTask(evt){
|
|
var link = evt.target;
|
|
var id = link.getAttribute('task_id');
|
|
return tasks[id];
|
|
}
|
|
|
|
function goTag(e,newTag){
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
router.navigate(`/tags/easylist/${newTag}`);
|
|
tag = newTag;
|
|
load();
|
|
}
|
|
|
|
function ignore(evt){
|
|
evt.preventDefault();
|
|
evt.stopPropagation();
|
|
return false;
|
|
}
|
|
|
|
async function load(){
|
|
const url = api(`task/tagged/${tag}`);
|
|
const res = await get(url);
|
|
if (res.ok){
|
|
yikes();
|
|
tasks = await res.json();
|
|
//input.focus();
|
|
} else error(res);
|
|
}
|
|
|
|
function match(task){
|
|
if (!search) return true;
|
|
if (task.name.toLowerCase().includes(search)) return true;
|
|
if (task.tags){
|
|
for (let tag of task.tags){
|
|
if (tag.toLowerCase().includes(search)) return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function measured(evt,duration,d){
|
|
if (d > 10) return;
|
|
if (duration < 500){
|
|
onclick(evt);
|
|
} else {
|
|
oncontextmenu(evt);
|
|
}
|
|
}
|
|
|
|
function noNoIndex(task){
|
|
return !task.no_index;
|
|
}
|
|
|
|
function onclick(evt) {
|
|
ignore(evt);
|
|
let task = getTask(evt);
|
|
if (task.status <= 20) { // open
|
|
update(task,60);
|
|
} else update(task,20);
|
|
return false;
|
|
}
|
|
|
|
function oncontextmenu(evt) {
|
|
ignore(evt);
|
|
highlight = getTask(evt);
|
|
return false;
|
|
}
|
|
|
|
function ontouchstart(evt){
|
|
ignore(evt);
|
|
start = evt.timeStamp;
|
|
x = evt.touches[0].clientX;
|
|
y = evt.touches[0].clientY;
|
|
return false;
|
|
}
|
|
|
|
function ontouchend(evt){
|
|
ignore(evt);
|
|
let d = Math.abs(x - evt.changedTouches[0].clientX) + Math.abs(y - evt.changedTouches[0].clientY);
|
|
measured(evt, evt.timeStamp - start, d);
|
|
return false;
|
|
}
|
|
|
|
|
|
async function update(task,newState){
|
|
highlight = null;
|
|
const url = api(`task/${task.id}`);
|
|
const res = await patch(url,{status:newState});
|
|
if (res.ok){
|
|
task.status = newState;
|
|
yikes();
|
|
// filter = null; // not sure what is better, resetting or keeping
|
|
} else error(res);
|
|
}
|
|
|
|
onMount(load);
|
|
</script>
|
|
|
|
<h2>{t('tasks_for_tag',{tag:decodeURI(tag)})}</h2>
|
|
|
|
<div class="easylist">
|
|
<fieldset class="open">
|
|
<legend>{t('state_open')}</legend>
|
|
{#if sorted}
|
|
{#each sorted as task}
|
|
{#if task.status == 20 && match(task)}
|
|
<div href={`/task/${task.id}/view`} title={task.description.source} task_id={task.id} {onclick} {oncontextmenu} {ontouchstart} {ontouchend} onmousedown={ontouchstart} onmouseup={ontouchend} >
|
|
{task.name}
|
|
</div>
|
|
{#if highlight == task}
|
|
<Detail task={task} goTag={goTag} />
|
|
{/if}
|
|
{/if}
|
|
{/each}
|
|
{/if}
|
|
</fieldset>
|
|
|
|
<fieldset class="closed">
|
|
<legend>{t('state_complete')}</legend>
|
|
{#if sorted}
|
|
{#each sorted as task}
|
|
{#if task.status > 20 && match(task)}
|
|
<div href={`/task/${task.id}/view`} title={task.description.source} task_id={task.id} {onclick} {oncontextmenu} {ontouchstart} {ontouchend} onmousedown={ontouchstart} onmouseup={ontouchend} >
|
|
{task.name}
|
|
</div>
|
|
{#if highlight == task}
|
|
<Detail task={task} goTag={goTag} />
|
|
{/if}
|
|
{/if}
|
|
{/each}
|
|
{/if}
|
|
</fieldset>
|
|
|
|
<div class="filter">
|
|
{t('filter')}: <input type="text" bind:value={filter} /> <!-- bind:this={input} -->
|
|
</div>
|
|
</div>
|