Skip to content

Commit

Permalink
feat: create post source functionality & component
Browse files Browse the repository at this point in the history
  • Loading branch information
AlejandroAkbal committed May 30, 2023
1 parent f3f70a7 commit 0d38c4b
Show file tree
Hide file tree
Showing 2 changed files with 232 additions and 6 deletions.
12 changes: 6 additions & 6 deletions components/posts/post/Post.vue
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,6 @@
return tags
})
function getHostnameFromUrl(url) {
const source = new URL(url)
return source.hostname
}
</script>
<template>
Expand All @@ -108,6 +102,12 @@
:mediaUrl="mediaFile.file"
/>
<PostSource
v-if="post.media_type === 'image'"
:post-file-url="mediaFile.file"
:post-sources="post.sources"
/>
<DisclosureButton
class="hover:hover-bg-util focus-visible:focus-util group ml-auto flex items-center gap-1 rounded-md px-1.5 py-1"
type="button"
Expand Down
226 changes: 226 additions & 0 deletions components/posts/post/PostSource.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
<script lang="ts" setup>
import { LinkIcon } from '@heroicons/vue/24/outline'
import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue'
import { toast } from 'vue-sonner'
const props = defineProps({
postSources: {
type: Array,
required: true
},
postFileUrl: {
type: String,
required: true
}
})
let [trigger, container] = usePopper({
placement: 'bottom-start',
strategy: 'fixed'
})
const { isPremium } = useUserData()
const imageAnimeRelatedServiceOptions = [
{
title: 'Find with SauceNAO',
link: `https://saucenao.com/search.php?url=${encodeURIComponent(props.postFileUrl)}`
},
{
title: 'Find with ASCII2D',
link: `https://ascii2d.net/search/url/${encodeURIComponent(props.postFileUrl)}`
},
{
title: 'Find with IQDB',
link: `https://iqdb.org/?url=${encodeURIComponent(props.postFileUrl)}`
}
]
const imageRelatedServiceOptions = [
{
title: 'Find with Google',
link: `https://lens.google.com/uploadbyurl?url=${encodeURIComponent(props.postFileUrl)}`
},
// TODO: Fix Yandex
{
title: 'Find with Yandex',
link: `https://yandex.com/images/search?url=${encodeURIComponent(props.postFileUrl)}`
},
{
title: 'Find with Bing',
link: `https://www.bing.com/images/searchbyimage?cbir=sbi&imgurl=${encodeURIComponent(props.postFileUrl)}`
},
{
title: 'Find with TinEye',
link: `https://tineye.com/search/?url=${encodeURIComponent(props.postFileUrl)}`
}
]
function isSourceAnUrl(source: string) {
try {
new URL(source)
return true
} catch {
return false
}
}
function getHostnameFromUrl(url: string) {
const source = new URL(url)
return source.hostname
}
function getFriendlyStringFromHostname(hostname: string) {
const friendlyString = hostname.replace('www.', '')
return friendlyString
}
function openSourceFinder(url: string) {
if (!isPremium) {
toast.error(
'[Premium feature] Find the source of this post with SauceNAO, IQDB, Google, Yandex, Bing, and many more!'
)
return
}
window.open(url, '_blank')
}
</script>

<template>
<Menu
as="div"
class="relative inline-block text-left"
>
<div>
<MenuButton
ref="trigger"
class="hover:hover-bg-util focus-visible:focus-util group flex items-center rounded-md px-1.5 py-1"
>
<span class="sr-only"> Open post source options </span>

<LinkIcon
aria-hidden="true"
class="group-hover:hover-text-util h-5 w-5 text-base-content"
/>
</MenuButton>
</div>

<div ref="container">
<transition
enter-active-class="transition ease-out duration-100"
enter-from-class="transform opacity-0 scale-95"
enter-to-class="transform opacity-100 scale-100"
leave-active-class="transition ease-in duration-75"
leave-from-class="transform opacity-100 scale-100"
leave-to-class="transform opacity-0 scale-95"
>
<MenuItems
class="absolute left-0 z-50 mt-4 w-56 origin-top-right divide-y divide-base-0/20 rounded-md bg-base-1000 shadow-lg ring-1 ring-base-0/20 focus:outline-none"
>
<div
v-if="postSources.length"
class="py-1"
>
<MenuItem
v-for="source of postSources"
:key="source"
v-slot="{ active }"
>
<template v-if="isSourceAnUrl(source)">
<NuxtLink
:class="[active ? 'bg-base-0/20 text-base-content-highlight' : 'text-base-content']"
:href="source"
class="group flex w-full items-center px-4 py-2 text-sm"
target="_blank"
>
<img
:src="`https://www.google.com/s2/favicons?domain=${getHostnameFromUrl(source)}&sz=128`"
alt="Favicon"
class="mr-3 h-5 w-5 flex-shrink-0 rounded"
height="128"
loading="lazy"
width="128"
/>

<span class="truncate">
{{ getFriendlyStringFromHostname(getHostnameFromUrl(source)) }}
</span>
</NuxtLink>
</template>

<template v-else>
{{ source }}
</template>
</MenuItem>
</div>

<!-- Anime services -->
<div class="py-1">
<!-- SauceNAO -->
<MenuItem
v-for="service in imageAnimeRelatedServiceOptions"
v-slot="{ active }"
:disabled="!isPremium"
>
<button
:class="[
active ? 'bg-base-0/20 text-base-content-highlight' : 'text-base-content',
!isPremium ? 'cursor-not-allowed opacity-50' : ''
]"
class="group flex w-full items-center px-4 py-2 text-sm"
type="button"
@click="openSourceFinder(service.link)"
>
<img
:src="`https://www.google.com/s2/favicons?domain=${getHostnameFromUrl(service.link)}&sz=128`"
alt="Favicon"
class="mr-3 h-5 w-5 flex-shrink-0 rounded"
height="128"
loading="lazy"
width="128"
/>

{{ service.title }}
</button>
</MenuItem>
</div>

<!--General services -->
<div class="py-1">
<!-- SauceNAO -->
<MenuItem
v-for="service in imageRelatedServiceOptions"
v-slot="{ active }"
:disabled="!isPremium"
>
<button
:class="[
active ? 'bg-base-0/20 text-base-content-highlight' : 'text-base-content',
!isPremium ? 'cursor-not-allowed opacity-50' : ''
]"
class="group flex w-full items-center px-4 py-2 text-sm"
type="button"
@click="openSourceFinder(service.link)"
>
<img
:src="`https://www.google.com/s2/favicons?domain=${getHostnameFromUrl(service.link)}&sz=128`"
alt="Favicon"
class="mr-3 h-5 w-5 flex-shrink-0 rounded"
height="128"
loading="lazy"
width="128"
/>

{{ service.title }}
</button>
</MenuItem>
</div>
</MenuItems>
</transition>
</div>
</Menu>
</template>

0 comments on commit 0d38c4b

Please sign in to comment.