Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 138 additions & 0 deletions components/torrent/TorrentDetails.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
<template>
<div class="flex flex-col min-h-fit grow">
<div class="flex flex-col items-center max-w-full">
<div id="torrent-view" class="flex flex-col-reverse items-start w-full gap-3 mb-8 md:flex-row flex-nowrap">
<div id="torrent-view-details" class="flex flex-col items-center flex-auto w-full">
<div id="torrent-view-details-body" class="flex flex-col w-full grow">
<div class="flex flex-col gap-6">
<div class="hidden md:block">
<button
class="pl-2 pr-4 border-none btn bg-base-100 hover:bg-base-100 text-base-content/75 hover:text-base-content"
@click.prevent="$router.go(-1)"
>
<ChevronLeftIcon class="w-5 mr-2" />
back
</button>
</div>
<div v-if="torrent" class="flex flex-col flex-auto w-full gap-6">
<TorrentDescriptionTab :torrent="torrent" @updated="reloadTorrent" />
<TorrentFilesTab :torrent="torrent" @updated="reloadTorrent" />
<TorrentTrackersTab :torrent="torrent" @updated="reloadTorrent" />
</div>
</div>
</div>
</div>
<TorrentActionCard v-if="torrent" class="max-w-md top-8 md:sticky" :torrent="torrent" @updated="reloadTorrent" @deleted="navigateToTorrentList" />
<div class="block md:hidden">
<button
class="border-none btn bg-base-200"
@click.prevent="$router.go(-1)"
>
<ChevronLeftIcon class="w-5 mr-2 text-base-content/50" />
back
</button>
</div>
</div>
</div>
</div>
</template>

<script setup lang="ts">
import { ChevronLeftIcon } from "@heroicons/vue/24/solid";
import { Ref } from "vue";
import { TorrentResponse } from "torrust-index-types-lib";
import { useRoute } from "#app";
import { notify } from "notiwind-ts";
import TorrentActionCard from "~/components/torrent/TorrentActionCard.vue";
import TorrentDescriptionTab from "~/components/torrent/TorrentDescriptionTab.vue";
import TorrentFilesTab from "~/components/torrent/TorrentFilesTab.vue";
import TorrentTrackersTab from "~/components/torrent/TorrentTrackersTab.vue";
import { navigateTo, ref, useRestApi } from "#imports";

const route = useRoute();
const rest = useRestApi().value;

const infoHash = route.params.infoHash as string;

const loadingTorrent: Ref<boolean> = ref(false);
const torrent: Ref<TorrentResponse> = ref(null);
const title = ref("");

useSeoMeta({
title: () => `${title.value} - Torrent`
});

onBeforeMount(() => {
if (!infoHash) {
navigateTo("/", { replace: true });
}

getTorrentFromApi(infoHash);
});

function getTorrentFromApi (infoHash: string) {
loadingTorrent.value = true;

rest.torrent.getTorrentInfo(infoHash)
.then((data) => {
torrent.value = data;
title.value = data.title;
})
.catch((err) => {
loadingTorrent.value = false;
notify({
group: "error",
title: "Error",
text: `Trying to get the torrent information. ${err.message}.`
}, 10000);
});
}

function reloadTorrent () {
getTorrentFromApi(torrent.value.info_hash);
}

function navigateToTorrentList () {
navigateTo("/torrents", { replace: true });
}
</script>

<style scoped>
.active {
@apply bg-primary/20 text-primary;
}
</style>

<style>
img {
@apply rounded-2xl;
}

.markdown-body {
@apply text-neutral-content;
}

.markdown-body a {
@apply text-sky-500;
}

.markdown-body blockquote {
@apply text-slate-400 dark:text-neutral-400 border-slate-600 dark:border-neutral-600;
}

.markdown-body hr {
@apply bg-slate-200/50 dark:bg-white/5;
}

.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 {
@apply border-slate-200/50 dark:border-white/5;
}

.markdown-body .highlight pre, .markdown-body pre {
@apply bg-slate-800 dark:bg-neutral-800 text-slate-400 dark:text-neutral-400 rounded-md;
}

.markdown-body table tr, .markdown-body table td, .markdown-body table th {
@apply bg-slate-800 dark:bg-neutral-800 border-slate-700 dark:border-white/5;
}
</style>
5 changes: 4 additions & 1 deletion components/torrent/TorrentListTorrentDetails.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div
class="flex flex-col items-center w-full group/details rounded-2xl"
@click.stop="$router.push(`/torrent/${props.infoHash}`)"
@click.stop="$router.push(`/torrent/${props.infoHash}/${slug}`)"
>
<div class="flex justify-center w-full p-4 overflow-y-auto duration-500 border-2 max-h-96 border-base-content/20 hover:border-primary text-base-content/75 rounded-2xl">
<template v-if="torrent?.description">
Expand All @@ -19,6 +19,7 @@ import { Ref } from "vue";
import { TorrentResponse } from "torrust-index-types-lib";
import { notify } from "notiwind-ts";
import { onMounted, ref, useRestApi } from "#imports";
import { generateSlug } from "~/src/domain/services/slug";

const rest = useRestApi();

