Version 1.2.0 - Ajout des suggestions et de paramètres
All checks were successful
Frontend Deployment / deploy-frontend (push) Successful in 35s
All checks were successful
Frontend Deployment / deploy-frontend (push) Successful in 35s
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chopin-frontend",
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite --host --port 8080",
|
||||
|
@@ -31,8 +31,6 @@ const router = useRouter();
|
||||
|
||||
const userSettings = ref(null);
|
||||
|
||||
//FIXME: Set to dispatcher
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
.box {
|
||||
|
@@ -25,8 +25,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="server-actions">
|
||||
<Button v-if="gestion" @click="settings.open()"><Icon icon="fa-solid fa-gear"/>Gestion</Button>
|
||||
<Button :style="{width: gestion ? '' : '100%'}" @click="router.push(`/servers`)"><Icon icon="fa-solid fa-arrow-right"/>Changer de serveur</Button>
|
||||
<Button icon="fa-solid fa-gear" v-if="gestion" @click="settings.open()">Gestion</Button>
|
||||
<Button icon="fa-solid fa-arrow-right" :style="{width: gestion ? '' : '100%'}" @click="router.push(`/servers`)">Changer de serveur</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -112,9 +112,6 @@ const playerOpen = ref(false);
|
||||
const durationBar = ref(null);
|
||||
const buffering = ref(false);
|
||||
|
||||
//TODO: Rework Animation Both Side
|
||||
//FIXME: Animation weird 550px
|
||||
|
||||
const playerMobile = ref(null);
|
||||
const playerMobileToggle = ref(null);
|
||||
const playerHeight = ref(0);
|
||||
|
@@ -12,7 +12,7 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Button class="add-playlist" @click="openModal()"><Icon icon="fa-add"/> Ajouter une playlist</Button>
|
||||
<Button icon="fa-add" class="add-playlist" @click="openModal()">Ajouter une playlist</Button>
|
||||
</div>
|
||||
<Modal icon="fa-list" title="Ajouter une Playlist" ref="modal">
|
||||
<div class="p-modal-content">
|
||||
@@ -23,7 +23,7 @@
|
||||
<label>Importer depuis Youtube</label>
|
||||
</div>
|
||||
|
||||
<Button :disabled="(!isYoutube && newPlaylistTitle.trim() === '') || (isYoutube && urlLink?.trim() === '')" @click="addPlaylist">Ajouter</Button>
|
||||
<Button icon="fa-add" :disabled="(!isYoutube && newPlaylistTitle.trim() === '') || (isYoutube && urlLink?.trim() === '')" @click="addPlaylist">Ajouter</Button>
|
||||
<p class="text-loading" v-if="isLoading"><Icon icon="fa-spinner" spin-pulse /> Création en cours...</p>
|
||||
<p class="text-loading" v-if="error"><Error> {{ error }}</Error></p>
|
||||
</div>
|
||||
|
@@ -1,7 +1,11 @@
|
||||
<template>
|
||||
<div class="search">
|
||||
<Icon color="#FFFFFF" icon="fa-solid fa-magnifying-glass" style="width: 20px;" />
|
||||
<input name="search" ref="searchBar" type="text" placeholder="Insérer votre recherche ici" v-model="searchQuery" />
|
||||
<div class="search-bar">
|
||||
<input autocomplete="off" name="search" ref="searchBar" type="text" placeholder="Insérer votre recherche ici" v-model="searchQuery" />
|
||||
<Icon color="#FFFFFF" v-if="searchQuery.trim() !== ''" icon="fa-solid fa-xmark" class="clear-icon" @click="searchQuery = ''" />
|
||||
<SearchSuggestions v-if="searchQuery.trim() !== '' && suggestClose" :query="searchQuery" />
|
||||
</div>
|
||||
<div class="search-actions">
|
||||
<IconAction
|
||||
icon="fa-solid fa-cloud-arrow-up"
|
||||
@@ -25,17 +29,24 @@ import { onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
import IconAction from '../UI/IconAction.vue';
|
||||
import { IORequest } from '@/utils/IORequest';
|
||||
import Events from '@/utils/Events';
|
||||
import SearchSuggestions from '@/components/Widget/View/Search/SearchSuggestions.vue';
|
||||
|
||||
const searchBar = ref(null);
|
||||
const searchQuery = ref('');
|
||||
const suggestClose = ref(true);
|
||||
|
||||
onMounted(() => {
|
||||
searchBar.value.addEventListener('change', find);
|
||||
searchBar.value.addEventListener('change', () => {
|
||||
find();
|
||||
suggestClose.value = true;
|
||||
});
|
||||
|
||||
searchBar.value.addEventListener('keydown', (event) => {
|
||||
suggestClose.value = true;
|
||||
});
|
||||
});
|
||||
|
||||
//TODO: Faire un systême de suggestions.
|
||||
|
||||
function find() {
|
||||
function find(queryByResult) {
|
||||
// If on mobile close the keyboard
|
||||
if (window.innerWidth < 768) {
|
||||
searchBar.value.blur();
|
||||
@@ -43,9 +54,15 @@ function find() {
|
||||
if (searchQuery.value.trim() === '') {
|
||||
return;
|
||||
}
|
||||
if(queryByResult == "result") {
|
||||
Events.emit("SEARCH_RESULT_SENDED");
|
||||
}
|
||||
Events.emit("SEARCH_STARTED");
|
||||
IORequest("/SEARCH", (data) => {
|
||||
Events.emit("SEARCH_RESULT", {data: data, query: searchQuery.value});
|
||||
if(queryByResult == "result") {
|
||||
Events.emit("SEARCH_RESULT_SENDED");
|
||||
}
|
||||
}, searchQuery.value);
|
||||
}
|
||||
|
||||
@@ -53,6 +70,13 @@ Events.on("VIEW_CLOSED", () => {
|
||||
searchQuery.value = '';
|
||||
});
|
||||
|
||||
Events.on("SEARCH_RESULT_SELECTED", (result) => {
|
||||
searchQuery.value = result;
|
||||
suggestClose.value = false;
|
||||
Events.emit("SEARCH_RESULT_SENDED");
|
||||
find("result");
|
||||
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
@@ -68,6 +92,19 @@ Events.on("VIEW_CLOSED", () => {
|
||||
box-shadow: 2px 2px 10px 0px rgba(0, 0, 0, 0.50);
|
||||
}
|
||||
|
||||
.clear-icon {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search input {
|
||||
border: none;
|
||||
outline: none;
|
||||
|
@@ -26,8 +26,6 @@ const disableClass = computed(() => {
|
||||
return props.disabled ? props.colorLower ? 'disabled color-lower' : 'disabled' : '';
|
||||
});
|
||||
|
||||
//TODO: Refactor every button component to use the icon prop
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
button {
|
||||
|
@@ -65,6 +65,7 @@ function open() {
|
||||
if (modal.value) {
|
||||
modal.value.style.display = 'flex';
|
||||
}
|
||||
Events.emit('modal:open');
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
<script setup>
|
||||
import IconAction from './IconAction.vue';
|
||||
import { ref, onMounted, watch, onUnmounted } from 'vue';
|
||||
import { useSlots } from 'vue';
|
||||
|
@@ -2,7 +2,11 @@
|
||||
<div class="subsonics-logo">
|
||||
<LogoDark class="img" v-if="globalStore.theme == 'light'"/>
|
||||
<LogoLight class="img" v-else/>
|
||||
<h1>Subsonics</h1>
|
||||
<div v-if="dev" class="text-p">
|
||||
<h1>Subsonics</h1>
|
||||
<p>Developement</p>
|
||||
</div>
|
||||
<h1 v-if="!dev">Subsonics</h1>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
@@ -10,6 +14,8 @@ import LogoDark from '@/assets/LogoDark.vue';
|
||||
import LogoLight from '@/assets/LogoLight.vue';
|
||||
import { useGlobalStore } from '@/stores/globalStore';
|
||||
|
||||
const dev = import.meta.env.DEV;
|
||||
|
||||
const globalStore = useGlobalStore();
|
||||
</script>
|
||||
<style scoped>
|
||||
@@ -27,6 +33,25 @@ const globalStore = useGlobalStore();
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.text-p {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.text-p h1 {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.text-p p {
|
||||
margin: 0 !important;
|
||||
font-family: 'Gunship', sans-serif;
|
||||
position: absolute;
|
||||
bottom: -10px;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px),
|
||||
screen and (max-height: 607px) {
|
||||
.img {
|
||||
|
@@ -3,9 +3,11 @@
|
||||
<ModalTree title="Utilisateurs" icon="fa-solid fa-users">
|
||||
<GuildUsers :server="server"/>
|
||||
</ModalTree>
|
||||
<ModalTree v-if="userStore.userInfo.labels.includes('ADMIN') || userStore.userInfo.identity.id == server.owner" title="Sécurité" icon="fa-solid fa-shield-halved">
|
||||
<GuildSecurity :server="server"/>
|
||||
</ModalTree>
|
||||
<ModalTree title="Statistiques" icon="fa-solid fa-chart-simple">
|
||||
<GuildStats :server="server"/>
|
||||
|
||||
</ModalTree>
|
||||
</Modal>
|
||||
</template>
|
||||
@@ -15,13 +17,14 @@ import ModalTree from '@/components/UI/ModalTree.vue';
|
||||
import { ref } from 'vue';
|
||||
import GuildUsers from './Settings/GuildUsers.vue';
|
||||
import GuildStats from './Settings/GuildStats.vue';
|
||||
import GuildSecurity from './Settings/GuildSecurity.vue';
|
||||
import Events from '@/utils/Events';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
const userStore = useUserStore();
|
||||
|
||||
const modal = ref(null);
|
||||
|
||||
//TODO: Ajouter la sécurité des roles pour empêcher l'utilisation publique du Bot
|
||||
//TODO: Ajout de Log pour serveur
|
||||
//TODO: Paramétérer une liste des channels autorisé !
|
||||
|
||||
const props = defineProps({
|
||||
server: {
|
||||
|
93
src/components/Widget/Guild/Settings/GuildSecurity.vue
Normal file
93
src/components/Widget/Guild/Settings/GuildSecurity.vue
Normal file
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<div class="setting">
|
||||
<p class="text-secondary"><Icon icon="fa-solid fa-user-shield" /> Autoriser les utilisateurs avec un rôle spécifique</p>
|
||||
<Selector v-model="roleSelected" ref="roleSelector" v-if="roles.length > 0 && modalOpen">
|
||||
<Tag v-for="role in roles" :key="role.id" :value="role.id" :color="decimalToHex(role.color)">{{role.name.replace("@everyone", "Tout le monde (@everyone)")}}</Tag>
|
||||
</Selector>
|
||||
<Button @click="updateRole" icon="fa-solid fa-rotate">Mettre à jour</Button>
|
||||
<Success v-if="roleUpdated">Rôle mis à jour avec succès !</Success>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
import Selector from '@/components/UI/Selector.vue';
|
||||
import { IORequest } from '@/utils/IORequest';
|
||||
import Tag from '@/components/UI/Tag.vue';
|
||||
import Events from '@/utils/Events';
|
||||
import Button from '@/components/UI/Button.vue';
|
||||
import Success from '@/components/UI/Success.vue';
|
||||
|
||||
const roles = ref([])
|
||||
const roleSelector = ref(null);
|
||||
const roleUpdated = ref(false);
|
||||
const roleSelected = ref(null);
|
||||
const modalOpen = ref(false);
|
||||
|
||||
const props = defineProps({
|
||||
server: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
//TODO: FINISH IMPLEMENTATION
|
||||
|
||||
onMounted(() => {
|
||||
actualizeRoles();
|
||||
})
|
||||
|
||||
Events.on("modal:open", () => {
|
||||
actualizeRoles();
|
||||
modalOpen.value = true;
|
||||
roleUpdated.value = false;
|
||||
})
|
||||
|
||||
Events.on("modal:close", () => {
|
||||
modalOpen.value = false;
|
||||
roleSelected.value = null;
|
||||
roleUpdated.value = false;
|
||||
})
|
||||
|
||||
function actualizeRoles() {
|
||||
IORequest("/OWNER/ROLES/GET", (data) => {
|
||||
roles.value = data;
|
||||
roleSelected.value = null;
|
||||
});
|
||||
}
|
||||
|
||||
function updateRole() {
|
||||
console.log(roleSelector.value?.firstSlot());
|
||||
IORequest("/OWNER/ROLES/SET", (data) => {
|
||||
actualizeRoles();
|
||||
roleUpdated.value = true;
|
||||
setTimeout(() => {
|
||||
roleUpdated.value = false;
|
||||
}, 3000);
|
||||
}, roles.value.find(r => r.id === roleSelected.value) || null);
|
||||
}
|
||||
|
||||
function decimalToHex(decimal) {
|
||||
if(decimal == 0) return "var(--quaternary)"; // Default color
|
||||
let hex = Number(decimal).toString(16);
|
||||
hex = "000000".substring(0, 6 - hex.length) + hex;
|
||||
return `#${hex}`;
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
.text-secondary {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.875rem;
|
||||
margin-bottom: 0.5rem;
|
||||
text-decoration: underline;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.setting {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
</style>
|
@@ -41,7 +41,7 @@
|
||||
<p class="selectorp" :value="playlist.playlistId" v-for="playlist in userStore.playlists" :key="playlist.playlistId"><Icon :icon="playlist.type === 'youtube' ? 'fa-brands fa-youtube' : 'fa-solid fa-music'" /> {{ playlist.title }}</p>
|
||||
</Selector>
|
||||
<p v-else class="info-no">Vous n'avez pas encore de playlist. Créez-en une pour sauvegarder ce titre.</p>
|
||||
<Button :disabled="userStore.playlists?.length === 0" @click="Events.emit('video:add', { video: nextList[selectedIndex], playlistId: playlistSelector.firstSlot().props.value }); saveModal.close()"><Icon icon="fa-solid fa-save" /> Sauvegarder</Button>
|
||||
<Button :disabled="userStore.playlists?.length === 0" @click="Events.emit('video:add', { video: nextList[selectedIndex], playlistId: playlistSelector.firstSlot().props.value }); saveModal.close()" icon="fa-solid fa-save">Sauvegarder</Button>
|
||||
</Modal>
|
||||
</section>
|
||||
</template>
|
||||
|
@@ -30,7 +30,7 @@
|
||||
<p class="selectorp" :value="playlist.playlistId" v-for="playlist in userStore.playlists" :key="playlist.playlistId"><Icon :icon="playlist.type === 'youtube' ? 'fa-brands fa-youtube' : 'fa-solid fa-music'" /> {{ playlist.title }}</p>
|
||||
</Selector>
|
||||
<p v-else class="info-no">Vous n'avez pas encore de playlist. Créez-en une pour sauvegarder ce titre.</p>
|
||||
<Button :disabled="userStore.playlists?.length === 0" @click="Events.emit('video:add', { video: previousList[selectedIndex], playlistId: playlistSelector.firstSlot().props.value }); saveModal.close()"><Icon icon="fa-solid fa-save" /> Sauvegarder</Button>
|
||||
<Button :disabled="userStore.playlists?.length === 0" @click="Events.emit('video:add', { video: previousList[selectedIndex], playlistId: playlistSelector.firstSlot().props.value }); saveModal.close()" icon="fa-solid fa-save">Sauvegarder</Button>
|
||||
</Modal>
|
||||
</section>
|
||||
</template>
|
||||
|
@@ -5,7 +5,8 @@
|
||||
<ServerOnlinePicture class="sop" :key="server.id" v-if="server.members.length > 0" :members="server.members"/>
|
||||
<section>
|
||||
<Error v-if="userStore.userInfo.labels.includes('BAN_' + server.id)">Banni</Error>
|
||||
<Button color-lower :disabled="userStore.userInfo.labels.includes('BAN_' + server.id)" class="btn" @click="access()"><Icon icon="fa-solid fa-right-to-bracket"/>Accéder</Button>
|
||||
<Error v-else-if="server.restricted">Restreint</Error>
|
||||
<Button color-lower :disabled="userStore.userInfo.labels.includes('BAN_' + server.id) || server.restricted" class="btn" @click="access()" icon="fa-solid fa-right-to-bracket">Accéder</Button>
|
||||
</section>
|
||||
</div>
|
||||
</Box>
|
||||
|
@@ -54,7 +54,7 @@ watch(showForm, (newValue) => {
|
||||
|
||||
<template>
|
||||
<p>Si vous rencontrez un problème, vous pouvez le signaler via ce formulaire. Vous pouvez être contacté en cas de besoin par Raphix pour plus d'informations.</p>
|
||||
<Button v-if="!showForm" @click="showForm = !showForm" class="margin"><Icon icon="fa-solid fa-paper-plane" /> Faire un rapport de bug</Button>
|
||||
<Button v-if="!showForm" @click="showForm = !showForm" class="margin" icon="fa-solid fa-paper-plane"> Faire un rapport de bug</Button>
|
||||
<Success class="margin" v-if="sended">Votre rapport a été envoyé avec succès</Success>
|
||||
<div v-if="showForm" class="report-content">
|
||||
<p>Catégorie</p>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<p class="privacy">Toutes les données récupérées sont à des fins <strong>strictement</strong> nécessaires au bon fonctionnement de l'application.</p>
|
||||
|
||||
<Button @click="openModal()"><Icon icon="fa-solid fa-trash"/> Supprimer mon compte</Button>
|
||||
<Button @click="openModal()" icon="fa-solid fa-trash"> Supprimer mon compte</Button>
|
||||
<Modal icon="fa-solid fa-trash" ref="deleteAccountModal" title="Supprimer mon compte">
|
||||
<p class="warning">
|
||||
Êtes-vous sûr de vouloir supprimer votre compte ? <br/>
|
||||
|
@@ -10,13 +10,13 @@
|
||||
<Modal icon="fa-solid fa-video" title="Actions" ref="modal">
|
||||
<Video :video="video"/>
|
||||
<Button v-if="globalStore.currentChannel" @click="playSong(false)"><AddList /> Ajouter à la liste de lecture</Button>
|
||||
<Button v-if="globalStore.currentChannel" @click="playSong(true)"><Icon icon="fa-solid fa-play"/> Lire maintenant</Button>
|
||||
<Button icon="fa-solid fa-play" v-if="globalStore.currentChannel" @click="playSong(true)"> Lire maintenant</Button>
|
||||
<div v-if="!globalStore.currentChannel" >
|
||||
<p class="text-secondary">Connectez vous à un salon audio sur le serveur {{ globalStore.actualServer ? globalStore.actualServer.name : '' }}, pour lancer un titre</p>
|
||||
<ActualChannel/>
|
||||
</div>
|
||||
<Button v-if="!props.delete && video.type != 'attachment'" @click="saveModal.open()"><Icon icon="fa-solid fa-save" /> Enregistrer dans une playlist</Button>
|
||||
<Button v-if="props.delete" @click="Events.emit('video:delete', { video: props.video })"><Icon icon="fa-solid fa-trash" /> Supprimer</Button>
|
||||
<Button v-if="!props.delete && video.type != 'attachment'" @click="saveModal.open()" icon="fa-solid fa-save"> Enregistrer dans une playlist</Button>
|
||||
<Button v-if="props.delete" @click="Events.emit('video:delete', { video: props.video })" icon="fa-solid fa-trash"> Supprimer</Button>
|
||||
</Modal>
|
||||
<Modal ref="saveModal" icon="fa-save" title="Sauvegarder dans une playlist">
|
||||
<Video class="save-video" :video="video"/>
|
||||
@@ -24,7 +24,7 @@
|
||||
<p class="selectorp" :value="playlist.playlistId" v-for="playlist in userStore.playlists" :key="playlist.playlistId"><Icon :icon="playlist.type === 'youtube' ? 'fa-brands fa-youtube' : 'fa-solid fa-music'" /> {{ playlist.title }}</p>
|
||||
</Selector>
|
||||
<p v-else class="info-no">Vous n'avez pas encore de playlist. Créez-en une pour sauvegarder ce titre.</p>
|
||||
<Button :disabled="userStore.playlists?.length === 0" @click="Events.emit('video:add', { video: props.video, playlistId: playlistSelector.firstSlot().props.value }); saveModal.close()"><Icon icon="fa-solid fa-save" /> Sauvegarder</Button>
|
||||
<Button :disabled="userStore.playlists?.length === 0" @click="Events.emit('video:add', { video: props.video, playlistId: playlistSelector.firstSlot().props.value }); saveModal.close()" icon="fa-solid fa-save" > Sauvegarder</Button>
|
||||
</Modal>
|
||||
</template>
|
||||
<script setup>
|
||||
|
@@ -4,7 +4,7 @@
|
||||
<img v-if="results.thumbnail" class="search-playlist-thumbnail" :src="results.thumbnail" alt="Playlist Thumbnail" />
|
||||
<div v-else class="search-playlist-thumbnail"><div class="defaultIcon"><Icon icon="fa-music" /></div></div>
|
||||
<div class="search-playlist-info">
|
||||
<p class="search-playlist-title">{{ results.title }} <Icon @click="openPlaylistPage()" class="link-icon" icon="fa-solid fa-link" /></p>
|
||||
<p class="search-playlist-title">{{ results.title }} <Icon v-if="results.url" @click="openPlaylistPage()" class="link-icon" icon="fa-solid fa-link" /></p>
|
||||
<p class="search-playlist-stats"><Tag color="var(--text-warning)">{{ results.songs.length }} titres</Tag><Tag v-if="results.views" color="var(--text-success)">{{ results.views }} vues</Tag ><Tag v-if="results.songs.length > 0">Durée : {{ results.readduration }}</Tag></p>
|
||||
<div @click="openAuthorPage()" class="search-playlist-author-info">
|
||||
<img v-if="results.authorAvatar" :src="results.authorAvatar" alt="Author Thumbnail" class="search-playlist-author" />
|
||||
|
116
src/components/Widget/View/Search/SearchSuggestions.vue
Normal file
116
src/components/Widget/View/Search/SearchSuggestions.vue
Normal file
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<div ref="suggestions" class="search-suggestions">
|
||||
<ul>
|
||||
<li class="search-suggestion" v-for="result in results" :key="result.id" @click="selectResult(result[0])">
|
||||
{{ result[0] }}
|
||||
<Icon class="search-icon" icon="fa-solid fa-arrow-up-right-from-square" style="float: right;"/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
import Events from '@/utils/Events';
|
||||
|
||||
const props = defineProps({
|
||||
query: String
|
||||
});
|
||||
|
||||
const results = ref([]);
|
||||
const suggestions = ref(null);
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('click', (event) => {
|
||||
if (suggestions.value && !suggestions.value.contains(event.target)) {
|
||||
results.value = [];
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('keydown', (event) => {
|
||||
if (event.key === 'Escape') {
|
||||
results.value = [];
|
||||
}
|
||||
|
||||
if (event.key === 'Enter') {
|
||||
results.value = [];
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
watch(() => props.query, (newQuery) => {
|
||||
if (newQuery) {
|
||||
fetchSuggest(newQuery)
|
||||
.then(data => {
|
||||
results.value = data[1];
|
||||
});
|
||||
} else {
|
||||
results.value = [];
|
||||
}
|
||||
});
|
||||
|
||||
function fetchSuggest(query) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const callbackName = "jsonp_callback_" + Math.random().toString(36).substr(2, 5);
|
||||
|
||||
window[callbackName] = function(data) {
|
||||
resolve(data);
|
||||
delete window[callbackName];
|
||||
script.remove();
|
||||
};
|
||||
|
||||
const script = document.createElement("script");
|
||||
script.src = `https://suggestqueries.google.com/complete/search?client=youtube&ds=yt&q=${encodeURIComponent(query)}&callback=${callbackName}`;
|
||||
script.onerror = reject;
|
||||
document.body.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
||||
function selectResult(result) {
|
||||
Events.emit("SEARCH_RESULT_SELECTED", result);
|
||||
|
||||
}
|
||||
|
||||
Events.on("SEARCH_RESULT_SENDED", () => {
|
||||
results.value = [];
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.search-suggestions {
|
||||
position: absolute;
|
||||
background: var(--secondary);
|
||||
opacity: 0.95;
|
||||
z-index: 1000;
|
||||
width: 100%;
|
||||
top: 40px;
|
||||
max-height: 30vh;
|
||||
overflow-y: auto;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.search-suggestions ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.search-suggestions li {
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
display: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.search-suggestion:hover .search-icon {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.search-suggestions li:hover {
|
||||
background: var(--tertiary);
|
||||
}
|
||||
</style>
|
@@ -4,7 +4,7 @@
|
||||
<h2><Icon icon="fa-folder"/> Mes fichiers</h2>
|
||||
<Box box-class="area-container" padding="close" level="second">
|
||||
<div class="upload-area">
|
||||
<Button :color-lower="status === 'download'" :disabled="status === 'download'" class="upload-button" @click="uploadFile()"><Icon icon="fa-upload"/> Ajouter des fichiers</Button>
|
||||
<Button :color-lower="status === 'download'" :disabled="status === 'download'" class="upload-button" @click="uploadFile()" icon="fa-upload"> Ajouter des fichiers</Button>
|
||||
<p v-if="status === 'download'" class="upload-file-name" ref="uploadStatus"><Icon icon='fa-spinner' spin-pulse/> Téléchargement en cours...</p>
|
||||
<p v-else-if="status === 'error'" class="upload-status" ref="uploadStatus"><Error>Erreur lors du téléchargement</Error></p>
|
||||
<p v-else-if="status === 'toohigh'" class="upload-status" ref="uploadStatus"><Error>Le fichier est trop volumineux</Error></p>
|
||||
|
@@ -1,10 +1,11 @@
|
||||
<script setup>
|
||||
import DefaultSplash from '@/components/UI/DefaultSplash.vue';
|
||||
import MusicAnimation from '@/components/UI/MusicAnimation.vue';
|
||||
|
||||
import { version } from '@/../package.json';
|
||||
const defaultMessage = "On s'accorde et on prépare le concert !";
|
||||
const connectMsg = "Erreur de connexion au serveur : xhr poll error"
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
interuptionMessage: {
|
||||
type: String,
|
||||
@@ -26,6 +27,7 @@ const props = defineProps({
|
||||
<p v-if="interuptionMessage" class="error"><Icon icon="fa-solid fa-circle-xmark"/> {{ interuptionMessage }}</p>
|
||||
<p v-else>{{ defaultMessage }}</p>
|
||||
<MusicAnimation />
|
||||
<p class="version">Version : {{ version }} - Chopin</p>
|
||||
</DefaultSplash>
|
||||
</template>
|
||||
<style scoped>
|
||||
@@ -63,6 +65,12 @@ const props = defineProps({
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.version {
|
||||
font-size: 0.6em;
|
||||
margin-bottom: 0;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px),
|
||||
screen and (max-height: 607px) {
|
||||
h1 {
|
||||
|
@@ -10,7 +10,7 @@
|
||||
</div>
|
||||
<br>
|
||||
<router-link class="no-decoration" to="/">
|
||||
<Button><Icon icon="fa-solid fa-house"/> Revenir au concert</Button>
|
||||
<Button icon="fa-solid fa-house"> Revenir au concert</Button>
|
||||
</router-link>
|
||||
</DefaultSplash>
|
||||
|
||||
|
@@ -103,7 +103,7 @@ onMounted(() => {
|
||||
</Box>
|
||||
<br/>
|
||||
<router-link class="no-decoration" to="/">
|
||||
<Button><Icon icon="fa-solid fa-house"/> Revenir au concert</Button>
|
||||
<Button icon="fa-solid fa-house"> Revenir au concert</Button>
|
||||
</router-link>
|
||||
<!-- Add more content here as needed -->
|
||||
</DefaultSplash>
|
||||
|
@@ -61,7 +61,7 @@ function inviteSubsonics() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button :disabled="!hasLink" @click="inviteSubsonics()"><Icon icon="fa-solid fa-user-plus"/>Inviter Subsonics</Button>
|
||||
<Button :disabled="!hasLink" @click="inviteSubsonics()" icon="fa-solid fa-user-plus">Inviter Subsonics</Button>
|
||||
</div>
|
||||
</Box>
|
||||
<Account class="full"/>
|
||||
|
@@ -31,7 +31,7 @@ onMounted(() => {
|
||||
</Box>
|
||||
<br>
|
||||
<router-link class="no-decoration" to="/">
|
||||
<Button><Icon icon="fa-solid fa-house"/> Revenir au concert</Button>
|
||||
<Button icon="fa-solid fa-house">Revenir au concert</Button>
|
||||
</router-link>
|
||||
<!-- Add more content here as needed -->
|
||||
</DefaultSplash>
|
||||
|
Reference in New Issue
Block a user