mirror of
https://codeberg.org/SafeTwitch/safetwitch.git
synced 2025-01-18 10:22:27 -05:00
Run eslint and fix all issues
This commit is contained in:
parent
0c38345bd1
commit
c31def6221
14 changed files with 333 additions and 272 deletions
|
@ -11,5 +11,6 @@ module.exports = {
|
||||||
],
|
],
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
ecmaVersion: 'latest'
|
ecmaVersion: 'latest'
|
||||||
}
|
},
|
||||||
|
'ignorePatterns': ["*.config.*"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { RouterView } from 'vue-router'
|
import { RouterView } from 'vue-router'
|
||||||
import NavbarItem from './components/Navbar.vue'
|
import NavbarItem from './components/NavbarView.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -1,27 +1,22 @@
|
||||||
import type { VideoJsPlayer } from "video.js";
|
import 'videojs-contrib-quality-levels'
|
||||||
import "videojs-contrib-quality-levels";
|
import type { QualityLevelList } from 'videojs-contrib-quality-levels'
|
||||||
import type { QualityLevelList } from "videojs-contrib-quality-levels";
|
|
||||||
|
|
||||||
export const createQualitySelector = (player: VideoJsPlayer) => {
|
export const createQualitySelector = (player: any) => {
|
||||||
const qualityLevels: QualityLevelList = player.qualityLevels()
|
const qualityLevels: QualityLevelList = player.qualityLevels()
|
||||||
let currentIndex = 0
|
|
||||||
|
|
||||||
player.hlsQualitySelector()
|
const myButton = player.controlBar.addChild('button')
|
||||||
var myButton = player.controlBar.addChild("button");
|
const myButtonDom = myButton.el()
|
||||||
var myButtonDom = myButton.el();
|
myButtonDom.innerHTML = 'Hello'
|
||||||
myButtonDom.innerHTML = "Hello";
|
|
||||||
|
|
||||||
myButtonDom.addEventListener('click', () => {
|
myButtonDom.addEventListener('click', () => {})
|
||||||
|
|
||||||
})
|
qualityLevels.on('change', function () {
|
||||||
|
console.log('Quality Level changed!')
|
||||||
|
console.log('New level:', qualityLevels[qualityLevels.selectedIndex])
|
||||||
|
console.log(qualityLevels)
|
||||||
|
|
||||||
qualityLevels.on('change', function() {
|
const qualityLabel = qualityLevels[qualityLevels.selectedIndex].height?.toString() + 'p'
|
||||||
console.log('Quality Level changed!');
|
|
||||||
console.log('New level:', qualityLevels[qualityLevels.selectedIndex]);
|
|
||||||
console.log(qualityLevels)
|
|
||||||
|
|
||||||
const qualityLabel = qualityLevels[qualityLevels.selectedIndex].height?.toString() + 'p'
|
myButtonDom.textContent = qualityLabel ?? ''
|
||||||
|
})
|
||||||
myButtonDom.textContent = qualityLabel ?? ''
|
|
||||||
});
|
|
||||||
}
|
}
|
|
@ -1,26 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
export default {};
|
|
||||||
|
|
||||||
import { RouterLink } from 'vue-router'
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="p-4 flex items-center justify-between bg-ctp-base text-white">
|
|
||||||
<h1 class="font-bold text-2xl">Naqvbar</h1>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<form class="relative">
|
|
||||||
<label for="searchBar" class="hidden">Search</label>
|
|
||||||
<v-icon name="io-search-outline" class="text-black absolute my-auto inset-y-0 left-2"></v-icon>
|
|
||||||
<input type="text" id="searchBar" name="searchBar" placeholder="Search" class="rounded-md p-1 pl-8 text-black">
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<ul class="inline-flex space-x-6 font-medium">
|
|
||||||
<router-link to="">Github</router-link>
|
|
||||||
<router-link to="/preferences">Preferences</router-link>
|
|
||||||
<router-link to="/about">About</router-link>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
32
frontend/src/components/NavbarView.vue
Normal file
32
frontend/src/components/NavbarView.vue
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<script lang="ts">
|
||||||
|
export default {}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="p-4 flex items-center justify-between bg-ctp-base text-white">
|
||||||
|
<h1 class="font-bold text-2xl">Naqvbar</h1>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<form class="relative">
|
||||||
|
<label for="searchBar" class="hidden">Search</label>
|
||||||
|
<v-icon
|
||||||
|
name="io-search-outline"
|
||||||
|
class="text-black absolute my-auto inset-y-0 left-2"
|
||||||
|
></v-icon>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="searchBar"
|
||||||
|
name="searchBar"
|
||||||
|
placeholder="Search"
|
||||||
|
class="rounded-md p-1 pl-8 text-black"
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class="inline-flex space-x-6 font-medium">
|
||||||
|
<router-link to="">Github</router-link>
|
||||||
|
<router-link to="/preferences">Preferences</router-link>
|
||||||
|
<router-link to="/about">About</router-link>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -1,16 +1,66 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {
|
import { ref, type Ref } from 'vue'
|
||||||
props: {
|
|
||||||
isLive: {
|
export default {
|
||||||
type: Boolean,
|
props: {
|
||||||
default() {
|
isLive: {
|
||||||
return false;
|
type: Boolean,
|
||||||
}
|
default() {
|
||||||
}
|
return false
|
||||||
},
|
}
|
||||||
|
},
|
||||||
|
channelName: {
|
||||||
|
type: String
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
let messages: Ref<
|
||||||
|
{ username: string; channel: string; message: string; messageType: string }[]
|
||||||
|
> = ref([])
|
||||||
|
let ws = new WebSocket('ws://localhost:7000')
|
||||||
|
|
||||||
|
return {
|
||||||
|
ws,
|
||||||
|
messages,
|
||||||
|
props
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
const chatList = this.$refs.chatList as Element
|
||||||
|
|
||||||
|
this.ws.onmessage = (message) => {
|
||||||
|
if (message.data !== 'OK') {
|
||||||
|
this.messages.push(JSON.parse(message.data))
|
||||||
|
this.scrollToBottom(chatList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ws.onopen = (data) => {
|
||||||
|
console.log(data)
|
||||||
|
this.ws.send('JOIN ' + this.props.channelName)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getChat() {
|
||||||
|
return this.messages
|
||||||
|
},
|
||||||
|
scrollToBottom(el: Element) {
|
||||||
|
el.scrollTop = el.scrollHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div v-if="isLive" class="p-3 bg-ctp-crust rounded-lg w-full max-w-xs">
|
<div v-if="isLive" class="p-3 bg-ctp-crust rounded-lg w-full max-w-xs flex flex-col">
|
||||||
</div>
|
<ul class="overflow-y-scroll h-[82vh]" ref="chatList">
|
||||||
|
<li v-for="message in getChat()" :key="messages.indexOf(message)">
|
||||||
|
<div class="text-white inline-flex">
|
||||||
|
<p class="text-sm">
|
||||||
|
<strong class="text-ctp-pink font-bold text-sm">{{ message.username }}</strong
|
||||||
|
>: {{ message.message }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
|
@ -5,10 +5,12 @@
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
// Importing video-js
|
// Importing video-js
|
||||||
import videojs from 'video.js';
|
import videojs from 'video.js'
|
||||||
import { createQualitySelector } from '../assets/qualitySelector'
|
import qualitySelector from 'videojs-hls-quality-selector'
|
||||||
import "videojs-contrib-quality-levels";
|
import qualityLevels from 'videojs-contrib-quality-levels'
|
||||||
import "../assets/vjs-theme.css"
|
|
||||||
|
videojs.registerPlugin('qualityLevels', qualityLevels)
|
||||||
|
videojs.registerPlugin('hlsQualitySelector', qualitySelector)
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'VideoJsPlayer',
|
name: 'VideoJsPlayer',
|
||||||
|
@ -16,7 +18,7 @@ export default {
|
||||||
options: {
|
options: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default() {
|
default() {
|
||||||
return {};
|
return {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -30,22 +32,8 @@ export default {
|
||||||
// when the component is being mounted
|
// when the component is being mounted
|
||||||
mounted() {
|
mounted() {
|
||||||
this.player = videojs('video-player', this.options, () => {
|
this.player = videojs('video-player', this.options, () => {
|
||||||
this.player.log('video player ready', this);
|
this.player.hlsQualitySelector({ displayCurrentQuality: true })
|
||||||
|
})
|
||||||
this.player.hlsQualitySelector({
|
|
||||||
displayCurrentQuality: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
createQualitySelector(this.player)
|
|
||||||
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// destroying the video player
|
|
||||||
// when the component is being destroyed
|
|
||||||
beforeDestroy() {
|
|
||||||
if (this.player) {
|
|
||||||
this.player.dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script><link rel="stylesheet" href="style.css">
|
</script>
|
||||||
|
|
|
@ -8,13 +8,29 @@ import 'video.js/dist/video-js.css'
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
import { OhVueIcon, addIcons } from "oh-vue-icons";
|
import { OhVueIcon, addIcons } from 'oh-vue-icons'
|
||||||
import { IoSearchOutline, IoLink, FaCircleNotch, BiTwitter, BiInstagram, BiDiscord, BiYoutube, BiTiktok } from "oh-vue-icons/icons";
|
import {
|
||||||
|
IoSearchOutline,
|
||||||
|
IoLink,
|
||||||
|
FaCircleNotch,
|
||||||
|
BiTwitter,
|
||||||
|
BiInstagram,
|
||||||
|
BiDiscord,
|
||||||
|
BiYoutube,
|
||||||
|
BiTiktok
|
||||||
|
} from 'oh-vue-icons/icons'
|
||||||
|
|
||||||
addIcons(IoSearchOutline, IoLink, FaCircleNotch, BiTwitter, BiInstagram, BiDiscord, BiYoutube, BiTiktok)
|
addIcons(
|
||||||
|
IoSearchOutline,
|
||||||
|
IoLink,
|
||||||
|
FaCircleNotch,
|
||||||
|
BiTwitter,
|
||||||
|
BiInstagram,
|
||||||
|
BiDiscord,
|
||||||
|
BiYoutube,
|
||||||
|
BiTiktok
|
||||||
|
)
|
||||||
|
|
||||||
app.component("v-icon", OhVueIcon);
|
app.component('v-icon', OhVueIcon)
|
||||||
app.use(router)
|
app.use(router)
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
import HomeView from '../views/HomeView.vue'
|
|
||||||
import UserView from '../views/UserView.vue'
|
import UserView from '../views/UserView.vue'
|
||||||
import PageNotFound from '../views/PageNotFound.vue'
|
import PageNotFound from '../views/PageNotFound.vue'
|
||||||
import PrivacyPageView from '../views/PrivacyPageView.vue'
|
import PrivacyPageView from '../views/PrivacyPageView.vue'
|
||||||
|
@ -7,11 +6,6 @@ import PrivacyPageView from '../views/PrivacyPageView.vue'
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
routes: [
|
routes: [
|
||||||
{
|
|
||||||
path: '/',
|
|
||||||
name: 'home',
|
|
||||||
component: HomeView
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/privacy',
|
path: '/privacy',
|
||||||
name: 'about',
|
name: 'about',
|
||||||
|
@ -21,7 +15,7 @@ const router = createRouter({
|
||||||
path: '/:username',
|
path: '/:username',
|
||||||
component: UserView
|
component: UserView
|
||||||
},
|
},
|
||||||
{ path: '/:pathMatch(.*)*', component: PageNotFound}
|
{ path: '/:pathMatch(.*)*', component: PageNotFound }
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
|
|
||||||
</template>
|
|
|
@ -1,11 +1,11 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {};
|
export default {}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col items-center pt-10 font-bold text-5xl text-white">
|
<div class="flex flex-col items-center pt-10 font-bold text-5xl text-white">
|
||||||
<h1>oops....</h1>
|
<h1>oops....</h1>
|
||||||
<h1>this page wasn't found(◞‸◟;)</h1>
|
<h1>this page wasn't found(◞‸◟;)</h1>
|
||||||
<h2 class="text-4xl">maybe go <RouterLink to="/" class="text-gray-500">home</RouterLink>?</h2>
|
<h2 class="text-4xl">maybe go <RouterLink to="/" class="text-gray-500">home</RouterLink>?</h2>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
|
@ -1,26 +1,24 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {};
|
export default {}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<article class="prose prose-invert border-2 border-ctp-peach max-w-prose bg-ctp-crust rounded-lg mx-auto p-8 pt-10 text-white">
|
<article
|
||||||
<h1>Privacy Policy</h1>
|
class="prose prose-invert border-2 border-ctp-peach max-w-prose bg-ctp-crust rounded-lg mx-auto p-8 pt-10 text-white"
|
||||||
<p>For the oficial instance, no logs are kept except for
|
>
|
||||||
when an error is met that affects the user is encounered.
|
<h1>Privacy Policy</h1>
|
||||||
An example of this is when data retrieval fails when a user
|
<p>
|
||||||
requests. No identifying information is kept except for the
|
For the oficial instance, no logs are kept except for when an error is met that affects the
|
||||||
time of request. below is an example of this data</p>
|
user is encounered. An example of this is when data retrieval fails when a user requests. No
|
||||||
|
identifying information is kept except for the time of request. below is an example of this
|
||||||
|
data
|
||||||
|
</p>
|
||||||
|
|
||||||
<code class="">
|
<code class="">
|
||||||
{
|
{ "endpoint":"/api/users/chibidoki", "level":"warn","message": "No element found for selector:
|
||||||
"endpoint":"/api/users/chibidoki",
|
li.InjectLayout-sc-1i43xsx-0:nth-child(2) > a:nth-child(1) > div:nth-child(1) >
|
||||||
"level":"warn","message":
|
div:nth-child(1) > p:nth-child(1)", "origin":"http://localhost:5173",
|
||||||
"No element found for selector: li.InjectLayout-sc-1i43xsx-0:nth-child(2) > a:nth-child(1) > div:nth-child(1) > div:nth-child(1) > p:nth-child(1)",
|
"reqId":"fed6f1f6-403f-4d6a-9943-3d07ea7bf9bb", "timestamp":"2023-03-07T22:42:37.982Z" }
|
||||||
"origin":"http://localhost:5173",
|
</code>
|
||||||
"reqId":"fed6f1f6-403f-4d6a-9943-3d07ea7bf9bb",
|
</article>
|
||||||
"timestamp":"2023-03-07T22:42:37.982Z"
|
|
||||||
}
|
|
||||||
</code>
|
|
||||||
|
|
||||||
</article>
|
|
||||||
</template>
|
</template>
|
|
@ -1,162 +1,182 @@
|
||||||
<script lang="ts" >
|
<script lang="ts">
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from 'vue-router'
|
||||||
import type { StreamerData } from '../../../server/types/scraping/Streamer'
|
import type { StreamerData } from '../../../server/types/scraping/Streamer'
|
||||||
import VideoPlayer from '../components/VideoPlayer.vue'
|
import VideoPlayer from '../components/VideoPlayer.vue'
|
||||||
import TwitchChat from '../components/TwitchChat.vue'
|
import TwitchChat from '../components/TwitchChat.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
async setup() {
|
async setup() {
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const username = route.params.username
|
const username = route.params.username
|
||||||
|
|
||||||
const getUser = async () => {
|
const getUser = async () => {
|
||||||
const res = await fetch(`http://localhost:7000/api/users/${username}`)
|
const res = await fetch(`http://localhost:7000/api/users/${username}`)
|
||||||
|
|
||||||
if (res.status !== 200) {
|
if (res.status !== 200) {
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
|
|
||||||
if(!data.code) {
|
if (!data.code) {
|
||||||
return {
|
return {
|
||||||
status: "error",
|
status: 'error',
|
||||||
code: "error"
|
code: 'error'
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...data
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const data: StreamerData = await res.json()
|
|
||||||
data.pfp = `http://localhost:7000/proxy/img?imageUrl=${encodeURIComponent(data.pfp)}`
|
|
||||||
return data
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = ref()
|
|
||||||
onMounted(async () => {
|
|
||||||
const fetchedUser = await getUser()
|
|
||||||
data.value = fetchedUser
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data,
|
...data
|
||||||
videoOptions: {
|
|
||||||
autoplay: true,
|
|
||||||
controls: true,
|
|
||||||
sources: [
|
|
||||||
{
|
|
||||||
src:
|
|
||||||
`http://localhost:7000/proxy/stream/${username}/hls.m3u8`,
|
|
||||||
type: 'application/vnd.apple.mpegurl',
|
|
||||||
}],
|
|
||||||
fluid: true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
components: {
|
|
||||||
VideoPlayer,
|
|
||||||
TwitchChat
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
truncate(value: string, length: number) {
|
|
||||||
if (value.length > length) {
|
|
||||||
return value.substring(0, length) + "...";
|
|
||||||
} else {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const data: StreamerData = await res.json()
|
||||||
|
data.pfp = `http://localhost:7000/proxy/img?imageUrl=${encodeURIComponent(data.pfp)}`
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = ref()
|
||||||
|
onMounted(async () => {
|
||||||
|
const fetchedUser = await getUser()
|
||||||
|
data.value = fetchedUser
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
videoOptions: {
|
||||||
|
autoplay: true,
|
||||||
|
controls: true,
|
||||||
|
sources: [
|
||||||
|
{
|
||||||
|
src: `http://localhost:7000/proxy/stream/${username}/hls.m3u8`,
|
||||||
|
type: 'application/vnd.apple.mpegurl'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
fluid: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
VideoPlayer,
|
||||||
|
TwitchChat
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
truncate(value: string, length: number) {
|
||||||
|
if (value.length > length) {
|
||||||
|
return value.substring(0, length) + '...'
|
||||||
|
} else {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="!data" id="loadingDiv" class="flex mx-auto justify-center bg-ctp-crust rounded-lg w-2/3 p-2 text-white">
|
<div
|
||||||
<div class="flex space-x-3">
|
v-if="!data"
|
||||||
<h1 class="text-4xl font-bold"> Searching... </h1>
|
id="loadingDiv"
|
||||||
<v-icon name="fa-circle-notch" class="animate-spin w-10 h-10"></v-icon>
|
class="flex mx-auto justify-center bg-ctp-crust rounded-lg w-2/3 p-2 text-white"
|
||||||
</div>
|
>
|
||||||
|
<div class="flex space-x-3">
|
||||||
|
<h1 class="text-4xl font-bold">Searching...</h1>
|
||||||
|
<v-icon name="fa-circle-notch" class="animate-spin w-10 h-10"></v-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-else-if="data.status === 'error'"
|
||||||
|
class="flex flex-col max-w-prose justify-center text-center mx-auto p-6 bg-ctp-crust rounded-lg text-white"
|
||||||
|
>
|
||||||
|
<div class="mb-6">
|
||||||
|
<h1 class="font-bold text-5xl">oops...</h1>
|
||||||
|
<p class="font-bold text-3xl">this wasn't supposed to happen</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="data.status === 'error'" class="flex flex-col max-w-prose justify-center text-center mx-auto p-6 bg-ctp-crust rounded-lg text-white">
|
<p class="text-xl">
|
||||||
<div class="mb-6">
|
the server was encountered an error while retriving the data, and now we're here :3
|
||||||
<h1 class="font-bold text-5xl">oops...</h1>
|
</p>
|
||||||
<p class="font-bold text-3xl">this wasn't supposed to happen</p>
|
|
||||||
|
<div class="mt-5">
|
||||||
|
<p class="text-xl">please contact the administrator with this code</p>
|
||||||
|
<p class="text-xl">error identifier: {{ data.code }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="w-full inline-flex space-x-4 justify-center p-4">
|
||||||
|
<div class="flex flex-wrap bg-ctp-crust p-6 rounded-lg max-w-prose min-w-[65ch] text-white">
|
||||||
|
<div v-if="data.isLive" class="w-full mx-auto rounded-lg mb-5">
|
||||||
|
<video-player :options="videoOptions"> </video-player>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="w-full flex-wrap p-3">
|
||||||
|
<div class="inline-flex w-2/3">
|
||||||
|
<div class="w-20 h-20 relative">
|
||||||
|
<img
|
||||||
|
:src="data.pfp"
|
||||||
|
class="rounded-full border-4 p-0.5 w-auto h-20"
|
||||||
|
:style="`border-color: ${data.colorHex};`"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
v-if="data.isLive"
|
||||||
|
class="absolute top-16 right-[1.2rem] bg-ctp-red font-bold text-sm p-1.5 py-0.5 rounded-md"
|
||||||
|
>LIVE</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ml-3 content-between">
|
||||||
|
<h1 class="text-4xl font-bold">{{ data.username }}</h1>
|
||||||
|
<h1 v-if="!data.stream" class="font-bold text-md self-end">
|
||||||
|
{{ data.followersAbbv }} Followers
|
||||||
|
</h1>
|
||||||
|
<div v-else class="w-[12rem]">
|
||||||
|
<p class="text-sm font-bold text-gray-200 self-end">
|
||||||
|
{{ truncate(data.stream.title, 75) }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="inline-flex w-1/3 float-right h-full text-right">
|
||||||
<p class="text-xl">the server was encountered an error while retriving the data, and now we're here :3</p>
|
<div v-if="!data.isLive" class="w-full">
|
||||||
|
<p
|
||||||
<div class="mt-5">
|
class="font-bold bg-ctp-mantle p-3 py-2 rounded-lg w-min float-right border-2 border-ctp-red"
|
||||||
<p class="text-xl">please contact the administrator with this code</p>
|
>
|
||||||
<p class="text-xl">error identifier: {{ data.code }}</p>
|
OFFLINE
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div v-else class="w-full">
|
||||||
|
<ul class="text-xs font-bold text-right space-x-1 space-y-1 overflow-y-auto">
|
||||||
|
<li
|
||||||
|
v-for="tag in data.stream.tags"
|
||||||
|
:key="tag"
|
||||||
|
class="inline-flex bg-ctp-mantle p-1.5 px-2 rounded-md"
|
||||||
|
>
|
||||||
|
{{ tag }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-ctp-mantle m-5 p-5 pt-3 rounded-lg w-full space-y-3">
|
||||||
|
<div class="inline-flex w-full">
|
||||||
|
<span class="pr-3 font-bold text-3xl">About</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="mb-5">{{ data.about }}</p>
|
||||||
|
|
||||||
|
<hr class="my-auto w-full bg-gray-200 rounded-full opacity-40" />
|
||||||
|
|
||||||
|
<ul class="flex font-semibold text-md justify-start flex-wrap flex-row">
|
||||||
|
<li v-for="link in data.socials" :key="link">
|
||||||
|
<a :href="link.link" class="text-white hover:text-gray-400 mr-4">
|
||||||
|
<v-icon :name="`bi-${link.type}`" class="w-6 h-6 mr-1"></v-icon>
|
||||||
|
<span>{{ link.name }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else class="w-full inline-flex space-x-4 justify-center p-4">
|
<twitch-chat :isLive="data.isLive" :channelName="data.username"></twitch-chat>
|
||||||
<div class="flex flex-wrap bg-ctp-crust p-6 rounded-lg max-w-prose min-w-[65ch] text-white">
|
</div>
|
||||||
<div v-if="data.isLive" class="w-full mx-auto rounded-lg mb-5">
|
|
||||||
<video-player
|
|
||||||
:options="videoOptions">
|
|
||||||
</video-player>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="w-full flex-wrap p-3">
|
|
||||||
<div class="inline-flex w-2/3">
|
|
||||||
<div class="w-20 h-20 relative">
|
|
||||||
<img :src="data.pfp" class="rounded-full border-4 p-0.5 w-auto h-20" :style="`border-color: ${data.colorHex};`">
|
|
||||||
<span v-if="data.isLive" class="absolute top-16 right-[1.2rem] bg-ctp-red font-bold text-sm p-1.5 py-0.5 rounded-md">LIVE</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="ml-3 content-between">
|
|
||||||
<h1 class="text-4xl font-bold">{{ data.username }}</h1>
|
|
||||||
<h1 v-if="!data.stream" class="font-bold text-md self-end ">{{ data.followersAbbv }} Followers</h1>
|
|
||||||
<div v-else class="w-[12rem]">
|
|
||||||
<p class="text-sm font-bold text-gray-200 self-end"> {{ truncate(data.stream.title, 75) }} </p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="inline-flex w-1/3 float-right h-full text-right">
|
|
||||||
<div v-if="!data.isLive" class="w-full">
|
|
||||||
<p class="font-bold bg-ctp-mantle p-3 py-2 rounded-lg w-min float-right border-2 border-ctp-red">OFFLINE</p>
|
|
||||||
</div>
|
|
||||||
<div v-else class="w-full">
|
|
||||||
<ul class="text-xs font-bold text-right space-x-1 space-y-1 overflow-y-auto">
|
|
||||||
<li v-for="tag in data.stream.tags" class="inline-flex bg-ctp-mantle p-1.5 px-2 rounded-md">
|
|
||||||
{{ tag }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="bg-ctp-mantle m-5 p-5 pt-3 rounded-lg w-full space-y-3">
|
|
||||||
<div class="inline-flex w-full">
|
|
||||||
<span class="pr-3 font-bold text-3xl">About</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p class="mb-5">{{ data.about }}</p>
|
|
||||||
|
|
||||||
<hr class="my-auto w-full bg-gray-200 rounded-full opacity-40" />
|
|
||||||
|
|
||||||
<ul class="flex font-semibold text-md justify-start flex-wrap flex-row">
|
|
||||||
<li v-for="link in data.socials">
|
|
||||||
<a :href="link.link" class="text-white hover:text-gray-400 mr-4">
|
|
||||||
<v-icon :name="`bi-${link.type}`" class="w-6 h-6 mr-1"></v-icon>
|
|
||||||
<span>{{ link.name }}</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<twitch-chat :isLive="data.isLive"></twitch-chat>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
Loading…
Add table
Reference in a new issue