index.vue 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. <script setup lang="ts">
  2. import { useRoute } from "vue-router";
  3. import { defineAsyncComponent, watch, onMounted } from "vue";
  4. import { storeToRefs } from "pinia";
  5. import { useConfigStore } from "@/stores/config";
  6. import { useUserAuthStore } from "@/stores/userAuth";
  7. import { useStationStore } from "@/stores/station";
  8. import { useTabQueryHandler } from "@/composables/useTabQueryHandler";
  9. const Queue = defineAsyncComponent(() => import("@/components/Queue.vue"));
  10. const Users = defineAsyncComponent(
  11. () => import("@/pages/Station/Sidebar/Users.vue")
  12. );
  13. const Request = defineAsyncComponent(() => import("@/components/Request.vue"));
  14. const History = defineAsyncComponent(
  15. () => import("@/pages/Station/Sidebar/History.vue")
  16. );
  17. const route = useRoute();
  18. const configStore = useConfigStore();
  19. const userAuthStore = useUserAuthStore();
  20. const stationStore = useStationStore();
  21. const { tab, showTab } = useTabQueryHandler("queue");
  22. const { loggedIn } = storeToRefs(userAuthStore);
  23. const { station, userCount } = storeToRefs(stationStore);
  24. const { hasPermission } = stationStore;
  25. const canRequest = (requireLogin = true) =>
  26. station.value &&
  27. (!requireLogin || loggedIn.value) &&
  28. station.value.requests &&
  29. station.value.requests.enabled &&
  30. (station.value.requests.access === "user" ||
  31. (station.value.requests.access === "owner" &&
  32. hasPermission("stations.request")));
  33. watch(
  34. () => [station.value.requests, hasPermission("stations.request")],
  35. () => {
  36. if (tab.value === "request" && !canRequest()) showTab("queue");
  37. }
  38. );
  39. onMounted(() => {
  40. if (
  41. route.query.tab === "queue" ||
  42. route.query.tab === "users" ||
  43. route.query.tab === "request"
  44. )
  45. tab.value = route.query.tab;
  46. });
  47. </script>
  48. <template>
  49. <div class="tabs-container">
  50. <div class="tab-selection">
  51. <button
  52. class="button is-default"
  53. :class="{ selected: tab === 'queue' }"
  54. @click="showTab('queue')"
  55. >
  56. Queue
  57. </button>
  58. <button
  59. class="button is-default"
  60. :class="{ selected: tab === 'users' }"
  61. @click="showTab('users')"
  62. >
  63. Users
  64. <span class="tag">{{ Math.max(userCount, 1) }}</span>
  65. </button>
  66. <button
  67. v-if="canRequest()"
  68. class="button is-default"
  69. :class="{ selected: tab === 'request' }"
  70. @click="showTab('request')"
  71. >
  72. Request
  73. </button>
  74. <button
  75. v-else-if="canRequest(false)"
  76. class="button is-default"
  77. content="Login to request songs"
  78. v-tippy="{ theme: 'info' }"
  79. >
  80. Request
  81. </button>
  82. <button
  83. v-if="configStore.get('experimental.station_history')"
  84. class="button is-default"
  85. :class="{ selected: tab === 'history' }"
  86. @click="showTab('history')"
  87. >
  88. History
  89. </button>
  90. </div>
  91. <Queue class="tab" v-show="tab === 'queue'" @on-change-tab="showTab" />
  92. <Users class="tab" v-show="tab === 'users'" />
  93. <Request
  94. v-if="canRequest()"
  95. v-show="tab === 'request'"
  96. class="tab requests-tab"
  97. sector="station"
  98. />
  99. <History
  100. v-if="configStore.get('experimental.station_history')"
  101. class="tab"
  102. v-show="tab === 'history'"
  103. />
  104. </div>
  105. </template>
  106. <style lang="less" scoped>
  107. .night-mode {
  108. .tab-selection .button {
  109. background: var(--dark-grey);
  110. color: var(--white);
  111. }
  112. .tab.requests-tab {
  113. background-color: var(--dark-grey-3) !important;
  114. border: 0 !important;
  115. }
  116. }
  117. .tabs-container .tab {
  118. width: 100%;
  119. height: calc(100% - 36px);
  120. position: absolute;
  121. border: 1px solid var(--light-grey-3);
  122. border-top: 0;
  123. }
  124. .tab-selection {
  125. display: flex;
  126. overflow-x: auto;
  127. .button {
  128. border-radius: @border-radius @border-radius 0 0;
  129. border: 0;
  130. text-transform: uppercase;
  131. font-size: 17px;
  132. color: var(--dark-grey-3);
  133. background-color: var(--light-grey-2);
  134. flex-grow: 1;
  135. &:not(:first-of-type) {
  136. margin-left: 5px;
  137. }
  138. }
  139. .selected {
  140. background-color: var(--primary-color) !important;
  141. color: var(--white) !important;
  142. font-weight: 600;
  143. }
  144. }
  145. :deep(.nothing-here-text) {
  146. height: 100%;
  147. }
  148. :deep(.tab) {
  149. .nothing-here-text:not(:only-child) {
  150. height: calc(100% - 40px);
  151. }
  152. &.requests-tab {
  153. background-color: var(--white);
  154. margin-bottom: 20px;
  155. border-radius: 0 0 @border-radius @border-radius;
  156. max-height: 100%;
  157. padding: 15px 10px;
  158. overflow-y: auto;
  159. .scrollable-list {
  160. padding: 10px 0;
  161. }
  162. }
  163. }
  164. :deep(.tab-actionable-button) {
  165. width: calc(100% - 20px);
  166. height: 40px;
  167. border-radius: @border-radius;
  168. margin: 10px;
  169. position: absolute;
  170. bottom: 0;
  171. border: 0;
  172. background-color: var(--primary-color) !important;
  173. color: var(--white) !important;
  174. &:active,
  175. &:focus {
  176. border: 0;
  177. }
  178. &:hover,
  179. &:focus {
  180. background-color: var(--primary-color) !important;
  181. filter: brightness(90%);
  182. }
  183. }
  184. :deep(.scrollable-list) {
  185. width: 100%;
  186. max-height: calc(100% - 40px - 20px);
  187. overflow: auto;
  188. padding: 10px;
  189. .song-item:not(:last-of-type) {
  190. margin-bottom: 10px;
  191. }
  192. }
  193. </style>