233 lines
7.4 KiB
TypeScript
233 lines
7.4 KiB
TypeScript
import { Component, createSignal, For, Show } from "solid-js";
|
|
import { click_out_directive } from "./helper";
|
|
|
|
import { DANBOORU_API, booru_post, iqdb_search, saucenao_search, tweet_search } from "./api";
|
|
import { OTISSettingsPage } from "./settings";
|
|
|
|
import iqdb_icon from "./assets/iqdb.ico";
|
|
import sauce_icon from './assets/saucenao.ico';
|
|
import grape_icon from './assets/grapecat.png';
|
|
import danbo_icon from './assets/danbo.png';
|
|
|
|
import "./style.css";
|
|
|
|
// Typescript strips this directive if we don't
|
|
// force call it in this module
|
|
const clickOutside = click_out_directive
|
|
|
|
type GrapeSearchParams = {
|
|
media_url:string[],
|
|
id:string,
|
|
username: string
|
|
}
|
|
|
|
const GrapeCatIcon = ({onClick}:any) => <img onClick={onClick} src={grape_icon} class={"otis-icon"} title="Tweet Search" style={{"width":"20px", "margin-bottom":"2px"}} />
|
|
const IqdbIcon = ({onClick}:any) => <img onClick={onClick} src={iqdb_icon } class={"otis-icon"} title="Danbooru IQDB Search" />
|
|
const SauceIcon = ({onClick}:any) => <img onClick={onClick} src={sauce_icon} class={"otis-icon"} title="SauceNAO Search" style={{"margin-right":"unset"}} />
|
|
const DanboIcon = ({onClick}:any) => <img onClick={onClick} src={danbo_icon} class={"otis-danbo-icon"} />
|
|
|
|
async function main() {
|
|
|
|
// check current page. Switch to Settings page instead
|
|
const settings_page = document.URL.endsWith("/old/settings");
|
|
if ( settings_page ) return OTISSettingsPage()
|
|
|
|
const GrapeSearch : Component<GrapeSearchParams> = ({media_url,id, username}) => {
|
|
|
|
const [showTooltip, setShowTooltip] = createSignal(false);
|
|
const [ShowDanboResult, setShowDanboResult] = createSignal(false);
|
|
const [danboPostID, setPostID ] = createSignal(0);
|
|
const [posts, setPosts] = createSignal<Array<booru_post>>([])
|
|
|
|
const booru_check = async (e:MouseEvent) => {
|
|
//console.debug({id,username})
|
|
//console.debug({event:e});
|
|
|
|
const ico = e.target as HTMLImageElement;
|
|
ico.classList.add('otis-icon-loading');
|
|
ico.classList.remove('otis-icon-noresult');
|
|
|
|
const res = await tweet_search(id,username)
|
|
|
|
ico.classList.remove('otis-icon-loading');
|
|
|
|
if ( res.length == 0 ) {
|
|
ico.classList.add('otis-icon-noresult');
|
|
ico.title = "Can't find any post based on Tweet ID."
|
|
return console.debug("grape: none")
|
|
}
|
|
setPosts(res)
|
|
|
|
if ( res.length === 1 ) setShowDanboResult(true)
|
|
else setShowTooltip(true)
|
|
}
|
|
|
|
const iqdb_check = async (e:MouseEvent) => {
|
|
|
|
const ico = e.target as HTMLImageElement;
|
|
ico.classList.add('otis-icon-loading');
|
|
ico.classList.remove('otis-icon-noresult');
|
|
|
|
// TODO: handle which image to check
|
|
const res = await iqdb_search(media_url[0])
|
|
|
|
ico.classList.remove('otis-icon-loading');
|
|
|
|
if ( res.length == 0 ) {
|
|
ico.classList.add('otis-icon-noresult');
|
|
ico.title = "No relevant image search results from IQDB"
|
|
return console.debug("iqdb: none");
|
|
}
|
|
setPosts(res)
|
|
|
|
if ( res.length === 1 ) setShowDanboResult(true)
|
|
else setShowTooltip(true)
|
|
}
|
|
|
|
const sauce_check = async (e:MouseEvent) => {
|
|
|
|
const ico = e.target as HTMLImageElement;
|
|
ico.classList.add('otis-icon-loading');
|
|
ico.classList.remove('otis-icon-noresult');
|
|
|
|
// TODO: handle which image to check
|
|
const res = await saucenao_search(media_url[0]);
|
|
|
|
ico.classList.remove('otis-icon-loading');
|
|
|
|
if ( res.length == 0 ) {
|
|
ico.classList.add('otis-icon-noresult');
|
|
ico.title = "No relevant image search results from IQDB"
|
|
return console.debug("sauce: none");
|
|
}
|
|
setPosts(res)
|
|
|
|
if ( res.length === 1 ) setShowDanboResult(true)
|
|
else setShowTooltip(true)
|
|
}
|
|
|
|
const DanboPostsResult = () => (
|
|
<>
|
|
<DanboIcon />
|
|
<a
|
|
rel="noopener"
|
|
target="_blank"
|
|
class={"danbo-text-link"}
|
|
href={`${DANBOORU_API}/posts/${posts()[0]?.id}`}
|
|
onmouseover={() => setShowTooltip(true)}>
|
|
{`#${posts()[0]?.id}`}
|
|
</a>
|
|
</>
|
|
)
|
|
|
|
return ( <>
|
|
<span>
|
|
<Show
|
|
when={!ShowDanboResult()}
|
|
fallback={<DanboPostsResult /> }>
|
|
<GrapeCatIcon onClick={booru_check} />
|
|
<IqdbIcon onClick={iqdb_check} />
|
|
<SauceIcon onClick={sauce_check} />
|
|
</Show>
|
|
</span>
|
|
|
|
<div class={"dropdown-menu danbo-more-results"} use:clickOutside={() => setShowTooltip(false)} hidden={!showTooltip()}>
|
|
<For each={posts()}>
|
|
{ (post) =>
|
|
<span
|
|
class={"danbo-item"}
|
|
onClick={() => window.open(`${DANBOORU_API}/posts/${post.id}`,'_blank')}>
|
|
<img src={post.thumbnail} width="80px"/>
|
|
<div>
|
|
<div> #{post.id} </div>
|
|
<div> {post.source_domain} </div>
|
|
<div> { post.img_width >= 0 ? `${post.img_width} x ${post.img_height}` : ""} </div>
|
|
</div>
|
|
</span>
|
|
}
|
|
</For>
|
|
</div>
|
|
</>
|
|
)
|
|
}
|
|
|
|
const callback : MutationCallback = (mutationList, observer) => {
|
|
for (const mutation of mutationList) {
|
|
if (mutation.type !== "childList") return;
|
|
|
|
mutation.addedNodes.forEach( (node) => {
|
|
|
|
const tweet = node as HTMLElement;
|
|
const media = tweet.getElementsByClassName('tweet-media').item(0);
|
|
if ( !media ) return;
|
|
|
|
const imgs = media.getElementsByTagName('img');
|
|
if ( !imgs ) return;
|
|
|
|
const twt_interact = tweet.getElementsByClassName('tweet-interact').item(0);
|
|
const twt_avatar = tweet.getElementsByClassName('tweet-avatar-link').item(0) as HTMLAnchorElement;
|
|
const twt_child_elipsis = twt_interact?.getElementsByClassName('tweet-interact-more').item(0);
|
|
|
|
const tweet_id = tweet.dataset.tweetId || "0"
|
|
const tweet_user = twt_avatar.href.split('/').pop() || "*"
|
|
|
|
//console.debug({tweet_id: tweet_id, media:imgs?.length || 0});
|
|
//console.debug({tweet_user:tweet_user});
|
|
|
|
let img_urls = []
|
|
for ( const img of imgs ) {
|
|
img_urls.push(img.src)
|
|
}
|
|
//console.debug({media:img_urls, id:tweet_id, user:tweet_user});
|
|
const grape = <GrapeSearch media_url={img_urls} id={tweet_id} username={tweet_user}/>;
|
|
twt_child_elipsis?.before(...grape as any);
|
|
|
|
// TODO: Implement OTIS buttons
|
|
})
|
|
}
|
|
};
|
|
|
|
|
|
const tweet_observer = new MutationObserver(callback);
|
|
const timeline = document.getElementById('timeline');
|
|
if ( !timeline ) return;
|
|
const config: MutationObserverInit = {
|
|
subtree:false,
|
|
childList:true,
|
|
attributes:false
|
|
}
|
|
tweet_observer.observe(timeline, config);
|
|
|
|
}
|
|
|
|
|
|
// const init:any = async (limit=0) => {
|
|
// /* We need to do this hack because Old Twitter Extension
|
|
// would just dump entire Twitter's page including userscripts
|
|
// and replace it with OT's custom page instead of replacing
|
|
// Twitter's Document Body page
|
|
// */
|
|
|
|
// if ( limit >= 10 ) return console.error("OTIS: Old Twitter Extension not detected, userscript won't run")
|
|
// const element = document.getElementById('center-cell');
|
|
// if (element) {
|
|
// console.debug("Old Twitter detected");
|
|
// return main();
|
|
// }
|
|
// else {
|
|
// console.debug("can't find it");
|
|
// await delay(1000);
|
|
// return init(limit += 1);
|
|
// }
|
|
// }
|
|
//init()
|
|
|
|
const element = document.getElementById('center-cell');
|
|
if (element) {
|
|
console.debug("Old Twitter Extension detected");
|
|
main();
|
|
}
|
|
else {
|
|
console.debug("can't find it");
|
|
}
|