Expand All @@ -31,6 +32,8 @@ const props = defineProps({
}
});

const slug = computed(() => generateSlug(torrent.value.title));

onMounted(() => {
rest.value.torrent.getTorrentInfo(props.infoHash)
.then((data) => {
Expand Down
133 changes: 1 addition & 132 deletions pages/torrent/[infoHash].vue
Original file line number Diff line number Diff line change
@@ -1,134 +1,3 @@
<template>
<div class="flex flex-col min-h-fit grow">
<div class="flex flex-col items-center max-w-full">
<div id="torrent-view" class="flex flex-col-reverse items-start w-full gap-3 mb-8 md:flex-row flex-nowrap">
<div id="torrent-view-details" class="flex flex-col items-center flex-auto w-full">
<div id="torrent-view-details-body" class="flex flex-col w-full grow">
<div class="flex flex-col gap-6">
<div class="hidden md:block">
<button
class="pl-2 pr-4 border-none btn bg-base-100 hover:bg-base-100 text-base-content/75 hover:text-base-content"
@click.prevent="$router.go(-1)"
>
<ChevronLeftIcon class="w-5 mr-2" />
back
</button>
</div>
<div v-if="torrent" class="flex flex-col flex-auto w-full gap-6">
<TorrentDescriptionTab :torrent="torrent" @updated="reloadTorrent" />
<TorrentFilesTab :torrent="torrent" @updated="reloadTorrent" />
<TorrentTrackersTab :torrent="torrent" @updated="reloadTorrent" />
</div>
</div>
</div>
</div>
<TorrentActionCard v-if="torrent" class="max-w-md top-8 md:sticky" :torrent="torrent" @updated="reloadTorrent" @deleted="navigateToTorrentList" />
<div class="block md:hidden">
<button
class="border-none btn bg-base-200"
@click.prevent="$router.go(-1)"
>
<ChevronLeftIcon class="w-5 mr-2 text-base-content/50" />
back
</button>
</div>
</div>
</div>
</div>
<TorrentDetails />
</template>

<script setup lang="ts">
import { ChevronLeftIcon } from "@heroicons/vue/24/solid";
import { Ref } from "vue";
import { TorrentResponse } from "torrust-index-types-lib";
import { notify } from "notiwind-ts";
import { useRoute, useRuntimeConfig } from "#app";
import TorrentActionCard from "~/components/torrent/TorrentActionCard.vue";
import TorrentDescriptionTab from "~/components/torrent/TorrentDescriptionTab.vue";
import TorrentFilesTab from "~/components/torrent/TorrentFilesTab.vue";
import TorrentTrackersTab from "~/components/torrent/TorrentTrackersTab.vue";
import { navigateTo, onMounted, ref, useRestApi } from "#imports";

const route = useRoute();
const rest = useRestApi().value;

const infoHash = route.params.infoHash as string;

const loadingTorrent: Ref<boolean> = ref(false);
const torrent: Ref<TorrentResponse> = ref(null);

onMounted(() => {
if (!infoHash) {
navigateTo("/", { replace: true });
}

getTorrentFromApi(infoHash);
});

function getTorrentFromApi (infoHash: string) {
loadingTorrent.value = true;

rest.torrent.getTorrentInfo(infoHash)
.then((data) => {
torrent.value = data;
})
.catch((err) => {
loadingTorrent.value = false;
notify({
group: "error",
title: "Error",
text: `Trying to get the torrent information. ${err.message}.`
}, 10000);
});

// TODO: Set torrent title in URL.
}

function reloadTorrent () {
getTorrentFromApi(torrent.value.info_hash);
}

function navigateToTorrentList () {
navigateTo("/torrents", { replace: true });
}
</script>

<style scoped>
.active {
@apply bg-primary/20 text-primary;
}
</style>

<style>
img {
@apply rounded-2xl;
}

.markdown-body {
@apply text-neutral-content;
}

.markdown-body a {
@apply text-sky-500;
}

.markdown-body blockquote {
@apply text-slate-400 dark:text-neutral-400 border-slate-600 dark:border-neutral-600;
}

.markdown-body hr {
@apply bg-slate-200/50 dark:bg-white/5;
}

.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 {
@apply border-slate-200/50 dark:border-white/5;
}

.markdown-body .highlight pre, .markdown-body pre {
@apply bg-slate-800 dark:bg-neutral-800 text-slate-400 dark:text-neutral-400 rounded-md;
}

.markdown-body table tr, .markdown-body table td, .markdown-body table th {
@apply bg-slate-800 dark:bg-neutral-800 border-slate-700 dark:border-white/5;
}
</style>
3 changes: 3 additions & 0 deletions pages/torrent/[infoHash]/[title].vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
<TorrentDetails />
</template>
9 changes: 9 additions & 0 deletions src/domain/services/slug.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export function generateSlug (input: string): string {
return input
.toLowerCase()
.replace(/[\s_]+/g, "-") // Replace spaces and underscores with -
.replace(/[^\w-]+/g, "") // Remove all non-word characters
.replace(/--+/g, "-") // Replace multiple - with single -
.replace(/^-+/, "") // Trim - from start of text
.replace(/-+$/, ""); // Trim - from end of text
}