Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
AURA
dashboard-clock
Commits
26cf5bc8
Commit
26cf5bc8
authored
Sep 04, 2020
by
david
Browse files
Improved loading behaviour.
#1
parent
4869c19e
Changes
4
Hide whitespace changes
Inline
Side-by-side
public/global.css
deleted
100644 → 0
View file @
4869c19e
html
,
body
{
position
:
relative
;
width
:
100%
;
height
:
100%
;
}
body
{
color
:
#333
;
margin
:
0
;
padding
:
8px
;
box-sizing
:
border-box
;
font-family
:
-apple-system
,
BlinkMacSystemFont
,
"Segoe UI"
,
Roboto
,
Oxygen-Sans
,
Ubuntu
,
Cantarell
,
"Helvetica Neue"
,
sans-serif
;
}
a
{
color
:
rgb
(
0
,
100
,
200
);
text-decoration
:
none
;
}
a
:hover
{
text-decoration
:
underline
;
}
a
:visited
{
color
:
rgb
(
0
,
80
,
160
);
}
label
{
display
:
block
;
}
input
,
button
,
select
,
textarea
{
font-family
:
inherit
;
font-size
:
inherit
;
padding
:
0.4em
;
margin
:
0
0
0.5em
0
;
box-sizing
:
border-box
;
border
:
1px
solid
#ccc
;
border-radius
:
2px
;
}
input
:disabled
{
color
:
#ccc
;
}
input
[
type
=
"range"
]
{
height
:
0
;
}
button
{
color
:
#333
;
background-color
:
#f4f4f4
;
outline
:
none
;
}
button
:disabled
{
color
:
#999
;
}
button
:not
(
:disabled
)
:active
{
background-color
:
#ddd
;
}
button
:focus
{
border-color
:
#666
;
}
public/index.html
View file @
26cf5bc8
...
@@ -19,9 +19,10 @@
...
@@ -19,9 +19,10 @@
logo=
"https://o94.at/themes/custom/radio_orange/logo1.png"
logo=
"https://o94.at/themes/custom/radio_orange/logo1.png"
logosize=
"180px"
logosize=
"180px"
api=
"http://localhost:8008/api/v1/"
api=
"http://localhost:8008/api/v1/"
css=
"
http://localhost:8008/css/aura
.css"
css=
"
/sample/o94
.css"
unknowntitle=
"Unbekannter Titel"
unknowntitle=
"Unbekannter Titel"
noschedulemessage=
"Keine weiteren Sendungen!"
nocurrentschedule=
"Derzeit keine Sendung"
nonextschedule=
"Keine weiteren Sendungen"
playoffset=
3
>
playoffset=
3
>
</aura-clock>
</aura-clock>
...
...
public/sample/o94.css
0 → 100644
View file @
26cf5bc8
src/StudioClock.svelte
View file @
26cf5bc8
<svelte:options
tag=
"aura-clock"
/>
<svelte:options
tag=
"aura-clock"
/>
<main
bind:this=
{rootElement}
></main>
<main
bind:this=
{rootElement}
></main>
<script>
<script>
import
{
onMount
}
from
'
svelte
'
;
import
{
onMount
,
afterUpdate
}
from
'
svelte
'
;
export
let
css
=
""
;
export
let
css
=
""
;
export
let
api
=
"
http://localhost:8008/api/v1/
"
;
export
let
api
=
"
http://localhost:8008/api/v1/
"
;
export
let
name
=
"
Studio Clock
"
;
export
let
name
=
"
Studio Clock
"
;
export
let
logo
=
"
https://gitlab.servus.at/aura/meta/-/raw/master/images/aura-logo.png
"
;
export
let
logo
=
"
https://gitlab.servus.at/aura/meta/-/raw/master/images/aura-logo.png
"
;
export
let
logosize
=
"
100px
"
;
export
let
logosize
=
"
100px
"
;
export
let
noschedulemessage
=
"
Nothing scheduled next!
"
;
export
let
nocurrentschedule
=
"
Right now, there's no show playing
"
;
export
let
nonextschedule
=
"
Nothing scheduled next!
"
;
export
let
unknowntitle
=
"
Unknown Title
"
;
export
let
unknowntitle
=
"
Unknown Title
"
;
export
let
playoffset
=
3
;
export
let
playoffset
=
3
;
let
time
=
new
Date
();
let
time
=
new
Date
();
let
queryCurrent
=
"
clock
"
;
let
queryCurrent
=
"
clock
"
;
let
rootElement
;
let
rootElement
;
let
data
;
let
promise
;
let
currentTrack
=
null
;
let
prevClockData
=
null
;
let
clockData
=
null
;
let
currentTrackElement
=
null
;
let
timeLeft
;
let
timeLeft
;
let
scheduleTimeLeft
=
0
;
let
reloadTime
=
10
;
let
reloadWait
=
0
;
// these automatically update when `time`
// these automatically update when `time`
// changes, because of the `$:` prefix
// changes, because of the `$:` prefix
...
@@ -25,27 +30,41 @@
...
@@ -25,27 +30,41 @@
$
:
minutes
=
time
.
getMinutes
();
$
:
minutes
=
time
.
getMinutes
();
$
:
seconds
=
time
.
getSeconds
();
$
:
seconds
=
time
.
getSeconds
();
data
=
fetchApi
(
queryCurrent
);
promise
=
fetchApi
(
queryCurrent
);
/* When component is mounted to the DOM */
/* When component is mounted to the DOM */
onMount
(()
=>
{
onMount
(()
=>
{
const
interval
=
setInterval
(()
=>
{
const
interval
=
setInterval
(()
=>
{
time
=
new
Date
();
time
=
new
Date
();
timeLeft
-=
1
;
timeLeft
-=
1
;
scheduleTimeLeft
-=
1
;
if
(
timeLeft
<=
0
||
data
==
null
)
{
currentTrack
=
null
;
/* End of track or end of schedule - load new data */
data
=
null
;
if
(
timeLeft
<=
0
||
scheduleTimeLeft
<=
0
)
{
data
=
fetchApi
(
queryCurrent
);
/* For some seconds refresh every second, to work around API timing delays */
if
(
timeLeft
<=
0
&&
timeLeft
>=
-
3
||
scheduleTimeLeft
<=
0
&&
scheduleTimeLeft
>=
-
3
||
reloadWait
==
0
)
{
promise
=
fetchApi
(
queryCurrent
);
reloadWait
=
reloadTime
;
}
reloadWait
-=
1
;
}
}
},
1000
);
},
1000
);
return
()
=>
{
return
()
=>
{
clearInterval
(
interval
);
clearInterval
(
interval
);
};
};
});
});
/* Load Clock data from the API */
/* Called after the component has been updated */
afterUpdate
(
async
()
=>
{
scrollToActiveTrack
();
});
/* Load clock data from the API */
async
function
fetchApi
(
query
)
{
async
function
fetchApi
(
query
)
{
let
response
;
let
response
;
let
data
;
let
data
;
...
@@ -71,26 +90,61 @@
...
@@ -71,26 +90,61 @@
}
}
}
}
/* Initialize the component */
/* Initialize the component */
function
initComponent
(
info
)
{
function
initComponent
(
value
)
{
/* Load external CSS */
/* Load external CSS */
if
(
css
!=
null
)
if
(
css
!=
null
)
loadExternalCss
(
rootElement
,
css
);
loadExternalCss
(
rootElement
,
css
);
/* Set currently loaded data */
/* Set currently loaded data */
if
(
currentTrack
==
null
&&
info
!=
null
&&
info
.
current_track
!=
null
)
{
if
(
value
!=
null
)
{
currentTrack
=
info
;
clockData
=
value
;
console
.
log
(
"
Current Data
"
,
value
);
if
(
value
.
current_track
!=
null
)
{
let
t
=
time
-
Date
.
parse
(
value
.
current_track
.
track_start
);
t
=
parseInt
(
t
/
1000
);
timeLeft
=
value
.
current_track
.
track_duration
-
t
-
playoffset
;
}
let
t
=
time
-
Date
.
parse
(
info
.
current_track
.
track_start
);
if
(
value
.
current_schedule
!=
null
)
{
t
=
parseInt
(
t
/
1000
);
let
schedule_end
=
Date
.
parse
(
value
.
current_schedule
.
schedule_end
);
schedule_end
=
parseInt
(
schedule_end
/
1000
);
timeLeft
=
info
.
current_track
.
track_duration
-
t
-
playoffset
;
scheduleTimeLeft
=
schedule_end
-
time
;
console
.
log
(
"
Current Data
"
,
info
);
}
else
{
/* Decrease time left in any case to avoid reloading too often */
scheduleTimeLeft
-=
1
;
}
}
}
return
""
;
return
""
;
}
}
/* Checks if there's an existing, valid schedule */
function
hasValidSchedule
(
value
)
{
if
(
value
.
current_schedule
!=
null
)
{
if
(
value
.
current_schedule
.
schedule_end
!=
null
)
{
let
schedule_end
=
Date
.
parse
(
value
.
current_schedule
.
schedule_end
);
let
diff
=
schedule_end
-
time
;
if
(
diff
>=
0
)
return
true
;
}
}
return
false
;
}
/* Checks if there is an existing valid playlist */
function
hasValidPlaylist
(
value
)
{
if
(
hasValidSchedule
(
value
))
if
(
value
.
current_playlist
!=
null
)
return
true
;
return
false
;
}
/* Display the title of a track */
/* Display the title of a track */
function
displayTitle
(
track
)
{
function
displayTitle
(
track
)
{
if
(
track
!=
null
)
{
if
(
track
!=
null
)
{
...
@@ -105,6 +159,7 @@
...
@@ -105,6 +159,7 @@
return
""
;
return
""
;
}
}
/* Format the time */
/* Format the time */
function
formatTime
(
seconds
)
{
function
formatTime
(
seconds
)
{
if
(
seconds
!=
null
&&
Number
.
isInteger
(
seconds
))
{
if
(
seconds
!=
null
&&
Number
.
isInteger
(
seconds
))
{
...
@@ -121,17 +176,19 @@
...
@@ -121,17 +176,19 @@
return
""
;
return
""
;
}
}
/* Display the name of a show */
/* Display the name of a show */
function
displayShowName
(
schedule
)
{
function
displayShowName
(
schedule
)
{
let
name
=
""
let
name
=
""
if
(
schedule
==
null
||
schedule
.
show_name
==
null
)
{
if
(
schedule
==
null
||
schedule
.
show_name
==
null
)
{
name
=
'
<span class="error">
'
+
noschedule
message
+
'
</span>
'
;
name
=
'
<span class="error">
'
+
no
next
schedule
+
'
</span>
'
;
}
else
{
}
else
{
name
=
schedule
.
show_name
;
name
=
schedule
.
show_name
;
}
}
return
name
;
return
name
;
}
}
/* Display the schedule of a show */
/* Display the schedule of a show */
function
displayShowSchedule
(
schedule
)
{
function
displayShowSchedule
(
schedule
)
{
let
str
=
""
;
let
str
=
""
;
...
@@ -146,7 +203,7 @@
...
@@ -146,7 +203,7 @@
hour
:
'
2-digit
'
,
hour
:
'
2-digit
'
,
minute
:
'
2-digit
'
minute
:
'
2-digit
'
});
});
str
=
"
(
"
+
scheduleStart
;
str
=
"
—
"
+
scheduleStart
;
}
}
if
(
schedule
.
schedule_end
!=
null
)
{
if
(
schedule
.
schedule_end
!=
null
)
{
scheduleEnd
=
new
Date
(
Date
.
parse
(
schedule
.
schedule_end
));
scheduleEnd
=
new
Date
(
Date
.
parse
(
schedule
.
schedule_end
));
...
@@ -154,30 +211,24 @@
...
@@ -154,30 +211,24 @@
hour
:
'
2-digit
'
,
hour
:
'
2-digit
'
,
minute
:
'
2-digit
'
minute
:
'
2-digit
'
});
});
str
=
str
+
"
-
"
+
scheduleEnd
+
"
)
"
;
str
=
str
+
"
-
"
+
scheduleEnd
+
""
;
}
else
{
}
else
{
str
+=
"
)
"
;
str
+=
""
;
}
}
}
}
return
str
;
return
str
;
}
}
/* Check if the given track is currently playing */
/* Check if the given track is currently playing */
function
isActive
(
entry
,
currentTrack
)
{
function
isActive
(
entry
,
currentTrack
)
{
if
(
currentTrack
!=
null
&&
entry
.
track_num
==
currentTrack
.
track_num
)
{
if
(
currentTrack
!=
null
&&
entry
.
track_num
==
currentTrack
.
track_num
)
{
// Scroll to current playlist entry
// location.hash = "#current-playlist-entry";
var
element
=
document
.
querySelector
(
"
#current-playlist-entry
"
);
if
(
element
!=
null
)
element
.
scrollIntoView
();
return
true
;
return
true
;
}
}
return
false
;
return
false
;
}
}
/* Hack to load external CSS into the Web Component */
/* Hack to load external CSS into the Web Component */
function
loadExternalCss
(
root
,
file
)
{
function
loadExternalCss
(
root
,
file
)
{
let
element
=
document
.
createElement
(
"
link
"
);
let
element
=
document
.
createElement
(
"
link
"
);
...
@@ -187,15 +238,13 @@
...
@@ -187,15 +238,13 @@
root
.
appendChild
(
element
);
root
.
appendChild
(
element
);
}
}
/* Called when the clock finished rendering */
function
finalize_rendering
()
{
/* Sroll to currently playing track */
/* Scrolls to the track currently playing */
var
element
=
document
.
querySelector
(
"
#current-playlist-entry
"
);
function
scrollToActiveTrack
()
{
if
(
element
!=
null
)
if
(
currentTrackElement
!=
null
)
element
.
scrollIntoView
();
currentTrackElement
.
scrollIntoView
();
return
""
}
}
</script>
</script>
<style>
<style>
...
@@ -210,13 +259,14 @@
...
@@ -210,13 +259,14 @@
margin
:
0
;
margin
:
0
;
font-size
:
3em
;
font-size
:
3em
;
line-height
:
80px
;
line-height
:
80px
;
color
:
#AAA
;
}
}
#station-logo
{
#station-logo
{
align-content
:
left
;
align-content
:
left
;
text-align
:
right
;
text-align
:
right
;
margin
:
0
40px
0
10px
;
margin
:
0
40px
0
10px
;
opacity
:
0.
5
;
opacity
:
0.
88
;
filter
:
invert
(
100%
);
filter
:
invert
(
100%
);
}
}
...
@@ -240,6 +290,13 @@
...
@@ -240,6 +290,13 @@
padding
:
25px
25px
25px
50px
;
padding
:
25px
25px
25px
50px
;
}
}
#current-schedule
{
border
:
2px
solid
#333
;
margin
:
20px
20px
40px
20px
;
background-color
:
#111
;
height
:
100%
;
}
#current-schedule
,
#current-schedule
,
#next-schedule
{
#next-schedule
{
margin
:
0
0
40px
20px
;
margin
:
0
0
40px
20px
;
...
@@ -252,25 +309,31 @@
...
@@ -252,25 +309,31 @@
}
}
#current-schedule
.schedule-title
{
#current-schedule
.schedule-title
{
color
:
#ccc
;
font-size
:
3.3em
;
text-align
:
center
;
text-align
:
center
;
height
:
100px
;
}
#current-schedule
.schedule-title
h1
{
color
:
#ccc
;
font-size
:
2.8em
;
position
:
relative
;
top
:
30%
;
transform
:
translateY
(
-50%
);
}
}
#next-schedule
.schedule-title
{
#next-schedule
.schedule-title
{
color
:
gray
!important
;
color
:
gray
!important
;
font-size
:
2em
;
font-size
:
2em
;
}
}
#playlist
{
#playlist
{
border
:
2px
solid
#333
;
height
:
calc
(
100%
-
155px
);
margin
:
20px
20px
40px
20px
;
padding
:
10px
;
height
:
calc
(
80%
-
100px
);
overflow-y
:
auto
;
overflow-y
:
auto
;
scroll-behavior
:
smooth
;
scroll-behavior
:
smooth
;
background-color
:
#111
;
padding
:
10px
;
display
:
flex
;
display
:
flex
;
align-items
:
center
;
align-items
:
center
;
border-top
:
1px
solid
#333
;
;
}
}
#playlist
::-webkit-scrollbar-track
#playlist
::-webkit-scrollbar-track
...
@@ -295,12 +358,14 @@
...
@@ -295,12 +358,14 @@
#playlist
ol
{
#playlist
ol
{
margin-left
:
33px
;
margin-left
:
33px
;
height
:
95%
;
}
}
.playlist-entry
{
.playlist-entry
{
font-size
:
1.9em
;
font-size
:
1.9em
;
padding-left
:
53px
;
padding-left
:
53px
;
padding-bottom
:
13px
;
padding-bottom
:
13px
;
color
:
#AAA
;
}
}
#current-track
*
{
#current-track
*
{
...
@@ -331,7 +396,7 @@
...
@@ -331,7 +396,7 @@
}
}
.error
{
.error
{
font-size
:
1.
3
em
;
font-size
:
1.
1
em
;
color
:
red
;
color
:
red
;
height
:
100%
;
height
:
100%
;
display
:
flex
;
display
:
flex
;
...
@@ -384,11 +449,6 @@
...
@@ -384,11 +449,6 @@
text-decoration
:
underline
;
text-decoration
:
underline
;
}
}
footer
#aura-logo
{
filter
:
invert
(
100%
);
width
:
75px
;
margin
:
0
0
20px
0
;
}
</style>
</style>
...
@@ -449,66 +509,75 @@
...
@@ -449,66 +509,75 @@
<div
id=
"right-column"
class=
"column"
>
<div
id=
"right-column"
class=
"column"
>
{#await
data
}
{#await
promise
}
<div
class=
"spinner-border mt-5"
role=
"status"
>
<div
class=
"spinner-border mt-5"
role=
"status"
>
<span
class=
"sr-only"
>
Loading...
</span>
<span
class=
"sr-only"
>
Loading...
</span>
</div>
</div>
{:then value}
{:then value}
{initComponent(value)}
{initComponent(value)}
{#if value.current_schedule}
<div
id=
"current-schedule"
>
<h1
class=
"schedule-title"
>
{@html displayShowName(value.current_schedule)} {displayShowSchedule(value.current_schedule)}
</h1>
<!-- <div class="schedule-details">
<b>Type:</b> {value.current_schedule}, <b>Host:</b> {value.current_schedule}</div>-->
</div>
<div
id=
"playlist"
>
{#if value.current_playlist}
<ol>
{#each value.current_playlist.entries as entry, index}
{#if isActive(entry, value.current_track)}
<li
id=
"current-playlist-entry"
class=
"playlist-entry is-active"
>
<!-- <span class="play-icon">▶</span> -->
<span
class=
"track-title"
>
{displayTitle(entry)}
</span>
<span
class=
"track-time-left"
>
({formatTime(timeLeft)})
</span>
</li>
<div
id=
"current-schedule"
>
<div
class=
"schedule-title"
>
<h1>
{#if hasValidSchedule(value)}
{@html displayShowName(value.current_schedule)} {displayShowSchedule(value.current_schedule)}
{:else}
<span
class=
"error"
>
{nocurrentschedule}
</span>
{/if}
</h1>
</div>
<div
id=
"playlist"
>
{#if hasValidPlaylist(value)}
<ol>
{#each value.current_playlist.entries as entry, index}
{#if isActive(entry, value.current_track)}
<li
id=
"current-playlist-entry"
class=
"playlist-entry is-active"
bind:this=
{currentTrackElement}
>
<!-- <span class="play-icon">▶</span> -->
<span
class=
"track-title"
>
{displayTitle(entry)}
</span>
<span
class=
"track-time-left"
>
({formatTime(timeLeft)})
</span>
</li>
{:else}
<li
class=
"playlist-entry"
>
<span
class=
"track-title"
>
{displayTitle(entry)}
</span>
<span
class=
"track-duration"
>
({formatTime(entry.track_duration)})
</span>
</li>