Initial commit
@@ -0,0 +1,22 @@
|
||||
@props(['icon' => '', 'classes' => ''])
|
||||
|
||||
<div
|
||||
class="w-full flex flex-col gap-y-4 rounded overflow-hidden bg-white pb-3 shadow-sm {{ $classes }}">
|
||||
<div class="flex gap-x-2 border-b bg-gray-50 p-3">
|
||||
<div
|
||||
class="max-w-[50px] max-h-[50px] min-w-[50px] min-h-[50px] rounded-full relative flex items-center justify-center {{ $icon }}">
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col justify-center text-sm">
|
||||
<p class="font-semibold text-black">{{ $title }}</p>
|
||||
|
||||
@if(isset($underTitle))
|
||||
<p>{{ $underTitle }}</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="px-3 text-[14px]">
|
||||
{{ $slot }}
|
||||
</section>
|
||||
</div>
|
||||
@@ -0,0 +1,11 @@
|
||||
@props(['primaryColor', 'secondaryColor'])
|
||||
|
||||
<div class="flex h-[45px] col-span-4 sm:col-span-2 md:col-span-1">
|
||||
<div class="w-1/3 {{ $secondaryColor }} rounded-l flex items-center justify-center">
|
||||
{{ $icon }}
|
||||
</div>
|
||||
|
||||
<div class="p-2 rounded rounded-l-none {{ $primaryColor }} w-2/3 font-semibold flex justify-center items-center">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 3h1.386c.51 0 .955.343 1.087.835l.383 1.437M7.5 14.25a3 3 0 0 0-3 3h15.75m-12.75-3h11.218c1.121-2.3 2.1-4.684 2.924-7.138a60.114 60.114 0 0 0-16.536-1.84M7.5 14.25 5.106 5.272M6 20.25a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Zm12.75 0a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Z" />
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 478 B |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"
|
||||
stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 218 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="h-6 w-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 487 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
|
||||
class="h-5 w-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 363 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="h-6 w-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21 11.25v8.25a1.5 1.5 0 0 1-1.5 1.5H5.25a1.5 1.5 0 0 1-1.5-1.5v-8.25M12 4.875A2.625 2.625 0 1 0 9.375 7.5H12m0-2.625V7.5m0-2.625A2.625 2.625 0 1 1 14.625 7.5H12m0 0V21m-8.625-9.75h18c.621 0 1.125-.504 1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125h-18c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125Z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 518 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="h-5 w-5">
|
||||
<path
|
||||
d="M11.47 3.84a.75.75 0 011.06 0l8.69 8.69a.75.75 0 101.06-1.06l-8.689-8.69a2.25 2.25 0 00-3.182 0l-8.69 8.69a.75.75 0 001.061 1.06l8.69-8.69z" />
|
||||
<path
|
||||
d="M12 5.432l8.159 8.159c.03.03.06.058.091.086v6.198c0 1.035-.84 1.875-1.875 1.875H15a.75.75 0 01-.75-.75v-4.5a.75.75 0 00-.75-.75h-3a.75.75 0 00-.75.75V21a.75.75 0 01-.75.75H5.625a1.875 1.875 0 01-1.875-1.875v-6.198a2.29 2.29 0 00.091-.086L12 5.43z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 541 B |
@@ -0,0 +1,4 @@
|
||||
<svg {{ $attributes }} fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 300 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
|
||||
class="h-5 w-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 377 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="h-4 w-4">
|
||||
<path fill-rule="evenodd"
|
||||
d="M7.5 6a4.5 4.5 0 119 0 4.5 4.5 0 01-9 0zM3.751 20.105a8.25 8.25 0 0116.498 0 .75.75 0 01-.437.695A18.683 18.683 0 0112 22.5c-2.786 0-5.433-.608-7.812-1.7a.75.75 0 01-.437-.695z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 354 B |
@@ -0,0 +1,193 @@
|
||||
<div x-data="radioPlayer()" x-init="initPlayer()"
|
||||
class="radio-player-widget"
|
||||
:class="{ 'radio-player-visible': isVisible, 'radio-player-hidden': !isVisible }"
|
||||
x-show="showWidget"
|
||||
style="display: none;">
|
||||
|
||||
<!-- Toggle Button -->
|
||||
<button
|
||||
x-show="!isExpanded"
|
||||
@click="toggleExpand()"
|
||||
class="fixed bottom-4 right-4 z-50 w-14 h-14 rounded-full bg-gradient-to-r from-amber-500 to-amber-600 text-white shadow-lg hover:shadow-amber-500/30 flex items-center justify-center transition-all hover:scale-110"
|
||||
>
|
||||
<svg class="w-6 h-6 animate-pulse" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19V6l12-3v13M9 19c0 1.105-1.343 2-3 2s-3-.895-3-2 1.343-2 3-2 3 .895 3 2zM9 10l12-3"/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Expanded Player -->
|
||||
<div
|
||||
x-show="isExpanded"
|
||||
class="fixed bottom-4 right-4 z-50 w-96 rounded-2xl shadow-2xl overflow-hidden bg-gray-900"
|
||||
>
|
||||
<!-- Header -->
|
||||
<div class="bg-gradient-to-r from-amber-500 to-amber-600 p-4 flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-2 h-2 bg-white rounded-full animate-pulse"></div>
|
||||
<span class="text-white font-semibold">RADIO</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span x-text="listenerCount" class="text-white/80 text-sm">--</span>
|
||||
<span class="text-white/60 text-xs">luisteraars</span>
|
||||
<button @click="toggleExpand()" class="text-white/80 hover:text-white">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="p-4 text-white">
|
||||
<!-- DJ Info -->
|
||||
<div x-show="currentDJ" class="flex items-center gap-4 mb-4">
|
||||
<div class="w-12 h-12 rounded-full bg-gradient-to-br from-amber-400 to-amber-600 flex items-center justify-center overflow-hidden">
|
||||
<img x-bind:src="djAvatar" x-bind:alt="djName" class="w-full h-full object-cover">
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xs text-amber-400">PRESENTATOR</p>
|
||||
<p x-text="djName" class="font-semibold">--</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Now Playing -->
|
||||
<div class="bg-gray-800 rounded-lg p-3 mb-4">
|
||||
<p class="text-xs text-gray-400 mb-1">NU DRAAIT</p>
|
||||
<p x-text="trackTitle" class="font-medium truncate">Radio</p>
|
||||
<p x-show="trackArtist" x-text="trackArtist" class="text-sm text-gray-400 truncate"></p>
|
||||
</div>
|
||||
|
||||
<!-- Controls -->
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<button @click="togglePlay()" class="w-14 h-14 rounded-full bg-amber-500 hover:bg-amber-400 flex items-center justify-center transition shadow-lg">
|
||||
<svg x-show="!isPlaying" class="w-6 h-6 ml-1" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M8 5v14l11-7z"/>
|
||||
</svg>
|
||||
<svg x-show="isPlaying" class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div class="flex-1 flex items-center gap-2">
|
||||
<svg class="w-4 h-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.536 8.464a5 5 0 010 7.072M18.364 5.636a9 9 0 010 12.728"/>
|
||||
</svg>
|
||||
<input type="range" x-model="volume" min="0" max="100" class="flex-1 h-1 bg-gray-700 rounded-lg appearance-none cursor-pointer">
|
||||
</div>
|
||||
|
||||
<a href="/community/radio" class="text-amber-400 hover:text-amber-300">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<audio x-ref="audioPlayer" preload="none">
|
||||
<source x-bind:src="streamUrl" type="audio/mpeg">
|
||||
</audio>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function radioPlayer() {
|
||||
return {
|
||||
showWidget: false,
|
||||
showOnRadioPage: false,
|
||||
showGlobal: false,
|
||||
isVisible: true,
|
||||
isExpanded: false,
|
||||
isPlaying: false,
|
||||
streamUrl: '',
|
||||
volume: 80,
|
||||
listenerCount: '--',
|
||||
trackTitle: 'Radio',
|
||||
trackArtist: '',
|
||||
currentDJ: null,
|
||||
djName: '--',
|
||||
djAvatar: '',
|
||||
|
||||
initPlayer() {
|
||||
this.checkVisibility();
|
||||
this.loadSettings();
|
||||
setInterval(() => this.updateListeners(), 30000);
|
||||
setInterval(() => this.updateNowPlaying(), 15000);
|
||||
this.updateVisibilityByUrl();
|
||||
},
|
||||
|
||||
checkVisibility() {
|
||||
if (!this.showWidget) {
|
||||
this.showWidget = false;
|
||||
return;
|
||||
}
|
||||
if (this.showGlobal) {
|
||||
this.showWidget = true;
|
||||
} else if (this.showOnRadioPage) {
|
||||
this.updateVisibilityByUrl();
|
||||
} else {
|
||||
this.showWidget = false;
|
||||
}
|
||||
},
|
||||
|
||||
updateVisibilityByUrl() {
|
||||
const path = window.location.pathname;
|
||||
this.showWidget = path === '/community/radio' || path.startsWith('/community/radio');
|
||||
},
|
||||
|
||||
loadSettings() {
|
||||
fetch('/api/radio/config')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.enabled && data.stream_url && data.widget_enabled) {
|
||||
this.streamUrl = data.streamUrl || data.stream_url;
|
||||
this.showGlobal = data.widget_show_globally || false;
|
||||
this.checkVisibility();
|
||||
} else {
|
||||
this.showWidget = false;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.showWidget = false;
|
||||
});
|
||||
},
|
||||
|
||||
toggleExpand() {
|
||||
this.isExpanded = !this.isExpanded;
|
||||
},
|
||||
|
||||
togglePlay() {
|
||||
const audio = this.$refs.audioPlayer;
|
||||
if (this.isPlaying) {
|
||||
audio.pause();
|
||||
this.isPlaying = false;
|
||||
} else {
|
||||
audio.play().catch(() => {});
|
||||
this.isPlaying = true;
|
||||
}
|
||||
},
|
||||
|
||||
updateListeners() {
|
||||
fetch('/api/radio/listeners')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
this.listenerCount = data.count.toLocaleString();
|
||||
})
|
||||
.catch(() => {
|
||||
this.listenerCount = '--';
|
||||
});
|
||||
},
|
||||
|
||||
updateNowPlaying() {
|
||||
fetch('/api/radio/now-playing')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.title) {
|
||||
this.trackTitle = data.title;
|
||||
this.trackArtist = data.artist || '';
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
new class extends Component
|
||||
{
|
||||
//
|
||||
};
|
||||
?>
|
||||
|
||||
<div>
|
||||
{{-- Simplicity is an acquired taste. - Katharine Gerould --}}
|
||||
</div>
|
||||