diff --git a/public/s/js/admin.js b/public/s/js/admin.js index d347027..cf95641 100644 --- a/public/s/js/admin.js +++ b/public/s/js/admin.js @@ -113,9 +113,18 @@ const flash = ({ type, msg }) => { const input = document.createElement("input"); input.size = "10"; input.value = ""; + input.setAttribute("list", "testlist"); - let tt; - input.addEventListener("keydown", async e => { + span.insertAdjacentElement("afterbegin", input); + insert.insertAdjacentElement("beforebegin", span); + + input.focus(); + + let tt = null; + let lastInput = ''; + const testList = document.querySelector('#testList'); + + input.addEventListener("keyup", async e => { if(e.key === "Enter") { const tmptag = input.value?.trim(); if(tags.includes(tmptag)) @@ -130,18 +139,42 @@ const flash = ({ type, msg }) => { tags = res.tags.map(t => t.tag); renderTags(res.tags); addtagClick(); + testList.innerText = ""; } else if(e.key === "Escape") { span.parentElement.removeChild(span); + testList.innerText = ""; + } + else { + if(tt != null) + clearTimeout(tt); + + tt = setTimeout(async () => { + globalTimeout = null; + + const tmptag = input.value?.trim(); + + if(tmptag == lastInput || tmptag.length <= 1) + return false; + + testList.innerText = ""; + lastInput = tmptag; + + const res = await (await post("/api/v2/admin/tags/suggest", { + searchString: tmptag + })).json(); + + for(const entry of res.suggestions) { + const option = document.createElement('option'); + option.value = entry.tag; + option.innerText = `tagged ${entry.tagged} times`; + testList.insertAdjacentElement('beforeEnd', option); + }; + }, 500); } return true; }); - span.insertAdjacentElement("afterbegin", input); - insert.insertAdjacentElement("beforebegin", span); - - input.focus(); - input.addEventListener("focusout", ie => { if(input.value.length === 0) input.parentElement.parentElement.removeChild(input.parentElement); diff --git a/src/inc/lib.mjs b/src/inc/lib.mjs index 1258205..0eeca9c 100644 --- a/src/inc/lib.mjs +++ b/src/inc/lib.mjs @@ -98,7 +98,7 @@ export default new class { }; async getTags(itemid) { const tags = await sql("tags_assign") - .select("tags.id", "tags.tag", "user.user") + .select("tags.id", "tags.tag", "tags.normalized", "user.user") .leftJoin("tags", "tags.id", "tags_assign.tag_id") .leftJoin("user", "user.id", "tags_assign.user_id") .where("tags_assign.item_id", itemid) diff --git a/src/inc/routes/apiv2.mjs b/src/inc/routes/apiv2.mjs index 44c1234..5425a2f 100644 --- a/src/inc/routes/apiv2.mjs +++ b/src/inc/routes/apiv2.mjs @@ -163,18 +163,41 @@ export default (router, tpl) => { q = q.andWhere("user_id", req.session.id); const reply = !!(await q); - /*await sql('tags') // delete unused tags - .whereNotIn('id', sql('tags_assign').select('tag_id')) - .andWhereNot('tag', 'sfw') - .andWhereNot('tag', 'nsfw') - .del();*/ - return res.reply({ body: JSON.stringify({ success: reply, tagid: tagid, tags: await lib.getTags(postid) })}); }); + + group.post(/\/admin\/tags\/suggest$/, auth, async (req, res) => { + const reply = { + success: false, + suggestions: {} + }; + + if(req.post?.searchString.length <= 1) { + reply.error = 'too short lol'; + return res.reply({ body: JSON.stringify(reply) }); + } + + try { + const q = await sql('tags') + .select('tag', sql.raw('count(tags_assign.tag_id) as tagged')) + .leftJoin('tags_assign', 'tags_assign.tag_id', 'tags.id') + .whereRaw("normalized like '%' || slugify(?) || '%'", [ req.post.searchString ]) + .groupBy('tags.id') + .orderBy('tagged', 'desc') + .limit(15); + reply.success = true; + reply.suggestions = q; + } catch(err) { + reply.success = false; + reply.error = err.msg; + } + + return res.reply({ body: JSON.stringify(reply) }); + }); group.get(/\/admin\/tags\/get\/\d+$/, auth, async (req, res) => { return res.reply({ body: JSON.stringify({