mirror of
https://gitlab.com/RemixDev/deemix-webui.git
synced 2024-12-28 18:36:30 +00:00
changed directory structure of js and Vue files; added plugin for translation (can try it in settings)
This commit is contained in:
parent
1a300a6b1b
commit
42e44c45fe
|
@ -7,6 +7,8 @@ This is just the WebUI for deemix, it should be used with deemix-pyweb or someth
|
||||||
- Use Vue as much as possible
|
- Use Vue as much as possible
|
||||||
- First step: rewrite the app in Single File Components way ✅
|
- First step: rewrite the app in Single File Components way ✅
|
||||||
- Second step: Remove jQuery
|
- Second step: Remove jQuery
|
||||||
|
- Make i18n async (https://kazupon.github.io/vue-i18n/guide/lazy-loading.html)
|
||||||
|
- Use ES2020 async imports
|
||||||
- Make the UI look coherent
|
- Make the UI look coherent
|
||||||
- Style buttons
|
- Style buttons
|
||||||
- Style text inputs
|
- Style text inputs
|
||||||
|
|
5
package-lock.json
generated
5
package-lock.json
generated
|
@ -1588,6 +1588,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.11.tgz",
|
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.11.tgz",
|
||||||
"integrity": "sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ=="
|
"integrity": "sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ=="
|
||||||
},
|
},
|
||||||
|
"vue-i18n": {
|
||||||
|
"version": "8.18.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-8.18.2.tgz",
|
||||||
|
"integrity": "sha512-0X5nBTCZAVjlwcrPaYJwNs3iipBBTv0AUHwQUOa8yP3XbQGWKbRHqBb3OhCYtum/IHDD21d/df5Xd2VgyxbxfA=="
|
||||||
|
},
|
||||||
"vue-template-compiler": {
|
"vue-template-compiler": {
|
||||||
"version": "2.6.11",
|
"version": "2.6.11",
|
||||||
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.11.tgz",
|
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.11.tgz",
|
||||||
|
|
|
@ -20,7 +20,8 @@
|
||||||
"jquery": "^3.5.1",
|
"jquery": "^3.5.1",
|
||||||
"lodash-es": "^4.17.15",
|
"lodash-es": "^4.17.15",
|
||||||
"toastify-js": "^1.8.0",
|
"toastify-js": "^1.8.0",
|
||||||
"vue": "^2.6.11"
|
"vue": "^2.6.11",
|
||||||
|
"vue-i18n": "^8.18.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-alias": "^3.1.0",
|
"@rollup/plugin-alias": "^3.1.0",
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -11,7 +11,7 @@ import vue from 'rollup-plugin-vue'
|
||||||
const production = !process.env.ROLLUP_WATCH
|
const production = !process.env.ROLLUP_WATCH
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
input: 'src/js/app.js',
|
input: 'src/app.js',
|
||||||
output: [
|
output: [
|
||||||
{
|
{
|
||||||
file: 'public/js/bundle.js',
|
file: 'public/js/bundle.js',
|
||||||
|
@ -30,9 +30,13 @@ export default {
|
||||||
find: '@',
|
find: '@',
|
||||||
replacement: __dirname + '/src'
|
replacement: __dirname + '/src'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
find: '@js',
|
||||||
|
replacement: __dirname + '/src/js'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
find: '@components',
|
find: '@components',
|
||||||
replacement: __dirname + '/src/js/components'
|
replacement: __dirname + '/src/components'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -5,12 +5,13 @@ window.vol = {
|
||||||
preview_max_volume: 100
|
preview_max_volume: 100
|
||||||
}
|
}
|
||||||
|
|
||||||
import App from '@/js/App.vue'
|
import App from '@components/App.vue'
|
||||||
|
import i18n from '@/plugins/i18n'
|
||||||
|
|
||||||
import $ from 'jquery'
|
import $ from 'jquery'
|
||||||
import { socket } from '@/js/socket.js'
|
import { socket } from '@/utils/socket'
|
||||||
import { toast } from '@/js/toasts.js'
|
import { toast } from '@/utils/toasts'
|
||||||
import { init as initTabs } from '@/js/tabs.js'
|
import { init as initTabs } from '@js/tabs.js'
|
||||||
|
|
||||||
/* ===== App initialization ===== */
|
/* ===== App initialization ===== */
|
||||||
|
|
||||||
|
@ -21,7 +22,8 @@ function startApp() {
|
||||||
|
|
||||||
function mountApp() {
|
function mountApp() {
|
||||||
// TODO Remove the App instance from the window when deemix will be a complete Vue App
|
// TODO Remove the App instance from the window when deemix will be a complete Vue App
|
||||||
window.App = new Vue({
|
new Vue({
|
||||||
|
i18n,
|
||||||
render: h => h(App)
|
render: h => h(App)
|
||||||
}).$mount('#app')
|
}).$mount('#app')
|
||||||
}
|
}
|
|
@ -89,10 +89,10 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { isEmpty, orderBy } from 'lodash-es'
|
import { isEmpty, orderBy } from 'lodash-es'
|
||||||
import { socket } from '@/js/socket.js'
|
import { socket } from '@/utils/socket'
|
||||||
import Downloads from '@/js/downloads.js'
|
import Downloads from '@/utils/downloads'
|
||||||
import { showView } from '@/js/tabs.js'
|
import { showView } from '@js/tabs'
|
||||||
import EventBus from '@/js/EventBus'
|
import EventBus from '@/utils/EventBus'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'artist-tab',
|
name: 'artist-tab',
|
|
@ -112,10 +112,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { socket } from '@/js/socket.js'
|
import { socket } from '@/utils/socket'
|
||||||
import { showView } from '@/js/tabs.js'
|
import { showView } from '@js/tabs.js'
|
||||||
import Downloads from '@/js/downloads.js'
|
import Downloads from '@/utils/downloads'
|
||||||
import Utils from '@/js/utils.js'
|
import Utils from '@/utils/utils'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'the-charts-tab',
|
name: 'the-charts-tab',
|
|
@ -28,8 +28,8 @@ import TheAboutTab from '@components/TheAboutTab.vue'
|
||||||
import TheSettingsTab from '@components/TheSettingsTab.vue'
|
import TheSettingsTab from '@components/TheSettingsTab.vue'
|
||||||
import TheMainSearch from '@components/TheMainSearch.vue'
|
import TheMainSearch from '@components/TheMainSearch.vue'
|
||||||
|
|
||||||
import { debounce } from '@/js/utils.js'
|
import { debounce } from '@/utils/utils'
|
||||||
import EventBus from '@/js/EventBus.js'
|
import EventBus from '@/utils/EventBus.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
|
@ -25,8 +25,8 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import $ from 'jquery'
|
import $ from 'jquery'
|
||||||
import { socket } from '@/js/socket.js'
|
import { socket } from '@/utils/socket'
|
||||||
import { toast } from '@/js/toasts.js'
|
import { toast } from '@/utils/toasts'
|
||||||
|
|
||||||
const tabMinWidth = 250
|
const tabMinWidth = 250
|
||||||
const tabMaxWidth = 500
|
const tabMaxWidth = 500
|
|
@ -19,9 +19,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { changeTab } from '@/js/tabs.js'
|
import { changeTab } from '@js/tabs.js'
|
||||||
|
|
||||||
import EventBus from '@/js/EventBus'
|
import EventBus from '@/utils/EventBus'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'the-errors-tab',
|
name: 'the-errors-tab',
|
|
@ -182,11 +182,11 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { socket } from '@/js/socket.js'
|
import { socket } from '@/utils/socket'
|
||||||
import { showView, changeTab } from '@/js/tabs.js'
|
import { showView, changeTab } from '@js/tabs.js'
|
||||||
import Downloads from '@/js/downloads.js'
|
import Downloads from '@/utils/downloads'
|
||||||
import Utils from '@/js/utils.js'
|
import Utils from '@/utils/utils'
|
||||||
import { toast } from '@/js/toasts'
|
import { toast } from '@/utils/toasts'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'the-favorites-tab',
|
name: 'the-favorites-tab',
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="home_tab" class="main_tabcontent">
|
<div id="home_tab" class="main_tabcontent">
|
||||||
<h2 class="page_heading">Welcome to deemix</h2>
|
<h2 class="page_heading">{{ $t('welcome') }}</h2>
|
||||||
<section id="home_not_logged_in" class="home_section" ref="notLogged">
|
<section id="home_not_logged_in" class="home_section" ref="notLogged">
|
||||||
<p id="home_not_logged_text">You need to log into your deezer account before you can start downloading.</p>
|
<p id="home_not_logged_text">You need to log into your deezer account before you can start downloading.</p>
|
||||||
<button type="button" name="button" @click="openSettings">Open Settings</button>
|
<button type="button" name="button" @click="openSettings">Open Settings</button>
|
||||||
|
@ -53,9 +53,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { socket } from '@/js/socket.js'
|
import { socket } from '@/utils/socket'
|
||||||
import { showView } from '@/js/tabs.js'
|
import { showView } from '@js/tabs.js'
|
||||||
import Downloads from '@/js/downloads.js'
|
import Downloads from '@/utils/downloads'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'the-home-tab',
|
name: 'the-home-tab',
|
|
@ -100,10 +100,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { socket } from '@/js/socket.js'
|
import { socket } from '@/utils/socket'
|
||||||
import { showView } from '@/js/tabs.js'
|
import { showView } from '@js/tabs.js'
|
||||||
import Utils from '@/js/utils.js'
|
import Utils from '@/utils/utils'
|
||||||
import EventBus from '@/js/EventBus'
|
import EventBus from '@/utils/EventBus'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'the-link-analyzer-tab',
|
name: 'the-link-analyzer-tab',
|
|
@ -437,14 +437,14 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { socket } from '@/js/socket.js'
|
import { socket } from '@/utils/socket'
|
||||||
import { showView } from '@/js/tabs.js'
|
import { showView } from '@js/tabs.js'
|
||||||
import Downloads from '@/js/downloads.js'
|
import Downloads from '@/utils/downloads'
|
||||||
import Utils from '@/js/utils.js'
|
import Utils from '@/utils/utils'
|
||||||
import BaseLoadingPlaceholder from '@components/BaseLoadingPlaceholder.vue'
|
import BaseLoadingPlaceholder from '@components/BaseLoadingPlaceholder.vue'
|
||||||
|
|
||||||
import { changeTab } from '@/js/tabs.js'
|
import { changeTab } from '@js/tabs.js'
|
||||||
import EventBus from '@/js/EventBus.js'
|
import EventBus from '@/utils/EventBus.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'the-main-search-tab',
|
name: 'the-main-search-tab',
|
|
@ -38,7 +38,7 @@
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
import Downloads from '@/js/downloads.js'
|
import Downloads from '@/utils/downloads'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data: () => ({
|
data: () => ({
|
|
@ -18,11 +18,11 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { isValidURL } from '@/js/utils.js'
|
import { isValidURL } from '@/utils/utils'
|
||||||
import Downloads from '@/js/downloads.js'
|
import Downloads from '@/utils/downloads'
|
||||||
|
|
||||||
import EventBus from '@/js/EventBus.js'
|
import EventBus from '@/utils/EventBus.js'
|
||||||
import { socket } from '@/js/socket.js'
|
import { socket } from '@/utils/socket'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
methods: {
|
methods: {
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="settings_tab" class="main_tabcontent fixed_footer">
|
<div id="settings_tab" class="main_tabcontent fixed_footer">
|
||||||
<h2 class="page_heading">Settings</h2>
|
<h2 class="page_heading">{{ $t('settings.title') }}</h2>
|
||||||
|
|
||||||
<div id="logged_in_info" ref="loggedInInfo">
|
<div id="logged_in_info" ref="loggedInInfo">
|
||||||
<img id="settings_picture" src="" alt="Profile Picture" ref="userpicture" class="circle" />
|
<img id="settings_picture" src="" alt="Profile Picture" ref="userpicture" class="circle" />
|
||||||
|
@ -22,9 +22,26 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<a href="https://notabug.org/RemixDevs/DeezloaderRemix/wiki/Login+via+userToken" target="_blank">
|
<a href="https://notabug.org/RemixDevs/DeezloaderRemix/wiki/Login+via+userToken" target="_blank">
|
||||||
How do I get my own ARL?
|
{{ $t('settings.login.arl.question') }}
|
||||||
</a>
|
</a>
|
||||||
<button id="settings_btn_updateArl" @click="login" style="width:100%;">Update ARL</button>
|
<button id="settings_btn_updateArl" @click="login" style="width:100%;">
|
||||||
|
{{ $t('settings.login.arl.update') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-group">
|
||||||
|
<h3 class="settings-group__header settings-group__header--with-icon">
|
||||||
|
<i class="material-icons">language</i>{{ $t('settings.languages') }}
|
||||||
|
</h3>
|
||||||
|
<span
|
||||||
|
v-for="locale in locales"
|
||||||
|
:key="locale"
|
||||||
|
style="width: 50px; height: 50px; cursor:pointer; border: 1px solid var(--foreground); flex: 1 50px; display: flex; justify-content: center; align-items: center;"
|
||||||
|
:data-locale="locale"
|
||||||
|
@click="$i18n.locale = locale"
|
||||||
|
>
|
||||||
|
{{ locale.toUpperCase() }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="settings-group">
|
<div class="settings-group">
|
||||||
|
@ -514,13 +531,14 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { toast } from '@/js/toasts.js'
|
import { toast } from '@/utils/toasts'
|
||||||
import { socket } from '@/js/socket.js'
|
import { socket } from '@/utils/socket'
|
||||||
import EventBus from '@/js/EventBus'
|
import EventBus from '@/utils/EventBus'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'the-settings-tab',
|
name: 'the-settings-tab',
|
||||||
data: () => ({
|
data: () => ({
|
||||||
|
locales: [],
|
||||||
settings: { tags: {} },
|
settings: { tags: {} },
|
||||||
lastSettings: {},
|
lastSettings: {},
|
||||||
spotifyFeatures: {},
|
spotifyFeatures: {},
|
||||||
|
@ -624,6 +642,8 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.locales = this.$i18n.availableLocales
|
||||||
|
|
||||||
EventBus.$on('settingsTab:revertSettings', this.revertSettings)
|
EventBus.$on('settingsTab:revertSettings', this.revertSettings)
|
||||||
EventBus.$on('settingsTab:revertCredentials', this.revertCredentials)
|
EventBus.$on('settingsTab:revertCredentials', this.revertCredentials)
|
||||||
|
|
|
@ -85,7 +85,7 @@
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { changeTab } from '@/js/tabs.js'
|
import { changeTab } from '@js/tabs.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'the-sidebar',
|
name: 'the-sidebar',
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import $ from 'jquery'
|
import $ from 'jquery'
|
||||||
import EventBus from '@/js/EventBus'
|
import EventBus from '@/utils/EventBus'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data: () => ({
|
data: () => ({
|
|
@ -148,11 +148,11 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { isEmpty } from 'lodash-es'
|
import { isEmpty } from 'lodash-es'
|
||||||
import { socket } from '@/js/socket.js'
|
import { socket } from '@/utils/socket'
|
||||||
import { showView } from '@/js/tabs.js'
|
import { showView } from '@js/tabs.js'
|
||||||
import Downloads from '@/js/downloads.js'
|
import Downloads from '@/utils/downloads'
|
||||||
import Utils from '@/js/utils.js'
|
import Utils from '@/utils/utils'
|
||||||
import EventBus from '@/js/EventBus'
|
import EventBus from '@/utils/EventBus'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'tracklist-tab',
|
name: 'tracklist-tab',
|
|
@ -1,5 +1,5 @@
|
||||||
import { socket } from '@/js/socket.js'
|
import { socket } from '@/utils/socket'
|
||||||
import EventBus from '@/js/EventBus'
|
import EventBus from '@/utils/EventBus'
|
||||||
|
|
||||||
/* ===== Globals ====== */
|
/* ===== Globals ====== */
|
||||||
window.search_selected = ''
|
window.search_selected = ''
|
||||||
|
|
15
src/lang/en.js
Normal file
15
src/lang/en.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
const en = {
|
||||||
|
welcome: 'Welcome to deemix',
|
||||||
|
settings: {
|
||||||
|
title: 'Settings',
|
||||||
|
languages: 'Languages',
|
||||||
|
login: {
|
||||||
|
arl: {
|
||||||
|
question: 'How do I get my own ARL?',
|
||||||
|
update: 'Update ARL'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default en
|
15
src/lang/it.js
Normal file
15
src/lang/it.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
const it = {
|
||||||
|
welcome: 'Benvenuto su deemix',
|
||||||
|
settings: {
|
||||||
|
title: 'Impostazioni',
|
||||||
|
languages: 'Lingue',
|
||||||
|
login: {
|
||||||
|
arl: {
|
||||||
|
question: 'Come ottengo il mio ARL?',
|
||||||
|
update: 'Aggiorna ARL'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default it
|
25
src/plugins/i18n.js
Normal file
25
src/plugins/i18n.js
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import Vue from 'vue'
|
||||||
|
import VueI18n from 'vue-i18n'
|
||||||
|
|
||||||
|
// Languages
|
||||||
|
import it from '@/lang/it'
|
||||||
|
import en from '@/lang/en'
|
||||||
|
|
||||||
|
Vue.use(VueI18n)
|
||||||
|
|
||||||
|
const DEFAULT_LANG = 'en'
|
||||||
|
|
||||||
|
document.querySelector('html').setAttribute('lang', DEFAULT_LANG)
|
||||||
|
|
||||||
|
const locales = {
|
||||||
|
it,
|
||||||
|
en
|
||||||
|
}
|
||||||
|
|
||||||
|
const i18n = new VueI18n({
|
||||||
|
locale: DEFAULT_LANG,
|
||||||
|
fallbackLocale: DEFAULT_LANG,
|
||||||
|
messages: locales
|
||||||
|
})
|
||||||
|
|
||||||
|
export default i18n
|
|
@ -1,4 +1,4 @@
|
||||||
import { socket } from '@/js/socket.js'
|
import { socket } from '@/utils/socket'
|
||||||
|
|
||||||
function sendAddToQueue(url, bitrate = null) {
|
function sendAddToQueue(url, bitrate = null) {
|
||||||
if (url != '') {
|
if (url != '') {
|
|
@ -1,11 +1,11 @@
|
||||||
import Toastify from 'toastify-js'
|
import Toastify from 'toastify-js'
|
||||||
import $ from 'jquery'
|
import $ from 'jquery'
|
||||||
|
|
||||||
import { socket } from './socket.js'
|
import { socket } from '@/utils/socket'
|
||||||
|
|
||||||
let toastsWithId = {}
|
let toastsWithId = {}
|
||||||
|
|
||||||
export const toast = function (msg, icon = null, dismiss = true, id = null) {
|
export const toast = function(msg, icon = null, dismiss = true, id = null) {
|
||||||
if (toastsWithId[id]) {
|
if (toastsWithId[id]) {
|
||||||
let toastObj = toastsWithId[id]
|
let toastObj = toastsWithId[id]
|
||||||
let toastDOM = $(`div.toastify[toast_id=${id}]`)
|
let toastDOM = $(`div.toastify[toast_id=${id}]`)
|
||||||
|
@ -18,7 +18,7 @@ export const toast = function (msg, icon = null, dismiss = true, id = null) {
|
||||||
toastDOM.find('.toast-icon').html(icon)
|
toastDOM.find('.toast-icon').html(icon)
|
||||||
}
|
}
|
||||||
if (dismiss !== null && dismiss) {
|
if (dismiss !== null && dismiss) {
|
||||||
setTimeout(function () {
|
setTimeout(function() {
|
||||||
toastObj.hideToast()
|
toastObj.hideToast()
|
||||||
delete toastsWithId[id]
|
delete toastsWithId[id]
|
||||||
}, 3000)
|
}, 3000)
|
Loading…
Reference in a new issue