From 80ddc9133c4f393943c6ce8cd92accb6f28c341c Mon Sep 17 00:00:00 2001 From: Drunkoatmeal Date: Sat, 5 Apr 2025 01:32:22 +0700 Subject: [PATCH] 2025/04/05 Initial base Signed-off-by: Drunkoatmeal --- deno.json | 2 +- src/ArtistFilter.ts | 4 + src/main.ts | 246 +++++++++++++++++++++++++++++++++++--------- src/trashIcon.ts | 3 + vite.config.ts | 13 ++- 5 files changed, 214 insertions(+), 54 deletions(-) create mode 100644 src/ArtistFilter.ts create mode 100644 src/trashIcon.ts diff --git a/deno.json b/deno.json index 0aa7a86..bcc8c8e 100644 --- a/deno.json +++ b/deno.json @@ -1,7 +1,7 @@ { "tasks": { "dev": "deno run -A --node-modules-dir npm:vite build --watch", - "build": "deno run -A --node-modules-dir npm:vite build --mode production", + "build": "deno run -A --node-modules-dir npm:vite build --mode prod", "preview": "deno run -A --node-modules-dir npm:vite preview", "serve": "deno run --allow-net --allow-read jsr:@std/http@1/file-server dist/" }, diff --git a/src/ArtistFilter.ts b/src/ArtistFilter.ts new file mode 100644 index 0000000..c445d60 --- /dev/null +++ b/src/ArtistFilter.ts @@ -0,0 +1,4 @@ +export type Artist = { + name: string, + userid: string +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 9ec8d48..2024e7a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,12 +1,11 @@ import type { AjaxSearchResponse } from './AjaxSearchResponse.ts'; +import type { Artist } from './ArtistFilter.ts'; + +import { TrashIcon } from './trashIcon.ts'; + const { fetch: originalFetch } = unsafeWindow; -type Artist = { - name: string, - userid: string -} - -let ArtistIdList: Artist[] = loadArtistList() +let ArtistIdList: string[] = artist_list_load().map(x => x.userid) unsafeWindow.fetch = async (...args) => { const [resource, config] = args; @@ -14,7 +13,7 @@ unsafeWindow.fetch = async (...args) => { const path : string = resource; - if ( path.startsWith('/ajax/search') ) { + if ( path.startsWith('/ajax/search/') ) { // tampering search results with artist filter const json = () => response @@ -23,15 +22,14 @@ unsafeWindow.fetch = async (...args) => { .then((data:AjaxSearchResponse) => { console.debug({ request: resource, - response: data + response: data, + ArtistIdList }) - // test filter artist id 106029976 - if (path.startsWith('/ajax/search/illustrations')) { data.body.illust.data = data.body.illust.data.filter( ({userId}) => !ArtistIdList.includes(userId) ); } - else if ( path.startsWith('/ajax/search/top')) { + else if ( path.startsWith('/ajax/search/top') || path.startsWith('/ajax/search/artworks')) { data.body.illustManga.data = data.body.illustManga.data.filter( ({userId}) => !ArtistIdList.includes(userId) ); } @@ -43,12 +41,140 @@ unsafeWindow.fetch = async (...args) => { return response; }; - -function loadArtistList() { +function artist_list_load() : Artist[] { return JSON.parse( GM_getValue('ArtistList', '[]') ); } +function artist_list_update(list: Artist[]) { + GM_setValue('ArtistList', JSON.stringify(list)); + ArtistIdList = artist_list_load().map(x => x.userid) +} + +function addArtist(name:string,id:string) { + const list = artist_list_load(); + if ( list.findIndex( x=> x.userid === id ) === -1) { + list.push({name,userid:id}) + artist_list_update(list); + } + console.debug({name,id, list: artist_list_load()}) +} + +function delArtist(userid:string) { + const list = artist_list_load().filter( x => x.userid !== userid ); + artist_list_update(list); +} + function AttachSettings(NavNode:HTMLElement) { + if ( NavNode.pxv_settings_but ) return; + + NavNode.pxv_settings_but = true + + const button = document.createElement('button'); + + button.id = 'pxv-artist-filter'; + button.classList.add('pxv-but','pxv-settings-but'); + button.textContent = 'Artist Filter'; + + + const FilterListElement = () => { + const template = document.createElement('template'); + + let list: Artist[] = artist_list_load(); + //list = list.concat(list).concat(list).concat(list); + + const el_list: Element[] = []; + + for ( const { name, userid } of list) { + + const item = document.createElement('div'); + + const artist_name = document.createElement('a'); + artist_name.target = '_blank' + artist_name.href = `/users/${userid}` + artist_name.text = name; + + const del_but = document.createElement('button'); + del_but.innerHTML = TrashIcon; + del_but.onclick = () => { + delArtist(userid); + item.parentNode?.removeChild(item); + } + + item.append(del_but,artist_name); + el_list.push(item); + } + + template.append(...el_list); + console.debug({template}) + return template.childNodes; + } + + button.onclick = () => { + button.blur(); + + const template = document.createElement('template'); + template.innerHTML = ` +
+ +

Artist Filter List

+
+
One
+
Two
+
Three
+
Four
+
Five
+
+
+ +
+
+
+ `; + + const node = template.content.firstElementChild; + + const grid = node?.getElementsByClassName('pxv-artist-grid').item(0) as HTMLDivElement; + grid.replaceChildren(...FilterListElement()) + + const submit = node?.getElementsByClassName('pxv-but').item(0) as HTMLButtonElement; + submit.onclick = () => document.body.removeChild(node); + + document.body.appendChild(node); + } + NavNode.parentNode.insertBefore(button,NavNode.nextSibling); +} + +function AttachFilterButton(ModalNode:Node) { + if ( ModalNode.pxv_fil_but ) return ; + ModalNode.pxv_fil_but = true; + + const info = ModalNode.parentElement?.querySelector('div a[data-gtm-value]:not(:has(*))') as HTMLAnchorElement; + + console.debug({ + name: info.text, + userid: info.getAttribute('data-gtm-value') + }) + + const button = document.createElement('button'); + + button.id = 'pxv-artist-filter'; + button.classList.add('pxv-but','pxv-modal-filter-but'); + button.textContent = 'Filter'; + button.onclick = () => { + if ( confirm(`Do you want to filter ${info.text}'s posts ?`)) { + const name = info.text; + const userid = info.getAttribute('data-gtm-value') || '-1'; + addArtist(name,userid); + alert(`${info.text} has been added to the filter list.`); + } + } + + ModalNode.appendChild(button); + console.debug({ModalNode}) +} + +function init() { + GM_addStyle(` .pxv-but { color: rgb(245, 245, 245); @@ -62,10 +188,16 @@ function AttachSettings(NavNode:HTMLElement) { font-weight: bold; cursor: pointer; } - .artist-filter-but { + + .pxv-settings-but { margin-left: auto; margin-right: 12px; } + + .pxv-modal-filter-but { + margin: 5px auto; + padding: 5px 24px; + } .pxv-but:focus { outline:none; @@ -92,56 +224,70 @@ function AttachSettings(NavNode:HTMLElement) { width: 400px; height: 400px; background-color: rgb(31, 31, 31); + display:flex; + flex-direction: column; + } + + .pxv-dialog .pxv-but { + display: block; + margin: 10px auto; } .pxv-title { font-size: 16px; text-align: center; } + + .pxv-artist-grid { + display: grid; + grid-template-columns: 1fr 1fr; + max-height: 310px; + overflow-y: scroll; + margin-bottom: auto; + } + .pxv-artist-grid > div { + padding:10px 0; + font-size: 14px; + transition: background-color 0.2s; + display:flex; + } + + .pxv-artist-grid > div:hover { + background-color: #3a3a3a; + } + + .pxv-artist-grid > div > button { + margin: 0 5px; + padding: 0px; + border: medium; + outline: none; + background: transparent; + cursor: pointer; + color: rgb(133, 133, 133); + } + + .pxv-artist-grid > div > a { + flex-grow:1; + margin: auto 0; + } `) - const button = document.createElement('button'); - - button.id = 'pxv-artist-filter'; - button.classList.add('pxv-but','artist-filter-but'); - button.textContent = 'Artist Filter'; - - button.onclick = () => { - button.blur(); - const template = document.createElement('template'); - template.innerHTML = ` -
- -

Artist Filter List

-
- -
-
-
- `; - const node = template.content.firstElementChild; - - const submit = node.getElementsByTagName('button').item(0); - submit.onclick = () => document.body.removeChild(node); - - - document.body.appendChild(node); - } - NavNode.parentNode.insertBefore(button,NavNode.nextSibling); -} - -function init() { - - const pixivgohomeyoudrunk = setInterval(() => { + const attach_settings = setInterval(() => { const navNode = document.getElementsByTagName('nav').item(0); if (navNode){ - clearInterval(pixivgohomeyoudrunk); AttachSettings(navNode) } },1000) + + const pixiv_attach_artist = setInterval( () => { + const profileModalsearch = document.evaluate("//*/div[@open='']", document,null, XPathResult.FIRST_ORDERED_NODE_TYPE); + //const profileModalsearch = document.evaluate("//*/a[contains(text(),'View Profile')]", document,null, XPathResult.FIRST_ORDERED_NODE_TYPE); + if ( profileModalsearch.singleNodeValue ) { + AttachFilterButton(profileModalsearch.singleNodeValue) + } + },1000) } -init() - +init() \ No newline at end of file diff --git a/src/trashIcon.ts b/src/trashIcon.ts new file mode 100644 index 0000000..7ed9da3 --- /dev/null +++ b/src/trashIcon.ts @@ -0,0 +1,3 @@ +export const TrashIcon = `` \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index 660b856..af2d9ee 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -3,7 +3,13 @@ import Userscript from 'vite-userscript-plugin' import { name, description, version } from './package.json'; // https://vite.dev/config/ -export default defineConfig({ +export default defineConfig( ({mode}) => { + + return { + esbuild: { + pure: mode === 'prod' ? ['console.debug'] : [], + drop: mode === 'prod' ? ['console'] : [] + }, plugins: [ Userscript({ entry: 'src/main.ts', @@ -14,12 +20,13 @@ export default defineConfig({ namespace: 'noproompter', match: 'https://www.pixiv.net/*/tags/*', noframes:true, - "run-at": "document-end", + "run-at": "document-start", grant: ['GM.setValue', 'GM.addStyle', 'GM.getValue', 'GM.addElement'] }, server: { port: 3000 } }) - ], + ] + } })