123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315 |
- <script lang="ts" setup>
- import { computed, defineAsyncComponent, reactive, ref } from "vue";
- import Toast from "toasters";
- import { Station } from "@/types/station";
- import { useUserAuthStore } from "@/stores/userAuth";
- import { useWebsocketsStore } from "@/stores/websockets";
- const props = defineProps<{
- station: Station;
- canVoteToSkip: boolean;
- votedToSkip: boolean;
- votesToSkip: number;
- votesRequiredToSkip: number;
- }>();
- const Button = defineAsyncComponent(
- () => import("@/pages/NewStation/Components/Button.vue")
- );
- const Pill = defineAsyncComponent(
- () => import("@/pages/NewStation/Components/Pill.vue")
- );
- const Sidebar = defineAsyncComponent(
- () => import("@/pages/NewStation/Components/Sidebar.vue")
- );
- const Tabs = defineAsyncComponent(
- () => import("@/pages/NewStation/Components/Tabs.vue")
- );
- const UserItem = defineAsyncComponent(
- () => import("@/pages/NewStation/UserItem.vue")
- );
- const { loggedIn, hasPermissionForStation } = useUserAuthStore();
- const { socket } = useWebsocketsStore();
- const isOwner = userId => props.station.owner === userId;
- const djSearch = reactive({
- query: "",
- searchedQuery: "",
- page: 0,
- count: 0,
- resultsLeft: 0,
- pageSize: 0,
- results: [],
- nextPageResultsCount: 0
- });
- const userTabs = computed(() => {
- const tabs = ["Audience"];
- if (hasPermissionForStation(props.station._id, "stations.update"))
- tabs.push("DJs");
- return tabs;
- });
- const sortedUsers = computed(() =>
- props.station.users && props.station.users.loggedIn
- ? props.station.users.loggedIn
- .slice()
- .sort(
- (a, b) =>
- Number(isOwner(b._id)) - Number(isOwner(a._id)) ||
- Number(!isOwner(a._id)) - Number(!isOwner(b._id))
- )
- : []
- );
- const favorite = () => {
- socket.dispatch("stations.favoriteStation", props.station._id, res => {
- if (res.status === "success") {
- new Toast("Successfully favorited station.");
- } else new Toast(res.message);
- });
- };
- const unfavorite = () => {
- socket.dispatch("stations.unfavoriteStation", props.station._id, res => {
- if (res.status === "success") {
- new Toast("Successfully unfavorited station.");
- } else new Toast(res.message);
- });
- };
- const resetDjsSearch = () => {
- djSearch.query = "";
- djSearch.searchedQuery = "";
- djSearch.page = 0;
- djSearch.count = 0;
- djSearch.resultsLeft = 0;
- djSearch.pageSize = 0;
- djSearch.results = [];
- djSearch.nextPageResultsCount = 0;
- };
- const searchForDjs = (page: number) => {
- if (djSearch.page >= page || djSearch.searchedQuery !== djSearch.query) {
- djSearch.results = [];
- djSearch.page = 0;
- djSearch.count = 0;
- djSearch.resultsLeft = 0;
- djSearch.pageSize = 0;
- djSearch.nextPageResultsCount = 0;
- }
- djSearch.searchedQuery = djSearch.query;
- socket.dispatch("users.search", djSearch.query, page, res => {
- const { data } = res;
- if (res.status === "success") {
- const { count, pageSize, users } = data;
- djSearch.results = [...djSearch.results, ...users];
- djSearch.page = page;
- djSearch.count = count;
- djSearch.resultsLeft = count - djSearch.results.length;
- djSearch.pageSize = pageSize;
- djSearch.nextPageResultsCount = Math.min(
- djSearch.pageSize,
- djSearch.resultsLeft
- );
- } else if (res.status === "error") {
- djSearch.results = [];
- djSearch.page = 0;
- djSearch.count = 0;
- djSearch.resultsLeft = 0;
- djSearch.pageSize = 0;
- djSearch.nextPageResultsCount = 0;
- new Toast(res.message);
- }
- });
- };
- </script>
- <template>
- <Sidebar>
- <section class="information">
- <div class="information__header">
- <i
- v-if="loggedIn && station.isFavorited"
- class="material-icons"
- @click.prevent="unfavorite"
- title="Favorite station"
- >star</i
- >
- <i
- v-else-if="loggedIn"
- class="material-icons"
- @click.prevent="favorite"
- title="Unfavorite station"
- >star_border</i
- >
- <h1>{{ station.displayName }}</h1>
- </div>
- <div class="information__actions">
- <Pill v-if="station.privacy === 'public'" icon="public">
- Public
- </Pill>
- <Pill v-else-if="station.privacy === 'unlisted'" icon="link">
- Unlisted
- </Pill>
- <Pill v-else-if="station.privacy === 'private'" icon="lock">
- Private
- </Pill>
- </div>
- <p style="min-height: 60px">{{ station.description }}</p>
- <div class="information__actions">
- <Button icon="share">Share</Button>
- </div>
- </section>
- <hr class="sidebar__divider" />
- <Tabs :tabs="userTabs">
- <template #Audience>
- <UserItem
- v-for="user in sortedUsers"
- :key="`audience-${user._id}`"
- :station="station"
- :user="user"
- />
- <p
- v-if="station.users && station.users.loggedOut?.length > 0"
- class="guest-users"
- >
- {{ sortedUsers.length > 0 ? "..and" : "There are" }}
- {{ station.users.loggedOut.length }} logged-out users.
- </p>
- </template>
- <template #DJs>
- <h3 style="margin: 0; font-size: 16px; font-weight: 600">
- Add DJ
- </h3>
- <p style="font-size: 14px">
- Search for a user to promote to DJ.
- </p>
- <div style="display: flex">
- <input
- type="text"
- style="
- line-height: 18px;
- font-size: 12px;
- padding: 5px 10px;
- background-color: var(--light-grey-2);
- border-radius: 5px 0 0 5px;
- border: solid 1px var(--light-grey-1);
- flex-grow: 1;
- border-right-width: 0;
- "
- v-model="djSearch.query"
- @keyup.enter="searchForDjs(1)"
- />
- <Button
- icon="restart_alt"
- square
- style="
- border-right-width: 0;
- background-color: var(--light-grey-2);
- border-color: var(--light-grey-1);
- color: var(--primary-color);
- border-radius: 0;
- "
- @click.prevent="resetDjsSearch()"
- title="Reset search"
- />
- <Button
- icon="search"
- square
- style="border-radius: 0 5px 5px 0; flex-shrink: 0"
- @click.prevent="searchForDjs(1)"
- title="Search"
- />
- </div>
- <UserItem
- v-for="dj in djSearch.results"
- :key="`dj-search-${dj._id}`"
- :station="station"
- :user="dj"
- />
- <Button
- v-if="djSearch.resultsLeft > 0"
- icon="search"
- @click.prevent="searchForDjs(djSearch.page + 1)"
- >
- Load {{ djSearch.nextPageResultsCount }}
- more results
- </Button>
- <p
- v-if="djSearch.page > 0 && djSearch.results.length === 0"
- class="guest-users"
- >
- No users found with this search query.
- </p>
- <hr class="sidebar__divider" />
- <h3 style="margin: 0; font-size: 16px; font-weight: 600">
- Current DJs
- </h3>
- <UserItem
- v-for="dj in station.djs"
- :key="`djs-${dj._id}`"
- :station="station"
- :user="dj"
- />
- <p v-if="station.djs.length === 0" class="guest-users">
- There are currently no DJs.
- </p>
- </template>
- </Tabs>
- </Sidebar>
- </template>
- <style lang="less" scoped>
- .information {
- display: flex;
- flex-direction: column;
- gap: 10px;
- &__header {
- display: flex;
- align-items: center;
- gap: 10px;
- h1 {
- font-size: 26px;
- font-weight: 600;
- margin: 0;
- }
- i {
- font-size: 26px;
- color: var(--yellow);
- // TODO: Wrap in button
- }
- }
- &__actions {
- display: flex;
- flex-wrap: wrap;
- gap: 10px;
- }
- }
- .guest-users {
- color: var(--dark-grey-1);
- font-size: 14px !important;
- font-weight: 500 !important;
- text-align: center;
- padding: 5px;
- }
- </style>
|