mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-12-22 20:57:54 +01:00
Style Fixes on Small Screens
This commit is contained in:
parent
a47d9f52a0
commit
90b99cbb51
@ -7,7 +7,6 @@ import io.ktor.client.request.*
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import org.khronos.webgl.ArrayBuffer
|
||||
|
||||
actual fun openPlatform(packageID:String, platformLink:String){
|
||||
//TODO
|
||||
@ -43,13 +42,14 @@ private suspend fun isInternetAvailable(): Boolean {
|
||||
actual val isInternetAvailable:Boolean
|
||||
get(){
|
||||
return true
|
||||
var result = false
|
||||
/*var result = false
|
||||
val job = GlobalScope.launch { result = isInternetAvailable() }
|
||||
while(job.isActive){}
|
||||
return result
|
||||
return result*/
|
||||
}
|
||||
|
||||
val DownloadProgressFlow: MutableSharedFlow<HashMap<String, DownloadStatus>> = MutableSharedFlow(1)
|
||||
val allTracksStatus: HashMap<String, DownloadStatus> = hashMapOf()
|
||||
|
||||
actual suspend fun downloadTracks(
|
||||
list: List<TrackDetails>,
|
||||
@ -58,35 +58,48 @@ actual suspend fun downloadTracks(
|
||||
){
|
||||
withContext(Dispatchers.Default){
|
||||
list.forEach {
|
||||
allTracksStatus[it.title] = DownloadStatus.Queued
|
||||
if (!it.videoID.isNullOrBlank()) {//Video ID already known!
|
||||
downloadTrack(it.videoID!!, it, fetcher, dir)
|
||||
} else {
|
||||
val searchQuery = "${it.title} - ${it.artists.joinToString(",")}"
|
||||
val videoID = fetcher.youtubeMusic.getYTIDBestMatch(searchQuery,it)
|
||||
if (videoID.isNullOrBlank()) {
|
||||
allTracksStatus[it.title] = DownloadStatus.Failed
|
||||
DownloadProgressFlow.emit(allTracksStatus)
|
||||
} else {//Found Youtube Video ID
|
||||
downloadTrack(videoID, it, fetcher, dir)
|
||||
}
|
||||
}
|
||||
}
|
||||
DownloadProgressFlow.emit(allTracksStatus)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun downloadTrack(videoID: String, track: TrackDetails, fetcher:FetchPlatformQueryResult,dir:Dir) {
|
||||
val url = fetcher.youtubeMp3.getMp3DownloadLink(videoID)
|
||||
if(url == null){
|
||||
// TODO Handle
|
||||
allTracksStatus[track.title] = DownloadStatus.Failed
|
||||
DownloadProgressFlow.emit(allTracksStatus)
|
||||
println("No URL to Download")
|
||||
}else {
|
||||
downloadFile(url).collect {
|
||||
when(it){
|
||||
is DownloadResult.Success -> {
|
||||
println("Download Completed")
|
||||
allTracksStatus[track.title] = DownloadStatus.Downloaded
|
||||
dir.saveFileWithMetadata(it.byteArray, track)
|
||||
}
|
||||
is DownloadResult.Error -> println("Download Error: ${track.title}")
|
||||
is DownloadResult.Progress -> println("Download Progress: ${it.progress} : ${track.title}")
|
||||
is DownloadResult.Error -> {
|
||||
allTracksStatus[track.title] = DownloadStatus.Failed
|
||||
println("Download Error: ${track.title}")
|
||||
}
|
||||
is DownloadResult.Progress -> {
|
||||
allTracksStatus[track.title] = DownloadStatus.Downloading(it.progress)
|
||||
println("Download Progress: ${it.progress} : ${track.title}")
|
||||
}
|
||||
}
|
||||
DownloadProgressFlow.emit(allTracksStatus)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ actual class Dir actual constructor(
|
||||
private suspend fun writeTagsAndSave(writer:ID3Writer, albumArt:Object?, trackDetails: TrackDetails){
|
||||
writer.apply {
|
||||
setFrame("TIT2", trackDetails.title)
|
||||
setFrame("TPE1", trackDetails.artists)
|
||||
setFrame("TPE1", trackDetails.artists.toTypedArray())
|
||||
setFrame("TALB", trackDetails.albumName?:"")
|
||||
try{trackDetails.year?.substring(0,4)?.toInt()?.let { setFrame("TYER", it) }} catch(e:Exception){}
|
||||
setFrame("TPE2", trackDetails.artists.joinToString(","))
|
||||
|
@ -6,11 +6,10 @@ import com.arkivanov.decompose.lifecycle.resume
|
||||
import com.arkivanov.mvikotlin.core.store.StoreFactory
|
||||
import com.arkivanov.mvikotlin.logging.store.LoggingStoreFactory
|
||||
import com.arkivanov.mvikotlin.main.store.DefaultStoreFactory
|
||||
import com.shabinder.common.models.DownloadStatus
|
||||
import com.shabinder.common.di.DownloadProgressFlow
|
||||
import com.shabinder.common.root.SpotiFlyerRoot
|
||||
import com.shabinder.database.Database
|
||||
import extras.renderableChild
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import react.*
|
||||
import root.RootR
|
||||
|
||||
@ -40,8 +39,7 @@ class App(props: AppProps): RComponent<AppProps, RState>(props) {
|
||||
override val directories = dependencies.directories
|
||||
override val database: Database? = directories.db
|
||||
override val showPopUpMessage: (String) -> Unit = {}//TODO
|
||||
override val downloadProgressReport: MutableSharedFlow<HashMap<String, DownloadStatus>>
|
||||
= MutableSharedFlow(1)
|
||||
override val downloadProgressReport = DownloadProgressFlow
|
||||
|
||||
}
|
||||
)
|
||||
|
@ -10,7 +10,6 @@ val colorOffWhite = Color("#E7E7E7")
|
||||
object Styles: StyleSheet("Searchbar", isStatic = true) {
|
||||
val makeRow by css {
|
||||
display = Display.flex
|
||||
flexDirection = FlexDirection.row
|
||||
alignItems = Align.center
|
||||
alignContent = Align.center
|
||||
justifyContent = JustifyContent.center
|
||||
|
@ -24,11 +24,11 @@ fun RBuilder.IconList(handler:IconListProps.() -> Unit): ReactElement {
|
||||
private val iconList = functionalComponent<IconListProps>("IconList") { props ->
|
||||
styledDiv {
|
||||
css {
|
||||
+Styles.makeRow
|
||||
margin(18.px)
|
||||
if(props.isBadge) {
|
||||
alignItems = Align.end
|
||||
classes = mutableListOf("info-banners")
|
||||
}
|
||||
+ Styles.makeRow
|
||||
}
|
||||
for((icon,platformLink) in props.iconsAndPlatforms){
|
||||
styledA(href = platformLink){
|
||||
|
@ -25,7 +25,7 @@ private val message = functionalComponent<MessageProps>("Message") { props->
|
||||
+ props.text
|
||||
css {
|
||||
classes = mutableListOf("headingTitle")
|
||||
fontSize = 3.2.rem
|
||||
fontSize = 2.6.em
|
||||
}
|
||||
}
|
||||
}
|
||||
|
38
web-app/src/main/kotlin/list/CircularProgressBar.kt
Normal file
38
web-app/src/main/kotlin/list/CircularProgressBar.kt
Normal file
@ -0,0 +1,38 @@
|
||||
package list
|
||||
|
||||
import kotlinx.css.px
|
||||
import kotlinx.css.width
|
||||
import react.*
|
||||
import styled.css
|
||||
import styled.styledDiv
|
||||
import styled.styledSpan
|
||||
|
||||
@Suppress("FunctionName")
|
||||
fun RBuilder.CircularProgressBar(handler: CircularProgressBarProps.() -> Unit): ReactElement {
|
||||
return child(circularProgressBar){
|
||||
attrs {
|
||||
handler()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
external interface CircularProgressBarProps : RProps {
|
||||
var progress:Int
|
||||
}
|
||||
|
||||
private val circularProgressBar = functionalComponent<CircularProgressBarProps>("Circular-Progress-Bar") { props->
|
||||
styledDiv {
|
||||
styledSpan { +"${props.progress}%" }
|
||||
styledDiv{
|
||||
css {
|
||||
classes = mutableListOf("left-half-clipper")
|
||||
}
|
||||
styledDiv{ css { classes = mutableListOf("first50-bar") } }
|
||||
styledDiv{ css { classes = mutableListOf("value-bar") } }
|
||||
}
|
||||
css{
|
||||
classes = mutableListOf("progress-circle","p${props.progress}").apply { if(props.progress>50) add("over50") }
|
||||
width = 50.px
|
||||
}
|
||||
}
|
||||
}
|
49
web-app/src/main/kotlin/list/DownloadButton.kt
Normal file
49
web-app/src/main/kotlin/list/DownloadButton.kt
Normal file
@ -0,0 +1,49 @@
|
||||
package list
|
||||
|
||||
import com.shabinder.common.models.DownloadStatus
|
||||
import kotlinx.css.*
|
||||
import kotlinx.html.js.onClickFunction
|
||||
import react.*
|
||||
import styled.css
|
||||
import styled.styledDiv
|
||||
import styled.styledImg
|
||||
|
||||
@Suppress("FunctionName")
|
||||
fun RBuilder.DownloadButton(handler: DownloadButtonProps.() -> Unit): ReactElement {
|
||||
return child(downloadButton){
|
||||
attrs {
|
||||
handler()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
external interface DownloadButtonProps : RProps {
|
||||
var onClick:()->Unit
|
||||
var status :DownloadStatus
|
||||
}
|
||||
|
||||
private val downloadButton = functionalComponent<DownloadButtonProps>("Circular-Progress-Bar") { props->
|
||||
styledDiv {
|
||||
val src = when(props.status){
|
||||
is DownloadStatus.NotDownloaded -> "download-gradient.svg"
|
||||
is DownloadStatus.Downloaded -> "check.svg"
|
||||
is DownloadStatus.Failed -> "error.svg"
|
||||
else -> ""
|
||||
}
|
||||
styledImg(src = src) {
|
||||
attrs {
|
||||
onClickFunction = {
|
||||
props.onClick()
|
||||
}
|
||||
}
|
||||
css {
|
||||
width = (2.5).em
|
||||
margin(8.px)
|
||||
}
|
||||
}
|
||||
css {
|
||||
classes = mutableListOf("glow-button")
|
||||
borderRadius = 100.px
|
||||
}
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ import kotlinx.html.id
|
||||
import react.RBuilder
|
||||
import styled.css
|
||||
import styled.styledDiv
|
||||
import styled.styledSection
|
||||
|
||||
class ListScreen(
|
||||
props: Props<SpotiFlyerList>,
|
||||
@ -20,9 +21,9 @@ class ListScreen(
|
||||
|
||||
val result = state.data.queryResult
|
||||
|
||||
styledDiv {
|
||||
styledSection {
|
||||
attrs {
|
||||
id = "list-screen-div"
|
||||
id = "list-screen"
|
||||
}
|
||||
|
||||
if(result == null){
|
||||
@ -37,10 +38,18 @@ class ListScreen(
|
||||
isActive = state.data.trackList.isNotEmpty()
|
||||
}
|
||||
|
||||
state.data.trackList.forEachIndexed{ index, trackDetails ->
|
||||
TrackItem {
|
||||
details = trackDetails
|
||||
downloadTrack = model::onDownloadClicked
|
||||
styledDiv{
|
||||
css {
|
||||
display =Display.flex
|
||||
flexGrow = 1.0
|
||||
flexDirection = FlexDirection.column
|
||||
color = Color.white
|
||||
}
|
||||
state.data.trackList.forEachIndexed{ index, trackDetails ->
|
||||
TrackItem {
|
||||
details = trackDetails
|
||||
downloadTrack = model::onDownloadClicked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -48,10 +57,9 @@ class ListScreen(
|
||||
css {
|
||||
classes = mutableListOf("list-screen")
|
||||
display = Display.flex
|
||||
padding(8.px)
|
||||
flexDirection = FlexDirection.column
|
||||
flexGrow = 1.0
|
||||
justifyContent = JustifyContent.center
|
||||
alignItems = Align.stretch
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,22 +16,28 @@ fun RBuilder.LoadingAnim(handler: RProps.() -> Unit): ReactElement {
|
||||
}
|
||||
|
||||
private val loadingAnim = functionalComponent<RProps>("Loading Animation") {
|
||||
styledDiv {
|
||||
|
||||
styledDiv { css { classes = mutableListOf("sk-cube sk-cube1") } }
|
||||
styledDiv { css { classes = mutableListOf("sk-cube sk-cube2") } }
|
||||
styledDiv { css { classes = mutableListOf("sk-cube sk-cube3") } }
|
||||
styledDiv { css { classes = mutableListOf("sk-cube sk-cube4") } }
|
||||
styledDiv { css { classes = mutableListOf("sk-cube sk-cube5") } }
|
||||
styledDiv { css { classes = mutableListOf("sk-cube sk-cube6") } }
|
||||
styledDiv { css { classes = mutableListOf("sk-cube sk-cube7") } }
|
||||
styledDiv { css { classes = mutableListOf("sk-cube sk-cube8") } }
|
||||
styledDiv { css { classes = mutableListOf("sk-cube sk-cube9") } }
|
||||
|
||||
styledDiv{
|
||||
css {
|
||||
classes = mutableListOf("sk-cube-grid")
|
||||
height = 60.px
|
||||
width = 60.px
|
||||
flexGrow = 1.0
|
||||
display = Display.flex
|
||||
alignItems = Align.center
|
||||
}
|
||||
styledDiv {
|
||||
styledDiv { css { classes = mutableListOf("sk-cube sk-cube1") } }
|
||||
styledDiv { css { classes = mutableListOf("sk-cube sk-cube2") } }
|
||||
styledDiv { css { classes = mutableListOf("sk-cube sk-cube3") } }
|
||||
styledDiv { css { classes = mutableListOf("sk-cube sk-cube4") } }
|
||||
styledDiv { css { classes = mutableListOf("sk-cube sk-cube5") } }
|
||||
styledDiv { css { classes = mutableListOf("sk-cube sk-cube6") } }
|
||||
styledDiv { css { classes = mutableListOf("sk-cube sk-cube7") } }
|
||||
styledDiv { css { classes = mutableListOf("sk-cube sk-cube8") } }
|
||||
styledDiv { css { classes = mutableListOf("sk-cube sk-cube9") } }
|
||||
|
||||
css {
|
||||
classes = mutableListOf("sk-cube-grid")
|
||||
height = 60.px
|
||||
width = 60.px
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
29
web-app/src/main/kotlin/list/LoadingSpinner.kt
Normal file
29
web-app/src/main/kotlin/list/LoadingSpinner.kt
Normal file
@ -0,0 +1,29 @@
|
||||
package list
|
||||
|
||||
import kotlinx.css.px
|
||||
import kotlinx.css.width
|
||||
import react.*
|
||||
import styled.css
|
||||
import styled.styledDiv
|
||||
|
||||
@Suppress("FunctionName")
|
||||
fun RBuilder.LoadingSpinner(handler: RProps.() -> Unit): ReactElement {
|
||||
return child(loadingSpinner){
|
||||
attrs {
|
||||
handler()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val loadingSpinner = functionalComponent<RProps>("Loading-Spinner") {
|
||||
styledDiv {
|
||||
styledDiv{}
|
||||
styledDiv{}
|
||||
styledDiv{}
|
||||
styledDiv{}
|
||||
css{
|
||||
classes = mutableListOf("lds-ring")
|
||||
width = 50.px
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
package list
|
||||
|
||||
import com.shabinder.common.models.DownloadStatus
|
||||
import com.shabinder.common.models.TrackDetails
|
||||
import kotlinx.css.*
|
||||
import kotlinx.html.id
|
||||
import kotlinx.html.js.onClickFunction
|
||||
import react.*
|
||||
import styled.*
|
||||
|
||||
@ -36,72 +36,99 @@ private val trackItem = functionalComponent<TrackItemProps>("Track-Item"){ props
|
||||
attrs {
|
||||
id = "text-details"
|
||||
}
|
||||
styledDiv {
|
||||
css {
|
||||
flexGrow = 1.0
|
||||
minWidth = 0.px
|
||||
display = Display.flex
|
||||
flexDirection = FlexDirection.column
|
||||
margin(8.px)
|
||||
}
|
||||
styledDiv{
|
||||
css {
|
||||
height = 40.px
|
||||
alignItems = Align.center
|
||||
display = Display.flex
|
||||
}
|
||||
styledH3 {
|
||||
+ details.title
|
||||
css {
|
||||
padding(8.px)
|
||||
fontSize = 1.3.em
|
||||
textOverflow = TextOverflow.ellipsis
|
||||
whiteSpace = WhiteSpace.nowrap
|
||||
overflow = Overflow.hidden
|
||||
}
|
||||
}
|
||||
css {
|
||||
height = 40.px
|
||||
display =Display.flex
|
||||
alignItems = Align.center
|
||||
}
|
||||
}
|
||||
styledDiv {
|
||||
css {
|
||||
height = 40.px
|
||||
alignItems = Align.center
|
||||
display = Display.flex
|
||||
}
|
||||
styledH4 {
|
||||
+ details.artists.joinToString(",")
|
||||
css {
|
||||
flexGrow = 1.0
|
||||
padding(8.px)
|
||||
minWidth = 4.em
|
||||
fontSize = 1.1.em
|
||||
textOverflow = TextOverflow.ellipsis
|
||||
whiteSpace = WhiteSpace.nowrap
|
||||
overflow = Overflow.hidden
|
||||
}
|
||||
}
|
||||
styledH4 {
|
||||
+ "${details.durationSec} sec"
|
||||
css {
|
||||
textAlign = TextAlign.end
|
||||
flexGrow = 1.0
|
||||
padding(8.px)
|
||||
textAlign = TextAlign.right
|
||||
minWidth = 4.em
|
||||
fontSize = 1.1.em
|
||||
textOverflow = TextOverflow.ellipsis
|
||||
whiteSpace = WhiteSpace.nowrap
|
||||
overflow = Overflow.hidden
|
||||
}
|
||||
+ details.durationSec.toString()
|
||||
}
|
||||
css {
|
||||
height = 40.px
|
||||
display =Display.flex
|
||||
alignItems = Align.center
|
||||
}
|
||||
}
|
||||
css {
|
||||
display = Display.flex
|
||||
flexGrow = 1.0
|
||||
flexDirection = FlexDirection.column
|
||||
margin(8.px)
|
||||
}
|
||||
}
|
||||
styledDiv {
|
||||
styledImg(src = "download-gradient.svg") {
|
||||
attrs {
|
||||
onClickFunction = {
|
||||
props.downloadTrack(details)
|
||||
}
|
||||
}
|
||||
css {
|
||||
margin(8.px)
|
||||
when(details.downloaded){
|
||||
is DownloadStatus.NotDownloaded ->{
|
||||
DownloadButton {
|
||||
onClick = { props.downloadTrack(details) }
|
||||
status = details.downloaded
|
||||
}
|
||||
}
|
||||
css {
|
||||
classes = mutableListOf("glow-button")
|
||||
borderRadius = 100.px
|
||||
width = 65.px
|
||||
is DownloadStatus.Downloading -> {
|
||||
CircularProgressBar {
|
||||
progress = (details.downloaded as DownloadStatus.Downloading).progress
|
||||
}
|
||||
}
|
||||
DownloadStatus.Queued -> {
|
||||
LoadingSpinner {}
|
||||
}
|
||||
DownloadStatus.Downloaded -> {
|
||||
DownloadButton {
|
||||
onClick = {}
|
||||
status = details.downloaded
|
||||
}
|
||||
}
|
||||
DownloadStatus.Converting -> {
|
||||
LoadingSpinner {}
|
||||
}
|
||||
DownloadStatus.Failed -> {
|
||||
DownloadButton {
|
||||
onClick = {}
|
||||
status = details.downloaded
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
css {
|
||||
alignItems = Align.center
|
||||
display =Display.flex
|
||||
flexDirection = FlexDirection.row
|
||||
flexGrow = 1.0
|
||||
color = Color.white
|
||||
}
|
||||
}
|
||||
}
|
||||
|
1
web-app/src/main/resources/check.svg
Normal file
1
web-app/src/main/resources/check.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientTransform="matrix(1 0 0 -1 0 -6826)" gradientUnits="userSpaceOnUse" x1="0" x2="512" y1="-7082" y2="-7082"><stop offset="0" stop-color="#31d8ff"/><stop offset="1" stop-color="#FC5C7D"/></linearGradient><path d="m512 256c0 141.386719-114.613281 256-256 256s-256-114.613281-256-256 114.613281-256 256-256 256 114.613281 256 256zm0 0" fill="url(#a)"/><path d="m175 395.246094c-4.035156 0-7.902344-1.628906-10.726562-4.511719l-81-82.832031c-5.789063-5.921875-5.683594-15.417969.238281-21.210938 5.921875-5.792968 15.417969-5.6875 21.210937.238282l70.273438 71.859374 232.277344-237.523437c5.792968-5.921875 15.289062-6.027344 21.210937-.234375 5.925781 5.789062 6.03125 15.289062.238281 21.210938l-243 248.492187c-2.820312 2.882813-6.6875 4.511719-10.722656 4.511719zm0 0" fill="#fff"/></svg>
|
After Width: | Height: | Size: 923 B |
168
web-app/src/main/resources/css-circular-prog-bar.css
Normal file
168
web-app/src/main/resources/css-circular-prog-bar.css
Normal file
@ -0,0 +1,168 @@
|
||||
.progress-circle {
|
||||
margin: 20px;
|
||||
position: relative; /* so that children can be absolutely positioned */
|
||||
line-height: 5em;
|
||||
}
|
||||
|
||||
.progress-circle:after{
|
||||
border: none;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
display: block;
|
||||
border-radius: 50%;
|
||||
width: 4.3em;
|
||||
height: 4.3em;
|
||||
background-color: white;
|
||||
content: " ";
|
||||
}
|
||||
/* Text inside the control */
|
||||
.progress-circle span {
|
||||
position: absolute;
|
||||
line-height: 5em;
|
||||
width: 5em;
|
||||
text-align: center;
|
||||
display: block;
|
||||
color: #53777A;
|
||||
z-index: 2;
|
||||
}
|
||||
.left-half-clipper {
|
||||
/* a round circle */
|
||||
border-radius: 50%;
|
||||
width: 5em;
|
||||
height: 5em;
|
||||
position: absolute; /* needed for clipping */
|
||||
clip: rect(0, 5em, 5em, 2.5em); /* clips the whole left half*/
|
||||
}
|
||||
/* when p>50, don't clip left half*/
|
||||
.progress-circle.over50 .left-half-clipper {
|
||||
clip: rect(auto,auto,auto,auto);
|
||||
}
|
||||
.value-bar {
|
||||
/*This is an overlayed square, that is made round with the border radius,
|
||||
then it is cut to display only the left half, then rotated clockwise
|
||||
to escape the outer clipping path.*/
|
||||
position: absolute; /*needed for clipping*/
|
||||
clip: rect(0, 2.5em, 5em, 0);
|
||||
width: 5em;
|
||||
height: 5em;
|
||||
border-radius: 50%;
|
||||
border: 0.45em solid #53777A; /*The border is 0.35 but making it larger removes visual artifacts */
|
||||
/*background-color: #4D642D;*/ /* for debug */
|
||||
box-sizing: border-box;
|
||||
|
||||
}
|
||||
/* Progress bar filling the whole right half for values above 50% */
|
||||
.progress-circle.over50 .first50-bar {
|
||||
/*Progress bar for the first 50%, filling the whole right half*/
|
||||
position: absolute; /*needed for clipping*/
|
||||
clip: rect(0, 5em, 5em, 2.5em);
|
||||
background-color: #53777A;
|
||||
border-radius: 50%;
|
||||
width: 5em;
|
||||
height: 5em;
|
||||
}
|
||||
.progress-circle:not(.over50) .first50-bar{ display: none; }
|
||||
|
||||
|
||||
/* Progress bar rotation position */
|
||||
.progress-circle.p0 .value-bar { display: none; }
|
||||
.progress-circle.p1 .value-bar { transform: rotate(4deg); }
|
||||
.progress-circle.p2 .value-bar { transform: rotate(7deg); }
|
||||
.progress-circle.p3 .value-bar { transform: rotate(11deg); }
|
||||
.progress-circle.p4 .value-bar { transform: rotate(14deg); }
|
||||
.progress-circle.p5 .value-bar { transform: rotate(18deg); }
|
||||
.progress-circle.p6 .value-bar { transform: rotate(22deg); }
|
||||
.progress-circle.p7 .value-bar { transform: rotate(25deg); }
|
||||
.progress-circle.p8 .value-bar { transform: rotate(29deg); }
|
||||
.progress-circle.p9 .value-bar { transform: rotate(32deg); }
|
||||
.progress-circle.p10 .value-bar { transform: rotate(36deg); }
|
||||
.progress-circle.p11 .value-bar { transform: rotate(40deg); }
|
||||
.progress-circle.p12 .value-bar { transform: rotate(43deg); }
|
||||
.progress-circle.p13 .value-bar { transform: rotate(47deg); }
|
||||
.progress-circle.p14 .value-bar { transform: rotate(50deg); }
|
||||
.progress-circle.p15 .value-bar { transform: rotate(54deg); }
|
||||
.progress-circle.p16 .value-bar { transform: rotate(58deg); }
|
||||
.progress-circle.p17 .value-bar { transform: rotate(61deg); }
|
||||
.progress-circle.p18 .value-bar { transform: rotate(65deg); }
|
||||
.progress-circle.p19 .value-bar { transform: rotate(68deg); }
|
||||
.progress-circle.p20 .value-bar { transform: rotate(72deg); }
|
||||
.progress-circle.p21 .value-bar { transform: rotate(76deg); }
|
||||
.progress-circle.p22 .value-bar { transform: rotate(79deg); }
|
||||
.progress-circle.p23 .value-bar { transform: rotate(83deg); }
|
||||
.progress-circle.p24 .value-bar { transform: rotate(86deg); }
|
||||
.progress-circle.p25 .value-bar { transform: rotate(90deg); }
|
||||
.progress-circle.p26 .value-bar { transform: rotate(94deg); }
|
||||
.progress-circle.p27 .value-bar { transform: rotate(97deg); }
|
||||
.progress-circle.p28 .value-bar { transform: rotate(101deg); }
|
||||
.progress-circle.p29 .value-bar { transform: rotate(104deg); }
|
||||
.progress-circle.p30 .value-bar { transform: rotate(108deg); }
|
||||
.progress-circle.p31 .value-bar { transform: rotate(112deg); }
|
||||
.progress-circle.p32 .value-bar { transform: rotate(115deg); }
|
||||
.progress-circle.p33 .value-bar { transform: rotate(119deg); }
|
||||
.progress-circle.p34 .value-bar { transform: rotate(122deg); }
|
||||
.progress-circle.p35 .value-bar { transform: rotate(126deg); }
|
||||
.progress-circle.p36 .value-bar { transform: rotate(130deg); }
|
||||
.progress-circle.p37 .value-bar { transform: rotate(133deg); }
|
||||
.progress-circle.p38 .value-bar { transform: rotate(137deg); }
|
||||
.progress-circle.p39 .value-bar { transform: rotate(140deg); }
|
||||
.progress-circle.p40 .value-bar { transform: rotate(144deg); }
|
||||
.progress-circle.p41 .value-bar { transform: rotate(148deg); }
|
||||
.progress-circle.p42 .value-bar { transform: rotate(151deg); }
|
||||
.progress-circle.p43 .value-bar { transform: rotate(155deg); }
|
||||
.progress-circle.p44 .value-bar { transform: rotate(158deg); }
|
||||
.progress-circle.p45 .value-bar { transform: rotate(162deg); }
|
||||
.progress-circle.p46 .value-bar { transform: rotate(166deg); }
|
||||
.progress-circle.p47 .value-bar { transform: rotate(169deg); }
|
||||
.progress-circle.p48 .value-bar { transform: rotate(173deg); }
|
||||
.progress-circle.p49 .value-bar { transform: rotate(176deg); }
|
||||
.progress-circle.p50 .value-bar { transform: rotate(180deg); }
|
||||
.progress-circle.p51 .value-bar { transform: rotate(184deg); }
|
||||
.progress-circle.p52 .value-bar { transform: rotate(187deg); }
|
||||
.progress-circle.p53 .value-bar { transform: rotate(191deg); }
|
||||
.progress-circle.p54 .value-bar { transform: rotate(194deg); }
|
||||
.progress-circle.p55 .value-bar { transform: rotate(198deg); }
|
||||
.progress-circle.p56 .value-bar { transform: rotate(202deg); }
|
||||
.progress-circle.p57 .value-bar { transform: rotate(205deg); }
|
||||
.progress-circle.p58 .value-bar { transform: rotate(209deg); }
|
||||
.progress-circle.p59 .value-bar { transform: rotate(212deg); }
|
||||
.progress-circle.p60 .value-bar { transform: rotate(216deg); }
|
||||
.progress-circle.p61 .value-bar { transform: rotate(220deg); }
|
||||
.progress-circle.p62 .value-bar { transform: rotate(223deg); }
|
||||
.progress-circle.p63 .value-bar { transform: rotate(227deg); }
|
||||
.progress-circle.p64 .value-bar { transform: rotate(230deg); }
|
||||
.progress-circle.p65 .value-bar { transform: rotate(234deg); }
|
||||
.progress-circle.p66 .value-bar { transform: rotate(238deg); }
|
||||
.progress-circle.p67 .value-bar { transform: rotate(241deg); }
|
||||
.progress-circle.p68 .value-bar { transform: rotate(245deg); }
|
||||
.progress-circle.p69 .value-bar { transform: rotate(248deg); }
|
||||
.progress-circle.p70 .value-bar { transform: rotate(252deg); }
|
||||
.progress-circle.p71 .value-bar { transform: rotate(256deg); }
|
||||
.progress-circle.p72 .value-bar { transform: rotate(259deg); }
|
||||
.progress-circle.p73 .value-bar { transform: rotate(263deg); }
|
||||
.progress-circle.p74 .value-bar { transform: rotate(266deg); }
|
||||
.progress-circle.p75 .value-bar { transform: rotate(270deg); }
|
||||
.progress-circle.p76 .value-bar { transform: rotate(274deg); }
|
||||
.progress-circle.p77 .value-bar { transform: rotate(277deg); }
|
||||
.progress-circle.p78 .value-bar { transform: rotate(281deg); }
|
||||
.progress-circle.p79 .value-bar { transform: rotate(284deg); }
|
||||
.progress-circle.p80 .value-bar { transform: rotate(288deg); }
|
||||
.progress-circle.p81 .value-bar { transform: rotate(292deg); }
|
||||
.progress-circle.p82 .value-bar { transform: rotate(295deg); }
|
||||
.progress-circle.p83 .value-bar { transform: rotate(299deg); }
|
||||
.progress-circle.p84 .value-bar { transform: rotate(302deg); }
|
||||
.progress-circle.p85 .value-bar { transform: rotate(306deg); }
|
||||
.progress-circle.p86 .value-bar { transform: rotate(310deg); }
|
||||
.progress-circle.p87 .value-bar { transform: rotate(313deg); }
|
||||
.progress-circle.p88 .value-bar { transform: rotate(317deg); }
|
||||
.progress-circle.p89 .value-bar { transform: rotate(320deg); }
|
||||
.progress-circle.p90 .value-bar { transform: rotate(324deg); }
|
||||
.progress-circle.p91 .value-bar { transform: rotate(328deg); }
|
||||
.progress-circle.p92 .value-bar { transform: rotate(331deg); }
|
||||
.progress-circle.p93 .value-bar { transform: rotate(335deg); }
|
||||
.progress-circle.p94 .value-bar { transform: rotate(338deg); }
|
||||
.progress-circle.p95 .value-bar { transform: rotate(342deg); }
|
||||
.progress-circle.p96 .value-bar { transform: rotate(346deg); }
|
||||
.progress-circle.p97 .value-bar { transform: rotate(349deg); }
|
||||
.progress-circle.p98 .value-bar { transform: rotate(353deg); }
|
||||
.progress-circle.p99 .value-bar { transform: rotate(356deg); }
|
||||
.progress-circle.p100 .value-bar { transform: rotate(360deg); }
|
@ -6,8 +6,8 @@
|
||||
<stop offset="0" style="stop-color:#80D8FF"/>
|
||||
<stop offset="0.16" style="stop-color:#88D1FF"/>
|
||||
<stop offset="0.413" style="stop-color:#9FBEFE"/>
|
||||
<stop offset="0.725" style="stop-color:#C4A0FD"/>
|
||||
<stop offset="1" style="stop-color:#EA80FC"/>
|
||||
<stop offset="0.725" style="stop-color:#FC5C7D"/>
|
||||
<stop offset="1" style="stop-color:#FC5C7D"/>
|
||||
</linearGradient>
|
||||
<path style="fill:url(#SVGID_1_);" d="M462.622,512H49.378C22.151,512,0,489.85,0,462.623v-118.49c0-11.046,8.954-20,20-20
|
||||
s20,8.954,20,20v118.49c0,5.171,4.207,9.377,9.378,9.377h413.244c5.171,0,9.378-4.207,9.378-9.377v-118.49c0-11.046,8.954-20,20-20
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
1
web-app/src/main/resources/error.svg
Normal file
1
web-app/src/main/resources/error.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientTransform="matrix(1 0 0 -1 0 -12146)" gradientUnits="userSpaceOnUse" x1="0" x2="512" y1="-12402" y2="-12402"><stop offset="0" stop-color="#9AB3FF"/><stop offset="1" stop-color="#FC5C7D"/></linearGradient><path d="m512 256c0 141.386719-114.613281 256-256 256s-256-114.613281-256-256 114.613281-256 256-256 256 114.613281 256 256zm0 0" fill="url(#a)"/><g fill="#fff"><path d="m256 56c-110.28125 0-200 89.71875-200 200s89.71875 200 200 200 200-89.71875 200-200-89.71875-200-200-200zm0 370c-93.738281 0-170-76.261719-170-170s76.261719-170 170-170 170 76.261719 170 170-76.261719 170-170 170zm0 0"/><path d="m324.179688 187.820312c-5.859376-5.855468-15.355469-5.855468-21.214844 0l-46.964844 46.964844-46.964844-46.964844c-5.859375-5.855468-15.355468-5.855468-21.214844 0-5.855468 5.859376-5.855468 15.355469 0 21.214844l46.964844 46.964844-46.964844 46.964844c-5.855468 5.859375-5.855468 15.355468 0 21.214844 2.929688 2.929687 6.769532 4.394531 10.605469 4.394531 3.839844 0 7.679688-1.464844 10.605469-4.394531l46.96875-46.964844 46.964844 46.964844c2.929687 2.929687 6.769531 4.394531 10.605468 4.394531 3.839844 0 7.679688-1.464844 10.609376-4.394531 5.855468-5.859376 5.855468-15.355469 0-21.214844l-46.964844-46.964844 46.964844-46.964844c5.855468-5.859375 5.855468-15.355468 0-21.214844zm0 0"/></g></svg>
|
After Width: | Height: | Size: 1.4 KiB |
@ -7,10 +7,11 @@
|
||||
<script src="web-app.js"></script>
|
||||
<link rel="icon" href="spotiflyer.svg" type="image/icon type">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<link href="css-circular-prog-bar.css" rel="stylesheet">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=RocknRoll+One&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div style="height: 100%;display: flex; align-items: center;flex-direction: column" id="root"></div>
|
||||
<div style="display: flex;flex-direction: column; height: inherit; color: white;" id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -2,6 +2,16 @@
|
||||
font-family: pristine;
|
||||
src: url("pristine_script.ttf");
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
/* CSS HERE ONLY ON PHONE */
|
||||
.searchBox:hover > .searchInput {
|
||||
width: 150px;
|
||||
padding: 0 6px;
|
||||
}
|
||||
.info-banners {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
html {
|
||||
background-image: url("header-dark.jpg");
|
||||
font-family: Lora,Helvetica Neue,Helvetica,Arial,sans-serif;
|
||||
@ -20,6 +30,8 @@ body, html {
|
||||
}
|
||||
#appName{
|
||||
font-family: pristine, cursive;
|
||||
font-weight: 100;
|
||||
text-shadow: 0.3px 0.5px #ffffff;
|
||||
}
|
||||
.headingTitle{
|
||||
text-align: center;
|
||||
@ -30,6 +42,42 @@ body, html {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
box-shadow: 0 5px 15px rgb(105, 44, 143);
|
||||
}
|
||||
|
||||
/*Loading Spinner*/
|
||||
.lds-ring {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
}
|
||||
.lds-ring div {
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 3.5em;
|
||||
height: 3.5em;
|
||||
border: 8px solid #fff;
|
||||
border-radius: 50%;
|
||||
animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
|
||||
border-color: #fff transparent transparent transparent;
|
||||
}
|
||||
.lds-ring div:nth-child(1) {
|
||||
animation-delay: -0.45s;
|
||||
}
|
||||
.lds-ring div:nth-child(2) {
|
||||
animation-delay: -0.3s;
|
||||
}
|
||||
.lds-ring div:nth-child(3) {
|
||||
animation-delay: -0.15s;
|
||||
}
|
||||
@keyframes lds-ring {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
text-decoration: none;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
@ -190,10 +238,4 @@ body, html {
|
||||
-webkit-transform: scale3D(0, 0, 1);
|
||||
transform: scale3D(0, 0, 1);
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 620px) {
|
||||
.searchBox:hover > .searchInput {
|
||||
width: 150px;
|
||||
padding: 0 6px;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user