Skip to content

Commit

Permalink
✨ Add new episode view
Browse files Browse the repository at this point in the history
  • Loading branch information
Xen0Xys committed Jun 15, 2024
1 parent f741149 commit d19e2fb
Show file tree
Hide file tree
Showing 12 changed files with 193 additions and 17 deletions.
14 changes: 14 additions & 0 deletions assets/css/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,18 @@
p.muted{
@apply text-sm text-muted-foreground;
}

/*::-webkit-scrollbar {*/
/* width: 10px;*/
/*}*/
/*::-webkit-scrollbar-track {*/
/* background: #f1f1f1;*/
/*}*/
/*::-webkit-scrollbar-thumb {*/
/* background: #888;*/
/* border-radius: 5px;*/
/*}*/
/*::-webkit-scrollbar-thumb:hover {*/
/* background: #555;*/
/*}*/
}
29 changes: 29 additions & 0 deletions components/ui/scroll-area/ScrollArea.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import {
ScrollAreaCorner,
ScrollAreaRoot,
type ScrollAreaRootProps,
ScrollAreaViewport,
} from 'radix-vue'
import ScrollBar from './ScrollBar.vue'
import { cn } from '@/lib/utils'
const props = defineProps<ScrollAreaRootProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script>

<template>
<ScrollAreaRoot v-bind="delegatedProps" :class="cn('relative overflow-hidden', props.class)">
<ScrollAreaViewport class="h-full w-full rounded-[inherit]">
<slot />
</ScrollAreaViewport>
<ScrollBar />
<ScrollAreaCorner />
</ScrollAreaRoot>
</template>
30 changes: 30 additions & 0 deletions components/ui/scroll-area/ScrollBar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { ScrollAreaScrollbar, type ScrollAreaScrollbarProps, ScrollAreaThumb } from 'radix-vue'
import { cn } from '@/lib/utils'
const props = withDefaults(defineProps<ScrollAreaScrollbarProps & { class?: HTMLAttributes['class'] }>(), {
orientation: 'vertical',
})
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script>

<template>
<ScrollAreaScrollbar
v-bind="delegatedProps"
:class="
cn('flex touch-none select-none transition-colors',
orientation === 'vertical'
&& 'h-full w-2.5 border-l border-l-transparent p-px',
orientation === 'horizontal'
&& 'h-2.5 flex-col border-t border-t-transparent p-px',
props.class)"
>
<ScrollAreaThumb class="relative flex-1 rounded-full bg-border" />
</ScrollAreaScrollbar>
</template>
2 changes: 2 additions & 0 deletions components/ui/scroll-area/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as ScrollArea } from './ScrollArea.vue'
export { default as ScrollBar } from './ScrollBar.vue'
35 changes: 35 additions & 0 deletions components/webtoons/episodes/EpisodeItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script setup lang="ts">
import {sumToImageUrl} from "~/utils/api";
import {Collapsible} from "~/components/ui/collapsible";
import {Badge} from "~/components/ui/badge";
const {episode} = defineProps<{
episode: any,
}>();
</script>

<template>
<Collapsible>
<div id="main" class="flex items-center cursor-pointer" @click="navigateTo(`/webtoon/${episode.id}`)">
<NuxtImg :src="sumToImageUrl(episode.thumbnail)" loading="lazy" class="aspect-square h-20 sm:h-24 md:h-28 xl:h-32"/>
<div id="infos" class="flex items-center w-full justify-between px-2 h-20 sm:h-24 md:h-28 xl:h-32">
<div id="text-infos" class="flex flex-col justify-between gap-auto h-full py-1 sm:py-2">
<div class="flex flex-col gap-0 md:gap-1 lg:gap-2">
<div id="title" class="flex flex-row gap-2">
<div v-if="episode.isNew" class="flex items-center">
<Badge v-if="episode.isNew" variant="default">New</Badge>
</div>
<h4 class="whitespace-nowrap truncate max-w-64">{{ episode.title }}</h4>
</div>
</div>
</div>
</div>
</div>
</Collapsible>
</template>

<style scoped>
</style>
30 changes: 30 additions & 0 deletions components/webtoons/episodes/EpisodeList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<script setup lang="ts">
import {ref, onMounted} from "vue";
import {getEpisodes} from "~/utils/api";
import EpisodeItem from "~/components/webtoons/episodes/EpisodeItem.vue";
const episodes = ref<any[]>([]);
const id = useRoute().params.id as any as number;
onMounted(async() => {
const response = await getEpisodes(id);
episodes.value = response.data;
});
</script>

<template>
<div class="h-full w-full md:w-3/4 lg:w-1/2 2xl:w-1/3 border-x-[1px]">
<div id="content" class="h-full overflow-y-scroll">
<div v-for="episode in episodes" :key="episode.id">
<Separator/>
<EpisodeItem :episode="episode"/>
</div>
<Separator/>
</div>
</div>
</template>

