mirror of
https://github.com/TeamPiped/Piped.git
synced 2025-10-14 19:38:18 +00:00
Reuse actions.okay string
This commit is contained in:
@@ -42,19 +42,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<div v-if="channel.description" class="whitespace-pre-wrap py-2 mx-1">
|
||||
<span v-if="fullDescription" v-html="purifyHTML(rewriteDescription(channel.description))" />
|
||||
<span v-html="purifyHTML(rewriteDescription(channel.description.slice(0, 100)))" v-else />
|
||||
<span v-if="channel.description.length > 100 && !fullDescription">...</span>
|
||||
<button
|
||||
v-if="channel.description.length > 100"
|
||||
class="hover:underline font-semibold text-neutral-500 block whitespace-normal"
|
||||
@click="fullDescription = !fullDescription"
|
||||
>
|
||||
[{{ fullDescription ? $t("actions.show_less") : $t("actions.show_more") }}]
|
||||
</button>
|
||||
</div>
|
||||
<CollapsableText :text="channel.description" />
|
||||
|
||||
<WatchOnYouTubeButton :link="`https://youtube.com/channel/${this.channel.id}`" />
|
||||
|
||||
@@ -90,6 +78,7 @@ import ErrorHandler from "./ErrorHandler.vue";
|
||||
import ContentItem from "./ContentItem.vue";
|
||||
import WatchOnYouTubeButton from "./WatchOnYouTubeButton.vue";
|
||||
import LoadingIndicatorPage from "./LoadingIndicatorPage.vue";
|
||||
import CollapsableText from "./CollapsableText.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -97,6 +86,7 @@ export default {
|
||||
ContentItem,
|
||||
WatchOnYouTubeButton,
|
||||
LoadingIndicatorPage,
|
||||
CollapsableText,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -105,7 +95,6 @@ export default {
|
||||
tabs: [],
|
||||
selectedTab: 0,
|
||||
contentItems: [],
|
||||
fullDescription: false,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
|
28
src/components/CollapsableText.vue
Normal file
28
src/components/CollapsableText.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<div v-if="text" class="whitespace-pre-wrap py-2 mx-1">
|
||||
<span v-if="showFullText" v-html="purifyHTML(rewriteDescription(text))" />
|
||||
<span v-else v-html="purifyHTML(rewriteDescription(text.slice(0, 100)))" />
|
||||
<span v-if="text.length > 100 && !showFullText">...</span>
|
||||
<button
|
||||
v-if="text.length > 100"
|
||||
class="hover:underline font-semibold text-neutral-500 block whitespace-normal"
|
||||
@click="showFullText = !showFullText"
|
||||
>
|
||||
[{{ showFullText ? $t("actions.show_less") : $t("actions.show_more") }}]
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
text: String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showFullText: false,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
@@ -4,7 +4,7 @@
|
||||
<h3 class="text-xl" v-text="message" />
|
||||
<div class="ml-auto mt-8 flex gap-2 w-min">
|
||||
<button class="btn" v-t="'actions.cancel'" @click="$emit('close')" />
|
||||
<button class="btn" v-t="'actions.confirm'" @click="$emit('confirm')" />
|
||||
<button class="btn" v-t="'actions.okay'" @click="$emit('confirm')" />
|
||||
</div>
|
||||
</div>
|
||||
</ModalComponent>
|
||||
|
@@ -1,40 +1,49 @@
|
||||
<template>
|
||||
<h1 v-t="'titles.feed'" class="font-bold text-center my-4" />
|
||||
|
||||
<button class="btn mr-2" @click="exportHandler">
|
||||
<router-link to="/subscriptions">Subscriptions</router-link>
|
||||
</button>
|
||||
<div class="flex flex-wrap md:items-center flex-col md:flex-row gap-2 children:(flex gap-1 items-center)">
|
||||
<span>
|
||||
<label for="filters">
|
||||
<strong v-text="`${$t('actions.filter')}:`" />
|
||||
</label>
|
||||
<select
|
||||
id="filters"
|
||||
v-model="selectedFilter"
|
||||
default="all"
|
||||
class="select flex-grow"
|
||||
@change="onFilterChange()"
|
||||
>
|
||||
<option v-for="filter in availableFilters" :key="filter" :value="filter" v-t="`video.${filter}`" />
|
||||
</select>
|
||||
</span>
|
||||
|
||||
<span>
|
||||
<a :href="getRssUrl">
|
||||
<span>
|
||||
<label for="group-selector">
|
||||
<strong v-text="`${$t('titles.channel_groups')}:`" />
|
||||
</label>
|
||||
<select id="group-selector" v-model="selectedGroupName" default="" class="select flex-grow">
|
||||
<option value="" v-t="`video.all`" />
|
||||
<option
|
||||
v-for="group in channelGroups"
|
||||
:key="group.groupName"
|
||||
:value="group.groupName"
|
||||
v-text="group.groupName"
|
||||
/>
|
||||
</select>
|
||||
</span>
|
||||
|
||||
<span class="md:ml-auto">
|
||||
<SortingSelector by-key="uploaded" @apply="order => videos.sort(order)" />
|
||||
</span>
|
||||
</div>
|
||||
<hr />
|
||||
|
||||
<span class="flex gap-2">
|
||||
<router-link class="btn" to="/subscriptions">Subscriptions</router-link>
|
||||
<a :href="getRssUrl" class="btn">
|
||||
<font-awesome-icon icon="rss" />
|
||||
</a>
|
||||
</span>
|
||||
|
||||
<label for="filters" class="ml-10 mr-2">
|
||||
<strong v-text="`${$t('actions.filter')}:`" />
|
||||
</label>
|
||||
<select id="filters" v-model="selectedFilter" default="all" class="select w-auto" @change="onFilterChange()">
|
||||
<option v-for="filter in availableFilters" :key="filter" :value="filter" v-t="`video.${filter}`" />
|
||||
</select>
|
||||
|
||||
<label for="group-selector" class="ml-10 mr-2">
|
||||
<strong v-text="`${$t('titles.channel_groups')}:`" />
|
||||
</label>
|
||||
<select id="group-selector" v-model="selectedGroupName" default="" class="select w-auto">
|
||||
<option value="" v-t="`video.all`" />
|
||||
<option
|
||||
v-for="group in channelGroups"
|
||||
:key="group.groupName"
|
||||
:value="group.groupName"
|
||||
v-text="group.groupName"
|
||||
/>
|
||||
</select>
|
||||
|
||||
<span class="md:float-right">
|
||||
<SortingSelector by-key="uploaded" @apply="order => videos.sort(order)" />
|
||||
</span>
|
||||
|
||||
<hr />
|
||||
|
||||
<LoadingIndicatorPage :show-content="videosStore != null" class="video-grid">
|
||||
|
@@ -1,14 +1,12 @@
|
||||
<template>
|
||||
<h1 class="font-bold text-center" v-t="'titles.history'" />
|
||||
|
||||
<div class="flex">
|
||||
<div>
|
||||
<button class="btn" v-t="'actions.clear_history'" @click="clearHistory" />
|
||||
<div class="flex md:items-center gap-2 flex-col md:flex-row">
|
||||
<button class="btn" v-t="'actions.clear_history'" @click="clearHistory" />
|
||||
|
||||
<button class="btn mx-3" v-t="'actions.export_to_json'" @click="exportHistory" />
|
||||
</div>
|
||||
<button class="btn" v-t="'actions.export_to_json'" @click="exportHistory" />
|
||||
|
||||
<div class="right-1">
|
||||
<div class="ml-auto flex gap-1 items-center">
|
||||
<SortingSelector by-key="watchedAt" @apply="order => videos.sort(order)" />
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -45,13 +45,13 @@ export default {
|
||||
}
|
||||
|
||||
.modal-container {
|
||||
@apply w-min m-auto px-8 bg-white p-6 rounded-xl min-w-[20vw] relative;
|
||||
@apply w-min m-auto bg-white p-5 rounded-xl min-w-[20vw] relative;
|
||||
}
|
||||
.dark .modal-container {
|
||||
@apply bg-dark-700;
|
||||
}
|
||||
|
||||
.modal-container > button {
|
||||
@apply absolute right-8 top-6;
|
||||
@apply absolute right-2.5 top-1;
|
||||
}
|
||||
</style>
|
||||
|
@@ -59,35 +59,41 @@
|
||||
</ul>
|
||||
</nav>
|
||||
<!-- navigation bar for mobile devices -->
|
||||
<ul
|
||||
<div
|
||||
v-if="showTopNav"
|
||||
class="flex flex-col justify-center items-end mb-4 children:(my-0.5 mr-5)"
|
||||
@click="showTopNav = false"
|
||||
class="mobile-nav flex flex-col mb-4 children:(p-1 w-full border-b border-dark-100 flex items-center gap-1)"
|
||||
>
|
||||
<li v-if="shouldShowTrending">
|
||||
<router-link v-t="'titles.trending'" to="/trending" />
|
||||
</li>
|
||||
<li>
|
||||
<router-link v-t="'titles.preferences'" to="/preferences" />
|
||||
</li>
|
||||
<li v-if="shouldShowLogin">
|
||||
<router-link v-t="'titles.login'" to="/login" />
|
||||
</li>
|
||||
<li v-if="shouldShowLogin">
|
||||
<router-link v-t="'titles.register'" to="/register" />
|
||||
</li>
|
||||
<li v-if="shouldShowHistory">
|
||||
<router-link v-t="'titles.history'" to="/history" />
|
||||
</li>
|
||||
<li>
|
||||
<router-link v-t="'titles.playlists'" to="/playlists" />
|
||||
</li>
|
||||
<li v-if="!shouldShowTrending">
|
||||
<router-link v-t="'titles.feed'" to="/feed" />
|
||||
</li>
|
||||
</ul>
|
||||
<router-link v-if="shouldShowTrending" to="/trending">
|
||||
<div class="i-fa6-solid:fire"></div>
|
||||
<i18n-t keypath="titles.trending"></i18n-t>
|
||||
</router-link>
|
||||
<router-link to="/preferences">
|
||||
<div class="i-fa6-solid:gear"></div>
|
||||
<i18n-t keypath="titles.preferences"></i18n-t>
|
||||
</router-link>
|
||||
<router-link v-if="shouldShowLogin" to="/login">
|
||||
<div class="i-fa6-solid:user"></div>
|
||||
<i18n-t keypath="titles.login"></i18n-t>
|
||||
</router-link>
|
||||
<router-link v-if="shouldShowLogin" to="/register">
|
||||
<div class="i-fa6-solid:user-plus"></div>
|
||||
<i18n-t keypath="titles.register"></i18n-t>
|
||||
</router-link>
|
||||
<router-link v-if="shouldShowHistory" to="/history">
|
||||
<div class="i-fa6-solid:clock-rotate-left"></div>
|
||||
<i18n-t keypath="titles.history"></i18n-t>
|
||||
</router-link>
|
||||
<router-link to="/playlists">
|
||||
<div class="i-fa6-solid:list"></div>
|
||||
<i18n-t keypath="titles.playlists"></i18n-t>
|
||||
</router-link>
|
||||
<router-link v-if="!shouldShowTrending" to="/feed">
|
||||
<div class="i-fa6-solid:play"></div>
|
||||
<i18n-t keypath="titles.feed"></i18n-t>
|
||||
</router-link>
|
||||
</div>
|
||||
<!-- search suggestions for mobile devices -->
|
||||
<div class="mobile-search md:hidden mx-2 search-container">
|
||||
<div class="w-full mb-2 md:hidden search-container">
|
||||
<input
|
||||
v-model="searchText"
|
||||
class="input h-10 w-full"
|
||||
@@ -189,8 +195,7 @@ export default {
|
||||
@apply absolute right-3 cursor-pointer rounded-full bg-[#ccc] w-4 h-4 text-center text-black opacity-50 hover:(opacity-70) text-size-[13px];
|
||||
line-height: 1.05;
|
||||
}
|
||||
.mobile-search {
|
||||
width: calc(100% - 1rem);
|
||||
@apply mx-2;
|
||||
.mobile-nav div {
|
||||
@apply mx-1;
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,20 +1,21 @@
|
||||
<template>
|
||||
<ErrorHandler v-if="playlist && playlist.error" :message="playlist.message" :error="playlist.error" />
|
||||
|
||||
<LoadingIndicatorPage :show-content="playlist" v-show="!playlist.error">
|
||||
<h1 class="text-center my-4" v-text="playlist.name" />
|
||||
<LoadingIndicatorPage :show-content="playlist" v-show="!playlist?.error">
|
||||
<h1 class="ml-1 mb-1 mt-4 text-3xl!" v-text="playlist.name" />
|
||||
|
||||
<div class="flex justify-between items-center">
|
||||
<CollapsableText :text="playlist.description" />
|
||||
|
||||
<div class="flex justify-between items-center mt-1">
|
||||
<div>
|
||||
<router-link class="link" :to="playlist.uploaderUrl || '/'">
|
||||
<router-link class="link flex items-center gap-3" :to="playlist.uploaderUrl || '/'">
|
||||
<img :src="playlist.uploaderAvatar" loading="lazy" class="rounded-full" />
|
||||
<strong v-text="playlist.uploader" />
|
||||
</router-link>
|
||||
</div>
|
||||
<div>
|
||||
<strong v-text="`${playlist.videos} ${$t('video.videos')}`" />
|
||||
<br />
|
||||
<button class="btn mr-1" v-if="!isPipedPlaylist" @click="bookmarkPlaylist">
|
||||
<strong v-text="`${playlist.videos} ${$t('video.videos')}`" class="mr-2" />
|
||||
<button class="btn mx-1" v-if="!isPipedPlaylist" @click="bookmarkPlaylist">
|
||||
{{ $t(`actions.${isBookmarked ? "playlist_bookmarked" : "bookmark_playlist"}`)
|
||||
}}<font-awesome-icon class="ml-3" icon="bookmark" />
|
||||
</button>
|
||||
@@ -52,6 +53,7 @@
|
||||
<script>
|
||||
import ErrorHandler from "./ErrorHandler.vue";
|
||||
import LoadingIndicatorPage from "./LoadingIndicatorPage.vue";
|
||||
import CollapsableText from "./CollapsableText.vue";
|
||||
import VideoItem from "./VideoItem.vue";
|
||||
import WatchOnYouTubeButton from "./WatchOnYouTubeButton.vue";
|
||||
|
||||
@@ -61,6 +63,7 @@ export default {
|
||||
VideoItem,
|
||||
WatchOnYouTubeButton,
|
||||
LoadingIndicatorPage,
|
||||
CollapsableText,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@@ -39,8 +39,26 @@
|
||||
v-text="playlist.name"
|
||||
/>
|
||||
</router-link>
|
||||
<button class="btn h-auto" @click="renamePlaylist(playlist.id)" v-t="'actions.rename_playlist'" />
|
||||
<button class="btn h-auto" @click="showPlaylistEditModal(playlist)" v-t="'actions.edit_playlist'" />
|
||||
<button class="btn h-auto ml-2" @click="playlistToDelete = playlist.id" v-t="'actions.delete_playlist'" />
|
||||
<ModalComponent v-if="playlist.id == playlistToEdit" @close="playlistToEdit = null">
|
||||
<div class="flex flex-col gap-2">
|
||||
<h2 v-t="'actions.edit_playlist'" />
|
||||
<input
|
||||
class="input"
|
||||
type="text"
|
||||
v-model="newPlaylistName"
|
||||
:placeholder="$t('actions.playlist_name')"
|
||||
/>
|
||||
<input
|
||||
class="input"
|
||||
type="text"
|
||||
v-model="newPlaylistDescription"
|
||||
:placeholder="$t('actions.playlist_description')"
|
||||
/>
|
||||
<button class="btn ml-auto" @click="editPlaylist(playlist)" v-t="'actions.okay'" />
|
||||
</div>
|
||||
</ModalComponent>
|
||||
<ConfirmModal
|
||||
v-if="playlistToDelete == playlist.id"
|
||||
:message="$t('actions.delete_playlist_confirm')"
|
||||
@@ -83,6 +101,7 @@
|
||||
|
||||
<script>
|
||||
import ConfirmModal from "./ConfirmModal.vue";
|
||||
import ModalComponent from "./ModalComponent.vue";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
@@ -90,6 +109,9 @@ export default {
|
||||
playlists: [],
|
||||
bookmarks: [],
|
||||
playlistToDelete: null,
|
||||
playlistToEdit: null,
|
||||
newPlaylistName: "",
|
||||
newPlaylistDescription: "",
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
@@ -109,30 +131,48 @@ export default {
|
||||
this.playlists = json;
|
||||
});
|
||||
},
|
||||
renamePlaylist(id) {
|
||||
const newName = prompt(this.$t("actions.new_playlist_name"));
|
||||
if (!newName) return;
|
||||
this.fetchJson(this.authApiUrl() + "/user/playlists/rename", null, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
playlistId: id,
|
||||
newName: newName,
|
||||
}),
|
||||
headers: {
|
||||
Authorization: this.getAuthToken(),
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}).then(json => {
|
||||
if (json.error) alert(json.error);
|
||||
else {
|
||||
this.playlists.forEach((playlist, index) => {
|
||||
if (playlist.id == id) {
|
||||
this.playlists[index].name = newName;
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
showPlaylistEditModal(playlist) {
|
||||
this.newPlaylistName = playlist.name;
|
||||
this.newPlaylistDescription = playlist.description;
|
||||
this.playlistToEdit = playlist.id;
|
||||
},
|
||||
editPlaylist(selectedPlaylist) {
|
||||
// save the new name and description since they could be overwritten during the http request
|
||||
const newName = this.newPlaylistName;
|
||||
const newDescription = this.newPlaylistDescription;
|
||||
if (newName != selectedPlaylist.name) {
|
||||
this.fetchJson(this.authApiUrl() + "/user/playlists/rename", null, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
playlistId: selectedPlaylist.id,
|
||||
newName: newName,
|
||||
}),
|
||||
headers: {
|
||||
Authorization: this.getAuthToken(),
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}).then(json => {
|
||||
if (json.error) alert(json.error);
|
||||
else selectedPlaylist.name = newName;
|
||||
});
|
||||
}
|
||||
if (newDescription != selectedPlaylist.description) {
|
||||
this.fetchJson(this.authApiUrl() + "/user/playlists/description", null, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify({
|
||||
playlistId: selectedPlaylist.id,
|
||||
description: newDescription,
|
||||
}),
|
||||
headers: {
|
||||
Authorization: this.getAuthToken(),
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}).then(json => {
|
||||
if (json.error) alert(json.error);
|
||||
else selectedPlaylist.description = newDescription;
|
||||
});
|
||||
}
|
||||
this.playlistToEdit = null;
|
||||
},
|
||||
deletePlaylist(id) {
|
||||
this.fetchJson(this.authApiUrl() + "/user/playlists/delete", null, {
|
||||
@@ -271,6 +311,6 @@ export default {
|
||||
this.bookmarks.splice(index, 1);
|
||||
},
|
||||
},
|
||||
components: { ConfirmModal },
|
||||
components: { ConfirmModal, ModalComponent },
|
||||
};
|
||||
</script>
|
||||
|
@@ -82,7 +82,7 @@ export default {
|
||||
|
||||
<style>
|
||||
.suggestions-container {
|
||||
@apply left-1/2 translate-x-[-50%] transform-gpu max-w-3xl w-full box-border p-y-1.25 z-10 lt-md:max-w-[calc(100%-0.5rem)] bg-gray-300;
|
||||
@apply left-1/2 translate-x-[-50%] transform-gpu max-w-3xl w-full box-border z-10 lt-md:max-w-[calc(100%-0.5rem)] bg-gray-300;
|
||||
}
|
||||
|
||||
.dark .suggestions-container {
|
||||
@@ -98,6 +98,6 @@ export default {
|
||||
}
|
||||
|
||||
.suggestion {
|
||||
@apply p-y-1;
|
||||
@apply p-1;
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<label for="ddlSortBy" v-t="'actions.sort_by'" />
|
||||
<select id="ddlSortBy" v-model="selectedSort" class="select w-auto">
|
||||
<select id="ddlSortBy" v-model="selectedSort" class="select flex-grow">
|
||||
<option v-for="(value, key) in options" v-t="`actions.${key}`" :key="key" :value="value" />
|
||||
</select>
|
||||
</template>
|
||||
|
@@ -43,13 +43,13 @@
|
||||
<div class="font-bold mt-2 text-2xl break-words" v-text="video.title" />
|
||||
<div class="flex flex-wrap mt-3 mb-3">
|
||||
<!-- views / date -->
|
||||
<div class="flex flex-auto children:ml-2">
|
||||
<div class="flex flex-auto gap-2">
|
||||
<span v-t="{ path: 'video.views', args: { views: addCommas(video.views) } }" />
|
||||
<span> | </span>
|
||||
<span v-text="uploadDate" />
|
||||
</div>
|
||||
<!-- Likes/dilikes -->
|
||||
<div class="flex children:mr-2">
|
||||
<div class="flex gap-2">
|
||||
<template v-if="video.likes >= 0">
|
||||
<div class="flex items-center">
|
||||
<div class="i-fa6-solid:thumbs-up" />
|
||||
@@ -68,7 +68,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- Channel info & options flex container -->
|
||||
<div class="flex">
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<!-- Channel Image & Info -->
|
||||
<div class="flex items-center">
|
||||
<img :src="video.uploaderAvatar" alt="" loading="lazy" class="rounded-full" />
|
||||
@@ -78,19 +78,6 @@
|
||||
<!-- Verified Badge -->
|
||||
<font-awesome-icon class="ml-1" v-if="video.uploaderVerified" icon="check" />
|
||||
</div>
|
||||
<div class="flex relative ml-auto children:mr-1 items-center">
|
||||
<button class="btn" v-if="authenticated" @click="showModal = !showModal">
|
||||
{{ $t("actions.add_to_playlist") }}<font-awesome-icon class="ml-1" icon="circle-plus" />
|
||||
</button>
|
||||
<button
|
||||
class="btn"
|
||||
@click="subscribeHandler"
|
||||
v-t="{
|
||||
path: `actions.${subscribed ? 'unsubscribe' : 'subscribe'}`,
|
||||
args: { count: numberFormat(video.uploaderSubscriberCount) },
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
<PlaylistAddModal v-if="showModal" :video-id="getVideoId()" @close="showModal = !showModal" />
|
||||
<ShareModal
|
||||
v-if="showShareModal"
|
||||
@@ -100,8 +87,20 @@
|
||||
:playlist-index="index"
|
||||
@close="showShareModal = !showShareModal"
|
||||
/>
|
||||
<div class="flex">
|
||||
<div class="self-center children:mr-1 my-1">
|
||||
<div class="flex flex-wrap gap-1 ml-auto">
|
||||
<!-- Subscribe Button -->
|
||||
<button class="btn flex items-center" v-if="authenticated" @click="showModal = !showModal">
|
||||
{{ $t("actions.add_to_playlist") }}<font-awesome-icon class="ml-1" icon="circle-plus" />
|
||||
</button>
|
||||
<button
|
||||
class="btn"
|
||||
@click="subscribeHandler"
|
||||
v-t="{
|
||||
path: `actions.${subscribed ? 'unsubscribe' : 'subscribe'}`,
|
||||
args: { count: numberFormat(video.uploaderSubscriberCount) },
|
||||
}"
|
||||
/>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<!-- RSS Feed button -->
|
||||
<a
|
||||
aria-label="RSS feed"
|
||||
@@ -110,18 +109,22 @@
|
||||
v-if="video.uploaderUrl"
|
||||
:href="`${apiUrl()}/feed/unauthenticated/rss?channels=${video.uploaderUrl.split('/')[2]}`"
|
||||
target="_blank"
|
||||
class="btn flex-col"
|
||||
class="btn flex items-center"
|
||||
>
|
||||
<font-awesome-icon icon="rss" />
|
||||
<font-awesome-icon class="mx-1.5" icon="rss" />
|
||||
</a>
|
||||
<WatchOnYouTubeButton :link="`https://youtu.be/${getVideoId()}`" />
|
||||
<!-- Share Dialog -->
|
||||
<button class="btn" @click="showShareModal = !showShareModal">
|
||||
<button class="btn flex items-center" @click="showShareModal = !showShareModal">
|
||||
<i18n-t class="lt-lg:hidden" keypath="actions.share" tag="strong"></i18n-t>
|
||||
<font-awesome-icon class="mx-1.5" icon="fa-share" />
|
||||
</button>
|
||||
<!-- LBRY -->
|
||||
<a v-if="video.lbryId" :href="'https://odysee.com/' + video.lbryId" class="btn">
|
||||
<a
|
||||
v-if="video.lbryId"
|
||||
:href="'https://odysee.com/' + video.lbryId"
|
||||
class="btn flex items-center"
|
||||
>
|
||||
<i18n-t keypath="player.watch_on" tag="strong">LBRY</i18n-t>
|
||||
</a>
|
||||
<!-- listen / watch toggle -->
|
||||
@@ -129,9 +132,9 @@
|
||||
:to="toggleListenUrl"
|
||||
:aria-label="(isListening ? 'Watch ' : 'Listen to ') + video.title"
|
||||
:title="(isListening ? 'Watch ' : 'Listen to ') + video.title"
|
||||
class="btn flex-col"
|
||||
class="btn flex items-center"
|
||||
>
|
||||
<font-awesome-icon :icon="isListening ? 'tv' : 'headphones'" />
|
||||
<font-awesome-icon class="mx-1.5" :icon="isListening ? 'tv' : 'headphones'" />
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user