<style scoped>
</style>
26 changes: 16 additions & 10 deletions components/webtoons/webtoons/WebtoonItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,18 @@ onMounted(() => {

<template>
<Collapsible>
<div id="main" class="flex items-center">
<NuxtImg :src="sumToImageUrl(webtoon.thumbnail)" loading="lazy" class="aspect-square h-20 md:h-24 xl:h-28"/>
<div id="infos" class="flex items-center w-full justify-between px-2 h-20 md:h-24 xl:h-28">
<div id="text-infos" class="flex flex-col justify-between gap-2 h-full py-1">
<div class="flex flex-col gap-1">
<h4>{{ webtoon.title }}</h4>
<div id="main" class="flex items-center cursor-pointer" @click="navigateTo(`/webtoon/${webtoon.id}`)">
<NuxtImg :src="sumToImageUrl(webtoon.thumbnail)" loading="lazy" class="aspect-square h-20 sm:h-24 md:h-28 xl:h-32"/>
<div id="infos" class="flex items-center w-full justify-between px-2 h-20 sm:h-24 md:h-28 xl:h-32">
<div id="text-infos" class="flex flex-col justify-between gap-auto h-full py-1 sm:py-2">
<div class="flex flex-col gap-0 md:gap-1 lg:gap-2">
<div id="title" class="flex flex-row gap-2">
<div v-if="webtoon.hasNew || webtoon.hasNewEpisodes" class="flex items-center">
<Badge v-if="webtoon.hasNewEpisodes && !webtoon.isNew" variant="default">New</Badge>
<Badge v-if="webtoon.isNew" variant="destructive">New</Badge>
</div>
<h4 class="whitespace-nowrap truncate max-w-64">{{ webtoon.title }}</h4>
</div>
<p class="!m-0 leading-4 xl:leading-6">{{ webtoon.author }}</p>
</div>
<Badge variant="secondary" class="w-fit">{{ webtoon.language.toUpperCase() }}</Badge>
Expand All @@ -47,10 +53,10 @@ onMounted(() => {
</CollapsibleTrigger>
</div>
</div>
<CollapsibleContent class="pl-20">
<div id="more" class="flex gap-2 p-2">
<div v-for="genre in webtoon.genres" id="genre" :key="webtoon.genres.indexOf(genre)">
<Badge variant="default">{{ genre }}</Badge>
<CollapsibleContent class="pl-2">
<div id="more" class="flex gap-2 py-2">
<div v-for="genre in webtoon.genres" id="genre" :key="webtoon.genres.indexOf(genre)" class="flex justify-center">
<Badge variant="outline">{{ genre }}</Badge>
</div>
</div>
</CollapsibleContent>
Expand Down
15 changes: 10 additions & 5 deletions components/webtoons/webtoons/WebtoonList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ function clearSearch(){
onMounted(async() => {
const response = await getWebtoons();
webtoons.value = response.data;
console.log(webtoons.value);
// Sort webtoons by title
webtoons.value.sort((a, b) => a.title.localeCompare(b.title));
// Then by hasNewEpisodes
webtoons.value.sort((a, b) => b.hasNewEpisodes - a.hasNewEpisodes);
// Then by isNew
webtoons.value.sort((a, b) => b.isNew - a.isNew);
});
// Computed property for filtered webtoons
Expand All @@ -26,9 +31,9 @@ const filteredWebtoons = computed(() => {
</script>

<template>
<div class="w-full md:w-3/4 lg:w-1/2 2xl:w-1/3 border-x-[1px]">
<div class="h-full w-full md:w-3/4 lg:w-1/2 2xl:w-1/3 border-x-[1px]">
<div id="header" class="flex gap-4 py-4 px-10">
<div id="input" class="relative w-full max-w-sm items-center">
<div id="input" class="relative w-full items-center">
<Input v-model="search" placeholder="Search" class="pl-8"/>
<span class="absolute start-0 inset-y-0 flex items-center justify-center px-2">
<Icon name="iconoir:search"/>
Expand All @@ -38,8 +43,8 @@ const filteredWebtoons = computed(() => {
<Icon name="iconoir:xmark"/>
</Button>
</div>
<div id="content">
<Separator/>
<Separator/>
<div id="content" class="h-full overflow-y-scroll">
<div v-for="webtoon in filteredWebtoons" :key="webtoon.id">
<WebtoonItem :webtoon="webtoon"/>
<Separator/>
Expand Down
2 changes: 1 addition & 1 deletion layouts/NavLayout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {Toaster} from "~/components/ui/toast";
<template>
<div>
<Toaster/>
<div class="flex flex-col min-h-dvh min-w-dvh">
<div class="flex flex-col min-h-dvh min-w-vw max-h-dvh max-w-vw">
<TopMenu class="sticky top-0 w-full bg-background z-10"/>
<slot/>
</div>
Expand Down
2 changes: 1 addition & 1 deletion pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ definePageMeta({
</script>

<template>
<div class="flex justify-center">
<div class="flex justify-center h-[calc(theme(height.dvh)-142px)]">
<WebtoonList/>
</div>
</template>
Expand Down
21 changes: 21 additions & 0 deletions pages/webtoon/[id].vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script setup lang="ts">
import * as apiurlMiddleware from "~/middleware/apiurl.middleware";
import EpisodeList from "~/components/webtoons/episodes/EpisodeList.vue";
definePageMeta({
middleware: [
apiurlMiddleware.default
],
layout: "nav-layout"
});
</script>

<template>
<div class="flex justify-center h-[calc(theme(height.dvh)-70px)]">
<EpisodeList/>
</div>
</template>

<style scoped>
</style>
4 changes: 4 additions & 0 deletions utils/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@ export function sumToImageUrl(sum: string): string{
export function getWebtoons(): Promise<any>{
return axios.get("/webtoons/raw");
}

export function getEpisodes(episodeId: number): Promise<any>{
return axios.get(`/webtoons/${episodeId}/episodes/raw`);
}

0 comments on commit d19e2fb

Please sign in to comment.