From 90b99cbb51016bed5290837b85c372c376c9e105 Mon Sep 17 00:00:00 2001 From: shabinder Date: Sat, 13 Mar 2021 16:07:27 +0530 Subject: [PATCH 01/10] Style Fixes on Small Screens --- .../com/shabinder/common/di/WebActual.kt | 25 ++- .../kotlin/com/shabinder/common/di/WebDir.kt | 2 +- web-app/src/main/kotlin/App.kt | 6 +- web-app/src/main/kotlin/Styles.kt | 1 - web-app/src/main/kotlin/home/IconList.kt | 4 +- web-app/src/main/kotlin/home/Message.kt | 2 +- .../main/kotlin/list/CircularProgressBar.kt | 38 ++++ .../src/main/kotlin/list/DownloadButton.kt | 49 +++++ web-app/src/main/kotlin/list/ListScreen.kt | 24 ++- web-app/src/main/kotlin/list/LoadingAnim.kt | 36 ++-- .../src/main/kotlin/list/LoadingSpinner.kt | 29 +++ web-app/src/main/kotlin/list/TrackItem.kt | 97 ++++++---- web-app/src/main/resources/check.svg | 1 + .../main/resources/css-circular-prog-bar.css | 168 ++++++++++++++++++ .../src/main/resources/download-gradient.svg | 4 +- web-app/src/main/resources/error.svg | 1 + web-app/src/main/resources/index.html | 3 +- web-app/src/main/resources/styles.css | 54 +++++- 18 files changed, 462 insertions(+), 82 deletions(-) create mode 100644 web-app/src/main/kotlin/list/CircularProgressBar.kt create mode 100644 web-app/src/main/kotlin/list/DownloadButton.kt create mode 100644 web-app/src/main/kotlin/list/LoadingSpinner.kt create mode 100644 web-app/src/main/resources/check.svg create mode 100644 web-app/src/main/resources/css-circular-prog-bar.css create mode 100644 web-app/src/main/resources/error.svg diff --git a/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebActual.kt b/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebActual.kt index 520e106d..b3037611 100644 --- a/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebActual.kt +++ b/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebActual.kt @@ -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> = MutableSharedFlow(1) +val allTracksStatus: HashMap = hashMapOf() actual suspend fun downloadTracks( list: List, @@ -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) } } } diff --git a/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebDir.kt b/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebDir.kt index e9d66437..b9db776b 100644 --- a/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebDir.kt +++ b/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebDir.kt @@ -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(",")) diff --git a/web-app/src/main/kotlin/App.kt b/web-app/src/main/kotlin/App.kt index 3b3ffaa6..e56fff15 100644 --- a/web-app/src/main/kotlin/App.kt +++ b/web-app/src/main/kotlin/App.kt @@ -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(props) { override val directories = dependencies.directories override val database: Database? = directories.db override val showPopUpMessage: (String) -> Unit = {}//TODO - override val downloadProgressReport: MutableSharedFlow> - = MutableSharedFlow(1) + override val downloadProgressReport = DownloadProgressFlow } ) diff --git a/web-app/src/main/kotlin/Styles.kt b/web-app/src/main/kotlin/Styles.kt index 8150645a..e4b6fd96 100644 --- a/web-app/src/main/kotlin/Styles.kt +++ b/web-app/src/main/kotlin/Styles.kt @@ -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 diff --git a/web-app/src/main/kotlin/home/IconList.kt b/web-app/src/main/kotlin/home/IconList.kt index e496fab2..cb21ad7b 100644 --- a/web-app/src/main/kotlin/home/IconList.kt +++ b/web-app/src/main/kotlin/home/IconList.kt @@ -24,11 +24,11 @@ fun RBuilder.IconList(handler:IconListProps.() -> Unit): ReactElement { private val iconList = functionalComponent("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){ diff --git a/web-app/src/main/kotlin/home/Message.kt b/web-app/src/main/kotlin/home/Message.kt index df5bc852..1460ccf0 100644 --- a/web-app/src/main/kotlin/home/Message.kt +++ b/web-app/src/main/kotlin/home/Message.kt @@ -25,7 +25,7 @@ private val message = functionalComponent("Message") { props-> + props.text css { classes = mutableListOf("headingTitle") - fontSize = 3.2.rem + fontSize = 2.6.em } } } diff --git a/web-app/src/main/kotlin/list/CircularProgressBar.kt b/web-app/src/main/kotlin/list/CircularProgressBar.kt new file mode 100644 index 00000000..b36eacf2 --- /dev/null +++ b/web-app/src/main/kotlin/list/CircularProgressBar.kt @@ -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("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 + } + } +} \ No newline at end of file diff --git a/web-app/src/main/kotlin/list/DownloadButton.kt b/web-app/src/main/kotlin/list/DownloadButton.kt new file mode 100644 index 00000000..fbca8036 --- /dev/null +++ b/web-app/src/main/kotlin/list/DownloadButton.kt @@ -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("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 + } + } +} \ No newline at end of file diff --git a/web-app/src/main/kotlin/list/ListScreen.kt b/web-app/src/main/kotlin/list/ListScreen.kt index 8a5aa508..a45b0bd5 100644 --- a/web-app/src/main/kotlin/list/ListScreen.kt +++ b/web-app/src/main/kotlin/list/ListScreen.kt @@ -9,6 +9,7 @@ import kotlinx.html.id import react.RBuilder import styled.css import styled.styledDiv +import styled.styledSection class ListScreen( props: Props, @@ -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 } } } diff --git a/web-app/src/main/kotlin/list/LoadingAnim.kt b/web-app/src/main/kotlin/list/LoadingAnim.kt index dedab437..e701adfc 100644 --- a/web-app/src/main/kotlin/list/LoadingAnim.kt +++ b/web-app/src/main/kotlin/list/LoadingAnim.kt @@ -16,22 +16,28 @@ fun RBuilder.LoadingAnim(handler: RProps.() -> Unit): ReactElement { } private val loadingAnim = functionalComponent("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 + } } } } \ No newline at end of file diff --git a/web-app/src/main/kotlin/list/LoadingSpinner.kt b/web-app/src/main/kotlin/list/LoadingSpinner.kt new file mode 100644 index 00000000..98adb9ba --- /dev/null +++ b/web-app/src/main/kotlin/list/LoadingSpinner.kt @@ -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("Loading-Spinner") { + styledDiv { + styledDiv{} + styledDiv{} + styledDiv{} + styledDiv{} + css{ + classes = mutableListOf("lds-ring") + width = 50.px + } + } +} \ No newline at end of file diff --git a/web-app/src/main/kotlin/list/TrackItem.kt b/web-app/src/main/kotlin/list/TrackItem.kt index a0ec5739..b28feda0 100644 --- a/web-app/src/main/kotlin/list/TrackItem.kt +++ b/web-app/src/main/kotlin/list/TrackItem.kt @@ -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("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 } } } diff --git a/web-app/src/main/resources/check.svg b/web-app/src/main/resources/check.svg new file mode 100644 index 00000000..65c47d51 --- /dev/null +++ b/web-app/src/main/resources/check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web-app/src/main/resources/css-circular-prog-bar.css b/web-app/src/main/resources/css-circular-prog-bar.css new file mode 100644 index 00000000..f3a9bfa9 --- /dev/null +++ b/web-app/src/main/resources/css-circular-prog-bar.css @@ -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); } \ No newline at end of file diff --git a/web-app/src/main/resources/download-gradient.svg b/web-app/src/main/resources/download-gradient.svg index d74dc881..13c3ead2 100644 --- a/web-app/src/main/resources/download-gradient.svg +++ b/web-app/src/main/resources/download-gradient.svg @@ -6,8 +6,8 @@ - - + + diff --git a/web-app/src/main/resources/index.html b/web-app/src/main/resources/index.html index 56d661df..92db7273 100644 --- a/web-app/src/main/resources/index.html +++ b/web-app/src/main/resources/index.html @@ -7,10 +7,11 @@ + -
+
diff --git a/web-app/src/main/resources/styles.css b/web-app/src/main/resources/styles.css index 9ddd95ef..5f49257e 100644 --- a/web-app/src/main/resources/styles.css +++ b/web-app/src/main/resources/styles.css @@ -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; - } } \ No newline at end of file From d4d3835f8d1e4e7638748ebefeaeafc0e4734a0f Mon Sep 17 00:00:00 2001 From: shabinder Date: Tue, 16 Mar 2021 16:10:11 +0530 Subject: [PATCH 02/10] Web App Changes and Fixes --- .../shabinder/common/models/AllPlatforms.kt | 7 ++ .../com/shabinder/common/models/CorsProxy.kt | 31 +++++ .../com/shabinder/common/di/AndroidActual.kt | 2 + .../kotlin/com/shabinder/common/di/Expect.kt | 3 + .../common/di/gaana/GaanaRequests.kt | 11 +- .../common/di/providers/YoutubeMp3.kt | 10 +- .../common/di/providers/YoutubeMusic.kt | 3 +- .../common/di/spotify/SpotifyRequests.kt | 3 +- .../shabinder/common/di/youtubeMp3/Yt1sMp3.kt | 7 +- .../com/shabinder/common/di/DesktopActual.kt | 2 + .../com/shabinder/common/di/WebActual.kt | 4 +- .../kotlin/com/shabinder/common/di/WebDir.kt | 6 +- .../shabinder/common/main/SpotiFlyerMain.kt | 1 + .../root/callbacks/SpotiFlyerRootCallBacks.kt | 1 + .../root/integration/SpotiFlyerRootImpl.kt | 5 + web-app/src/main/kotlin/home/HomeScreen.kt | 3 +- web-app/src/main/kotlin/home/Searchbar.kt | 12 +- .../main/kotlin/list/CircularProgressBar.kt | 6 +- .../src/main/kotlin/list/DownloadAllButton.kt | 83 ++++++++----- web-app/src/main/kotlin/list/ListScreen.kt | 6 +- web-app/src/main/kotlin/list/TrackItem.kt | 24 ++-- web-app/src/main/kotlin/navbar/NavBar.kt | 109 ++++++++++++++---- web-app/src/main/kotlin/root/RootR.kt | 6 +- .../main/resources/css-circular-prog-bar.css | 3 +- web-app/src/main/resources/styles.css | 57 +++++++++ 25 files changed, 328 insertions(+), 77 deletions(-) create mode 100644 common/data-models/src/commonMain/kotlin/com/shabinder/common/models/AllPlatforms.kt create mode 100644 common/data-models/src/commonMain/kotlin/com/shabinder/common/models/CorsProxy.kt diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/AllPlatforms.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/AllPlatforms.kt new file mode 100644 index 00000000..cb9407b1 --- /dev/null +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/AllPlatforms.kt @@ -0,0 +1,7 @@ +package com.shabinder.common.models + +sealed class AllPlatforms{ + object Js:AllPlatforms() + object Jvm:AllPlatforms() + object Native:AllPlatforms() +} diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/CorsProxy.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/CorsProxy.kt new file mode 100644 index 00000000..70baacfb --- /dev/null +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/CorsProxy.kt @@ -0,0 +1,31 @@ +package com.shabinder.common.models + +sealed class CorsProxy(open val url: String){ + data class SelfHostedCorsProxy(override val url:String = "https://kind-grasshopper-73.telebit.io/cors/"):CorsProxy(url) + data class PublicProxyWithExtension(override val url:String = "https://cors.bridged.cc/"):CorsProxy(url) + + fun toggle(mode:CorsProxy? = null):CorsProxy{ + mode?.let { + corsProxy = mode + return corsProxy + } + corsProxy = when(corsProxy){ + is SelfHostedCorsProxy -> PublicProxyWithExtension() + is PublicProxyWithExtension -> SelfHostedCorsProxy() + } + return corsProxy + } + + fun extensionMode():Boolean{ + return when(corsProxy){ + is SelfHostedCorsProxy -> false + is PublicProxyWithExtension -> true + } + } +} + +/* +* This Var Keeps Track for Cors Config in JS Platform +* Default Self Hosted, However ask user to use extension if possible. +* */ +var corsProxy:CorsProxy = CorsProxy.SelfHostedCorsProxy() diff --git a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidActual.kt b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidActual.kt index e08c0164..0f9d23d8 100644 --- a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidActual.kt +++ b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidActual.kt @@ -11,6 +11,7 @@ import com.github.kiulian.downloader.model.quality.AudioQuality import com.razorpay.Checkout import com.shabinder.common.database.activityContext import com.shabinder.common.di.worker.ForegroundService +import com.shabinder.common.models.AllPlatforms import com.shabinder.common.models.TrackDetails import kotlinx.coroutines.Dispatchers import org.json.JSONObject @@ -30,6 +31,7 @@ actual fun openPlatform(packageID:String, platformLink:String){ } } actual val dispatcherIO = Dispatchers.IO +actual val currentPlatform: AllPlatforms = AllPlatforms.Jvm actual val isInternetAvailable:Boolean get() = internetAvailability.value ?: true diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Expect.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Expect.kt index 4f3c9218..5a6da405 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Expect.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Expect.kt @@ -1,5 +1,6 @@ package com.shabinder.common.di +import com.shabinder.common.models.AllPlatforms import com.shabinder.common.models.TrackDetails import kotlinx.coroutines.CoroutineDispatcher @@ -13,6 +14,8 @@ expect val dispatcherIO: CoroutineDispatcher expect val isInternetAvailable:Boolean +expect val currentPlatform: AllPlatforms + expect suspend fun downloadTracks( list: List, fetcher: FetchPlatformQueryResult, diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/gaana/GaanaRequests.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/gaana/GaanaRequests.kt index 57db44ee..8d490484 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/gaana/GaanaRequests.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/gaana/GaanaRequests.kt @@ -1,11 +1,19 @@ package com.shabinder.common.di.gaana +import com.shabinder.common.di.currentPlatform +import com.shabinder.common.models.AllPlatforms +import com.shabinder.common.models.corsProxy import com.shabinder.common.models.gaana.* import io.ktor.client.* import io.ktor.client.request.* +val corsApi get() = if(currentPlatform is AllPlatforms.Js){ + corsProxy.url +} // "https://spotiflyer-cors.azurewebsites.net/" //"https://spotiflyer-cors.herokuapp.com/"//"https://cors.bridged.cc/" +else "" + private const val TOKEN = "b2e6d7fbc136547a940516e9b77e5990" -private const val BASE_URL = "https://api.gaana.com/" +private val BASE_URL get() = "${corsApi}https://api.gaana.com" interface GaanaRequests { @@ -76,6 +84,7 @@ interface GaanaRequests { "$BASE_URL/?type=$type&subtype=$subtype&seokey=$seokey&token=$TOKEN&format=$format" ) } + /* * Api Request: http://api.gaana.com/?type=artist&subtype=artist_track_listing&seokey=neha-kakkar&limit=50&token=b2e6d7fbc136547a940516e9b77e5990&format=JSON * diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeMp3.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeMp3.kt index 4cd87a71..74a0bfeb 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeMp3.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeMp3.kt @@ -2,7 +2,11 @@ package com.shabinder.common.di.providers import co.touchlab.kermit.Kermit import com.shabinder.common.di.Dir +import com.shabinder.common.di.currentPlatform import com.shabinder.common.di.youtubeMp3.Yt1sMp3 +import com.shabinder.common.models.AllPlatforms +import com.shabinder.common.models.CorsProxy +import com.shabinder.common.models.corsProxy import com.shabinder.database.Database import io.ktor.client.* @@ -11,5 +15,9 @@ class YoutubeMp3( private val logger: Kermit, private val dir: Dir, ):Yt1sMp3 { - suspend fun getMp3DownloadLink(videoID:String):String? = getLinkFromYt1sMp3(videoID) + suspend fun getMp3DownloadLink(videoID:String):String? = getLinkFromYt1sMp3(videoID)?.let{ + println("Is Self Hosted"+(corsProxy is CorsProxy.SelfHostedCorsProxy)) + if (currentPlatform is AllPlatforms.Js && corsProxy !is CorsProxy.PublicProxyWithExtension) "https://kind-grasshopper-73.telebit.io/cors/$it" + else it + } } \ No newline at end of file diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeMusic.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeMusic.kt index 10b3531e..a55c9d25 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeMusic.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeMusic.kt @@ -1,6 +1,7 @@ package com.shabinder.common.di.providers import co.touchlab.kermit.Kermit +import com.shabinder.common.di.gaana.corsApi import com.shabinder.common.models.TrackDetails import com.shabinder.common.models.YoutubeTrack import com.willowtreeapps.fuzzywuzzy.diffutils.FuzzySearch @@ -246,7 +247,7 @@ class YoutubeMusic constructor( } private suspend fun getYoutubeMusicResponse(query: String):String{ - return httpClient.post("https://music.youtube.com/youtubei/v1/search?alt=json&key=$apiKey"){ + return httpClient.post("${corsApi}https://music.youtube.com/youtubei/v1/search?alt=json&key=$apiKey"){ contentType(ContentType.Application.Json) headers{ append("referer","https://music.youtube.com/search") diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/spotify/SpotifyRequests.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/spotify/SpotifyRequests.kt index c4e94bf1..b98451e4 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/spotify/SpotifyRequests.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/spotify/SpotifyRequests.kt @@ -1,5 +1,6 @@ package com.shabinder.common.di.spotify +import com.shabinder.common.di.gaana.corsApi import com.shabinder.common.models.spotify.Album import com.shabinder.common.models.spotify.PagingObjectPlaylistTrack import com.shabinder.common.models.spotify.Playlist @@ -7,7 +8,7 @@ import com.shabinder.common.models.spotify.Track import io.ktor.client.* import io.ktor.client.request.* -private const val BASE_URL = "https://api.spotify.com/v1" +private val BASE_URL get() = "${corsApi}https://api.spotify.com/v1" interface SpotifyRequests { diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/youtubeMp3/Yt1sMp3.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/youtubeMp3/Yt1sMp3.kt index a01a48ba..16440679 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/youtubeMp3/Yt1sMp3.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/youtubeMp3/Yt1sMp3.kt @@ -1,5 +1,6 @@ package com.shabinder.common.di.youtubeMp3 +import com.shabinder.common.di.gaana.corsApi import io.ktor.client.* import io.ktor.client.request.* import io.ktor.client.request.forms.* @@ -19,14 +20,14 @@ interface Yt1sMp3 { * Downloadable Mp3 Link for YT videoID. * */ suspend fun getLinkFromYt1sMp3(videoID: String):String? = - getConvertedMp3Link(videoID,getKey(videoID))?.get("dlink")?.jsonPrimitive?.toString()?.replace("\"", ""); + getConvertedMp3Link(videoID,getKey(videoID))?.get("dlink")?.jsonPrimitive?.toString()?.replace("\"", "") /* * POST:https://yt1s.com/api/ajaxSearch/index * Body Form= q:yt video link ,vt:format=mp3 * */ private suspend fun getKey(videoID:String):String{ - val response:JsonObject? = httpClient.post("https://yt1s.com/api/ajaxSearch/index"){ + val response:JsonObject? = httpClient.post("${corsApi}https://yt1s.com/api/ajaxSearch/index"){ body = FormDataContent(Parameters.build { append("q","https://www.youtube.com/watch?v=$videoID") append("vt","mp3") @@ -36,7 +37,7 @@ interface Yt1sMp3 { } private suspend fun getConvertedMp3Link(videoID: String,key:String):JsonObject?{ - return httpClient.post("https://yt1s.com/api/ajaxConvert/convert"){ + return httpClient.post("${corsApi}https://yt1s.com/api/ajaxConvert/convert"){ body = FormDataContent(Parameters.build { append("vid", videoID) append("k",key) diff --git a/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/DesktopActual.kt b/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/DesktopActual.kt index 42f8cc25..df6ce47f 100644 --- a/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/DesktopActual.kt +++ b/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/DesktopActual.kt @@ -4,6 +4,7 @@ import com.github.kiulian.downloader.YoutubeDownloader import com.github.kiulian.downloader.model.YoutubeVideo import com.github.kiulian.downloader.model.formats.Format import com.github.kiulian.downloader.model.quality.AudioQuality +import com.shabinder.common.models.AllPlatforms import com.shabinder.common.models.DownloadResult import com.shabinder.common.models.DownloadStatus import com.shabinder.common.models.TrackDetails @@ -18,6 +19,7 @@ import kotlinx.coroutines.withContext actual fun openPlatform(packageID:String, platformLink:String){ //TODO } +actual val currentPlatform: AllPlatforms = AllPlatforms.Jvm actual val dispatcherIO = Dispatchers.IO diff --git a/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebActual.kt b/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebActual.kt index b3037611..255e5087 100644 --- a/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebActual.kt +++ b/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebActual.kt @@ -1,5 +1,6 @@ package com.shabinder.common.di +import com.shabinder.common.models.AllPlatforms import com.shabinder.common.models.DownloadResult import com.shabinder.common.models.DownloadStatus import com.shabinder.common.models.TrackDetails @@ -8,6 +9,8 @@ import kotlinx.coroutines.* import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.collect +actual val currentPlatform:AllPlatforms = AllPlatforms.Js + actual fun openPlatform(packageID:String, platformLink:String){ //TODO } @@ -87,7 +90,6 @@ suspend fun downloadTrack(videoID: String, track: TrackDetails, fetcher:FetchPla when(it){ is DownloadResult.Success -> { println("Download Completed") - allTracksStatus[track.title] = DownloadStatus.Downloaded dir.saveFileWithMetadata(it.byteArray, track) } is DownloadResult.Error -> { diff --git a/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebDir.kt b/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebDir.kt index b9db776b..90004b11 100644 --- a/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebDir.kt +++ b/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebDir.kt @@ -1,7 +1,9 @@ package com.shabinder.common.di import co.touchlab.kermit.Kermit +import com.shabinder.common.di.gaana.corsApi import com.shabinder.common.models.DownloadResult +import com.shabinder.common.models.DownloadStatus import com.shabinder.common.models.TrackDetails import com.shabinder.database.Database import kotlinext.js.Object @@ -50,7 +52,7 @@ actual class Dir actual constructor( trackDetails: TrackDetails ) { val writer = ID3Writer(mp3ByteArray.toArrayBuffer()) - val albumArt = downloadFile(trackDetails.albumArtURL) + val albumArt = downloadFile(corsApi+trackDetails.albumArtURL) albumArt.collect { when(it){ is DownloadResult.Success -> { @@ -83,7 +85,9 @@ actual class Dir actual constructor( albumArt?.let { setFrame("APIC", it) } } writer.addTag() + allTracksStatus[trackDetails.title] = DownloadStatus.Downloaded saveAs(writer.getBlob(), "${removeIllegalChars(trackDetails.title)}.mp3") + DownloadProgressFlow.emit(allTracksStatus) } actual fun addToLibrary(path:String){} diff --git a/common/main/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMain.kt b/common/main/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMain.kt index 72f453c8..9168cf49 100644 --- a/common/main/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMain.kt +++ b/common/main/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMain.kt @@ -42,6 +42,7 @@ interface SpotiFlyerMain { val dir: Dir val showPopUpMessage:(String)->Unit } + sealed class Output { data class Search(val link: String) : Output() } diff --git a/common/root/src/commonMain/kotlin/com/shabinder/common/root/callbacks/SpotiFlyerRootCallBacks.kt b/common/root/src/commonMain/kotlin/com/shabinder/common/root/callbacks/SpotiFlyerRootCallBacks.kt index db0250b3..fb3933cb 100644 --- a/common/root/src/commonMain/kotlin/com/shabinder/common/root/callbacks/SpotiFlyerRootCallBacks.kt +++ b/common/root/src/commonMain/kotlin/com/shabinder/common/root/callbacks/SpotiFlyerRootCallBacks.kt @@ -2,4 +2,5 @@ package com.shabinder.common.root.callbacks interface SpotiFlyerRootCallBacks { fun searchLink(link:String) + fun popBackToHomeScreen() } diff --git a/common/root/src/commonMain/kotlin/com/shabinder/common/root/integration/SpotiFlyerRootImpl.kt b/common/root/src/commonMain/kotlin/com/shabinder/common/root/integration/SpotiFlyerRootImpl.kt index 22515ff3..a1577d25 100644 --- a/common/root/src/commonMain/kotlin/com/shabinder/common/root/integration/SpotiFlyerRootImpl.kt +++ b/common/root/src/commonMain/kotlin/com/shabinder/common/root/integration/SpotiFlyerRootImpl.kt @@ -29,6 +29,11 @@ internal class SpotiFlyerRootImpl( override val callBacks = object : SpotiFlyerRootCallBacks{ override fun searchLink(link: String) = onMainOutput(SpotiFlyerMain.Output.Search(link)) + override fun popBackToHomeScreen() { + router.popWhile { + it !is Configuration.Main + } + } } private fun createChild(configuration: Configuration, componentContext: ComponentContext): Child = diff --git a/web-app/src/main/kotlin/home/HomeScreen.kt b/web-app/src/main/kotlin/home/HomeScreen.kt index a51ab5d3..7200d516 100644 --- a/web-app/src/main/kotlin/home/HomeScreen.kt +++ b/web-app/src/main/kotlin/home/HomeScreen.kt @@ -1,7 +1,9 @@ package home +import com.shabinder.common.di.currentPlatform import com.shabinder.common.main.SpotiFlyerMain import com.shabinder.common.main.SpotiFlyerMain.State +import com.shabinder.common.models.AllPlatforms import extras.RenderableComponent import kotlinx.coroutines.flow.Flow import kotlinx.css.* @@ -19,7 +21,6 @@ class HomeScreen( override val stateFlow: Flow = model.models override fun RBuilder.render() { - println("Rendering New State = \"${state.data}\" ") styledDiv{ css { display = Display.flex diff --git a/web-app/src/main/kotlin/home/Searchbar.kt b/web-app/src/main/kotlin/home/Searchbar.kt index 126581e9..d27328f9 100644 --- a/web-app/src/main/kotlin/home/Searchbar.kt +++ b/web-app/src/main/kotlin/home/Searchbar.kt @@ -1,9 +1,12 @@ package home +import kotlinx.browser.window import kotlinx.html.InputType import kotlinx.html.js.onChangeFunction import kotlinx.html.js.onClickFunction +import kotlinx.html.js.onKeyDownFunction import org.w3c.dom.HTMLInputElement +import org.w3c.dom.Window import react.* import styled.* @@ -34,6 +37,12 @@ val searchbar = functionalComponent("SearchBar"){ props -> val target = it.target as HTMLInputElement props.onLinkChange(target.value) } + this.onKeyDownFunction = { + if(it.asDynamic().key == "Enter") { + if(props.link.isEmpty()) window.alert("Enter a Link from Supported Platforms") + else props.search(props.link) + } + } value = props.link } css { @@ -43,7 +52,8 @@ val searchbar = functionalComponent("SearchBar"){ props -> styledButton { attrs { onClickFunction = { - props.search(props.link) + if(props.link.isEmpty()) window.alert("Enter a Link from Supported Platforms") + else props.search(props.link) } } css { diff --git a/web-app/src/main/kotlin/list/CircularProgressBar.kt b/web-app/src/main/kotlin/list/CircularProgressBar.kt index b36eacf2..26f7520e 100644 --- a/web-app/src/main/kotlin/list/CircularProgressBar.kt +++ b/web-app/src/main/kotlin/list/CircularProgressBar.kt @@ -1,7 +1,6 @@ package list -import kotlinx.css.px -import kotlinx.css.width +import kotlinx.css.* import react.* import styled.css import styled.styledDiv @@ -31,8 +30,11 @@ private val circularProgressBar = functionalComponent( styledDiv{ css { classes = mutableListOf("value-bar") } } } css{ + display = Display.flex + justifyContent = JustifyContent.center classes = mutableListOf("progress-circle","p${props.progress}").apply { if(props.progress>50) add("over50") } width = 50.px + marginBottom = 65.px } } } \ No newline at end of file diff --git a/web-app/src/main/kotlin/list/DownloadAllButton.kt b/web-app/src/main/kotlin/list/DownloadAllButton.kt index 489760aa..7ef116ec 100644 --- a/web-app/src/main/kotlin/list/DownloadAllButton.kt +++ b/web-app/src/main/kotlin/list/DownloadAllButton.kt @@ -2,6 +2,7 @@ package list import kotlinx.css.* import kotlinx.html.id +import kotlinx.html.js.onClickFunction import react.* import styled.css import styled.styledDiv @@ -10,6 +11,8 @@ import styled.styledImg external interface DownloadAllButtonProps : RProps { var isActive:Boolean + var link : String + var downloadAll:()->Unit } @Suppress("FunctionName") @@ -22,37 +25,65 @@ fun RBuilder.DownloadAllButton(handler: DownloadAllButtonProps.() -> Unit): Reac } private val downloadAllButton = functionalComponent("DownloadAllButton") { props-> - styledDiv { - styledDiv { - styledImg(src = "download.svg",alt = "Download All Button") { + val (isClicked,setClicked) = useState(false) + + useEffect(mutableListOf(props.link)){ + setClicked(false) + } + + if(props.isActive){ + if(isClicked) { + styledDiv{ css { - classes = mutableListOf("download-all-icon") - height = 32.px + display = Display.flex + alignItems = Align.center + justifyContent = JustifyContent.center + height = 52.px } - } - - styledH5 { - attrs { - id = "download-all-text" - } - + "Download All" - css { - whiteSpace = WhiteSpace.nowrap - fontSize = 15.px - } - } - - css { - classes = mutableListOf("download-icon") - display = Display.flex - alignItems = Align.center + LoadingSpinner { } } } - css { - classes = mutableListOf("download-button") - display = if(props.isActive) Display.flex else Display.none - alignItems = Align.center + else{ + styledDiv { + attrs { + onClickFunction = { + //props.downloadAll() + setClicked(true) + } + } + styledDiv { + + styledImg(src = "download.svg",alt = "Download All Button") { + css { + classes = mutableListOf("download-all-icon") + height = 32.px + } + } + + styledH5 { + attrs { + id = "download-all-text" + } + + "Download All" + css { + whiteSpace = WhiteSpace.nowrap + fontSize = 15.px + } + } + + css { + classes = mutableListOf("download-icon") + display = Display.flex + alignItems = Align.center + } + } + css { + classes = mutableListOf("download-button") + display = Display.flex + alignItems = Align.center + } + } } } } diff --git a/web-app/src/main/kotlin/list/ListScreen.kt b/web-app/src/main/kotlin/list/ListScreen.kt index a45b0bd5..d43ded27 100644 --- a/web-app/src/main/kotlin/list/ListScreen.kt +++ b/web-app/src/main/kotlin/list/ListScreen.kt @@ -35,7 +35,11 @@ class ListScreen( } DownloadAllButton { - isActive = state.data.trackList.isNotEmpty() + isActive = state.data.trackList.size > 1 + downloadAll = { + model.onDownloadAllClicked(state.data.trackList) + } + link = state.data.link } styledDiv{ diff --git a/web-app/src/main/kotlin/list/TrackItem.kt b/web-app/src/main/kotlin/list/TrackItem.kt index b28feda0..587b9789 100644 --- a/web-app/src/main/kotlin/list/TrackItem.kt +++ b/web-app/src/main/kotlin/list/TrackItem.kt @@ -22,9 +22,12 @@ fun RBuilder.TrackItem(handler: TrackItemProps.() -> Unit): ReactElement { } private val trackItem = functionalComponent("Track-Item"){ props -> + val (downloadStatus,setDownloadStatus) = useState(props.details.downloaded) val details = props.details + useEffect(listOf(props.details)){ + setDownloadStatus(props.details.downloaded) + } styledDiv { - styledImg(src = details.albumArtURL) { css { height = 90.px @@ -89,20 +92,23 @@ private val trackItem = functionalComponent("Track-Item"){ props whiteSpace = WhiteSpace.nowrap overflow = Overflow.hidden } - + details.durationSec.toString() + + "${details.durationSec/60} min, ${details.durationSec%60} sec" } } } - when(details.downloaded){ + when(downloadStatus){ is DownloadStatus.NotDownloaded ->{ DownloadButton { - onClick = { props.downloadTrack(details) } - status = details.downloaded + onClick = { + setDownloadStatus(DownloadStatus.Queued) + props.downloadTrack(details) + } + status = downloadStatus } } is DownloadStatus.Downloading -> { CircularProgressBar { - progress = (details.downloaded as DownloadStatus.Downloading).progress + progress = downloadStatus.progress } } DownloadStatus.Queued -> { @@ -111,7 +117,7 @@ private val trackItem = functionalComponent("Track-Item"){ props DownloadStatus.Downloaded -> { DownloadButton { onClick = {} - status = details.downloaded + status = downloadStatus } } DownloadStatus.Converting -> { @@ -120,7 +126,7 @@ private val trackItem = functionalComponent("Track-Item"){ props DownloadStatus.Failed -> { DownloadButton { onClick = {} - status = details.downloaded + status = downloadStatus } } } @@ -128,7 +134,7 @@ private val trackItem = functionalComponent("Track-Item"){ props css { alignItems = Align.center display =Display.flex - flexGrow = 1.0 + paddingRight = 16.px } } } diff --git a/web-app/src/main/kotlin/navbar/NavBar.kt b/web-app/src/main/kotlin/navbar/NavBar.kt index 4d0c0d75..8f791c68 100644 --- a/web-app/src/main/kotlin/navbar/NavBar.kt +++ b/web-app/src/main/kotlin/navbar/NavBar.kt @@ -2,10 +2,11 @@ package navbar import kotlinx.css.* import kotlinx.html.id +import kotlinx.html.js.onBlurFunction +import kotlinx.html.js.onClickFunction import react.* import styled.* - @Suppress("FunctionName") fun RBuilder.NavBar(handler: NavBarProps.() -> Unit): ReactElement{ return child(navBar){ @@ -17,47 +18,107 @@ fun RBuilder.NavBar(handler: NavBarProps.() -> Unit): ReactElement{ external interface NavBarProps:RProps{ var isBackVisible: Boolean + var popBackToHomeScreen: () -> Unit } private val navBar = functionalComponent("NavBar") { props -> + styledNav { css { +NavBarStyles.nav } - styledImg(src = "left-arrow.svg",alt = "Back Arrow"){ - css { - height = 42.px - width = 42.px - display = if(props.isBackVisible) Display.inline else Display.none - filter = "invert(100)" - marginRight = 12.px - } - } - styledImg(src = "spotiflyer.svg",alt = "Logo") { - css { - height = 42.px - width = 42.px - } - } - styledH1 { - +"SpotiFlyer" + styledDiv{ attrs { - id = "appName" + onClickFunction = { + props.popBackToHomeScreen() + } + onBlurFunction = { + props.popBackToHomeScreen() + } } - css{ - fontSize = 46.px - margin(horizontal = 14.px) + styledImg(src = "left-arrow.svg",alt = "Back Arrow"){ + css { + height = 42.px + width = 42.px + display = if(props.isBackVisible) Display.inline else Display.none + filter = "invert(100)" + marginRight = 12.px + } } } - styledA(href = "https://github.com/Shabinder/SpotiFlyer/"){ - styledImg(src = "github.svg"){ + styledA(href = "TODO Website Link") { + css { + display = Display.flex + alignItems = Align.center + } + styledImg(src = "spotiflyer.svg",alt = "Logo") { css { height = 42.px width = 42.px } } + styledH1 { + +"SpotiFlyer" + attrs { + id = "appName" + } + css{ + fontSize = 46.px + margin(horizontal = 14.px) + } + } + } + + /*val (corsMode,setCorsMode) = useState(CorsProxy.SelfHostedCorsProxy() as CorsProxy) + + useEffect { + setCorsMode(corsProxy) + }*/ + + styledDiv{ + + /*styledH4 { + "Extension" } + + styledDiv { + styledInput(type = InputType.checkBox) { + attrs{ + id = "cmn-toggle-4" + value = "Extension" + checked = corsMode.extensionMode() + onChangeFunction = { + val state = it.target as HTMLInputElement + if(state.checked){ + setCorsMode(corsProxy.toggle(CorsProxy.PublicProxyWithExtension())) + } else{ + setCorsMode(corsProxy.toggle(CorsProxy.SelfHostedCorsProxy())) + } + println("Active Proxy: ${corsProxy.url}") + } + } + css{ + classes = mutableListOf("cmn-toggle","cmn-toggle-round-flat") + } + } + styledLabel { attrs { htmlFor = "cmn-toggle-4" } } + css{ + classes = mutableListOf("switch") + marginLeft = 8.px + marginRight = 16.px + } + }*/ + + styledA(href = "https://github.com/Shabinder/SpotiFlyer/"){ + styledImg(src = "github.svg"){ + css { + height = 42.px + width = 42.px + } + } + } css { + display = Display.flex + alignItems = Align.center marginLeft = LinearDimension.auto } } diff --git a/web-app/src/main/kotlin/root/RootR.kt b/web-app/src/main/kotlin/root/RootR.kt index 279e4220..d0882e15 100644 --- a/web-app/src/main/kotlin/root/RootR.kt +++ b/web-app/src/main/kotlin/root/RootR.kt @@ -6,7 +6,6 @@ import com.shabinder.common.root.SpotiFlyerRoot.* import extras.RenderableRootComponent import extras.renderableChild import home.HomeScreen -import kotlinx.coroutines.launch import list.ListScreen import navbar.NavBar import react.RBuilder @@ -19,9 +18,12 @@ class RootR(props: Props) : RenderableRootComponent renderableChild(HomeScreen::class, (component as Child.Main).component) @@ -33,7 +35,7 @@ class RootR(props: Props) : RenderableRootComponent, + var routerState: RouterState<*, Child> ) : RState } diff --git a/web-app/src/main/resources/css-circular-prog-bar.css b/web-app/src/main/resources/css-circular-prog-bar.css index f3a9bfa9..fc7fc4f9 100644 --- a/web-app/src/main/resources/css-circular-prog-bar.css +++ b/web-app/src/main/resources/css-circular-prog-bar.css @@ -1,7 +1,5 @@ .progress-circle { margin: 20px; - position: relative; /* so that children can be absolutely positioned */ - line-height: 5em; } .progress-circle:after{ @@ -14,6 +12,7 @@ height: 4.3em; background-color: white; content: " "; + margin-top: 0.35em; } /* Text inside the control */ .progress-circle span { diff --git a/web-app/src/main/resources/styles.css b/web-app/src/main/resources/styles.css index 5f49257e..afa98b23 100644 --- a/web-app/src/main/resources/styles.css +++ b/web-app/src/main/resources/styles.css @@ -16,6 +16,10 @@ html { background-image: url("header-dark.jpg"); font-family: Lora,Helvetica Neue,Helvetica,Arial,sans-serif; } +a:link, a:visited { + color: white; + text-decoration: none; +} body, html { width: 100%; height: 100%; @@ -238,4 +242,57 @@ body, html { -webkit-transform: scale3D(0, 0, 1); transform: scale3D(0, 0, 1); } +} + +/*Extension Switch*/ +.cmn-toggle { + position: absolute; + margin-left: -9999px; + visibility: hidden; +} + +.cmn-toggle + label { + display: block; + position: relative; + cursor: pointer; + outline: none; + user-select: none; +} + +input.cmn-toggle-round-flat + label { + /* width = 2*height or 2*border-radius */ + padding: 2px; + width: 40px; + height: 20px; + border: 3px solid #dddddd; + border-radius: 60px; + transition: border-color 0.3s; +} + +input.cmn-toggle-round-flat + label:before, +input.cmn-toggle-round-flat + label:after { + display: block; + position: absolute; + content: ""; +} + +input.cmn-toggle-round-flat + label:after { + /* width = 2*border-radius */ + top: 4px; + left: 4px; + bottom: 4px; + width: 16px; + background-color: #dddddd; + border-radius: 52px; + transition: margin 0.3s, background 0.3s; +} + +input.cmn-toggle-round-flat:checked + label { + border-color: #8ce196; +} + +input.cmn-toggle-round-flat:checked + label:after { + /* margin-left = border-radius from 'input.cmn-toggle-round-flat + label' */ + margin-left: 20px; + background-color: #8ce196; } \ No newline at end of file From 4471e38d3b36584168873d1373b53971933be1ec Mon Sep 17 00:00:00 2001 From: shabinder Date: Tue, 16 Mar 2021 16:49:16 +0530 Subject: [PATCH 03/10] Web App WorkFlows --- .github/workflows/build-and-publish-kjs.yml | 32 +++++++++++++++++++ .../willowtree/fuzzywuzzy/ExtractorTest.kt | 2 +- .../willowtree/fuzzywuzzy/FuzzyWuzzyTest.kt | 2 +- 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/build-and-publish-kjs.yml diff --git a/.github/workflows/build-and-publish-kjs.yml b/.github/workflows/build-and-publish-kjs.yml new file mode 100644 index 00000000..a332627b --- /dev/null +++ b/.github/workflows/build-and-publish-kjs.yml @@ -0,0 +1,32 @@ +name: Build and Publish +on: [ push, pull_request ] +jobs: + build: + name: Test and Build + runs-on: ubuntu-latest + steps: + + # Setup Java 1.8 environment for the next steps + - name: Setup Java + uses: actions/setup-java@v1 + with: + java-version: 1.8 + + # Check out current repository + - name: Fetch Sources + uses: actions/checkout@v2.3.1 + + # Build application + - name: Test and Build + run: ./gradlew :web-app:build + + # If main branch update, deploy to gh-pages + - name: Deploy + if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/KMP' + uses: JamesIves/github-pages-deploy-action@4.1.0 + with: + repository-name: Shabinder/SpotiFlyer + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BRANCH: gh-pages # The branch the action should deploy to. + FOLDER: build/distributions # The folder the action should deploy. + CLEAN: true # Automatically remove deleted files from the deploy branch diff --git a/fuzzywuzzy/app/src/test/kotlin/com/willowtree/fuzzywuzzy/ExtractorTest.kt b/fuzzywuzzy/app/src/test/kotlin/com/willowtree/fuzzywuzzy/ExtractorTest.kt index 1cee74a2..9446c2e2 100644 --- a/fuzzywuzzy/app/src/test/kotlin/com/willowtree/fuzzywuzzy/ExtractorTest.kt +++ b/fuzzywuzzy/app/src/test/kotlin/com/willowtree/fuzzywuzzy/ExtractorTest.kt @@ -1,4 +1,4 @@ -package com.willowtreeapps.fuzzywuzzy +package kotlin.com.willowtree.fuzzywuzzy import com.willowtreeapps.fuzzywuzzy.diffutils.Extractor import com.willowtreeapps.fuzzywuzzy.diffutils.algorithms.WeightedRatio diff --git a/fuzzywuzzy/app/src/test/kotlin/com/willowtree/fuzzywuzzy/FuzzyWuzzyTest.kt b/fuzzywuzzy/app/src/test/kotlin/com/willowtree/fuzzywuzzy/FuzzyWuzzyTest.kt index d860bdd1..e34eff1a 100644 --- a/fuzzywuzzy/app/src/test/kotlin/com/willowtree/fuzzywuzzy/FuzzyWuzzyTest.kt +++ b/fuzzywuzzy/app/src/test/kotlin/com/willowtree/fuzzywuzzy/FuzzyWuzzyTest.kt @@ -1,4 +1,4 @@ -package com.willowtreeapps.fuzzywuzzy +package kotlin.com.willowtree.fuzzywuzzy import com.willowtreeapps.fuzzywuzzy.diffutils.FuzzySearch From 176c19b2d0a9c49c0c415eb923df2068d0d4461d Mon Sep 17 00:00:00 2001 From: Shabinder Singh Date: Tue, 16 Mar 2021 17:03:51 +0530 Subject: [PATCH 04/10] Create web-app/build/distributions Dir. --- web-app/build/distributions/temp.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 web-app/build/distributions/temp.txt diff --git a/web-app/build/distributions/temp.txt b/web-app/build/distributions/temp.txt new file mode 100644 index 00000000..9c595a6f --- /dev/null +++ b/web-app/build/distributions/temp.txt @@ -0,0 +1 @@ +temp From 871727430fe5d741c3e8b1dce5a394e468f92f1c Mon Sep 17 00:00:00 2001 From: shabinder Date: Tue, 16 Mar 2021 17:19:56 +0530 Subject: [PATCH 05/10] WorkFlow update --- .github/workflows/build-and-publish-kjs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-publish-kjs.yml b/.github/workflows/build-and-publish-kjs.yml index a332627b..51b507c9 100644 --- a/.github/workflows/build-and-publish-kjs.yml +++ b/.github/workflows/build-and-publish-kjs.yml @@ -26,7 +26,7 @@ jobs: uses: JamesIves/github-pages-deploy-action@4.1.0 with: repository-name: Shabinder/SpotiFlyer - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} BRANCH: gh-pages # The branch the action should deploy to. - FOLDER: build/distributions # The folder the action should deploy. + FOLDER: web-app/build/distributions # The folder the action should deploy. CLEAN: true # Automatically remove deleted files from the deploy branch From c1e61f28ab108875834fcc27e27116d412aaeee4 Mon Sep 17 00:00:00 2001 From: shabinder Date: Tue, 16 Mar 2021 17:30:52 +0530 Subject: [PATCH 06/10] WorkFlow token update --- .github/workflows/build-and-publish-kjs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-and-publish-kjs.yml b/.github/workflows/build-and-publish-kjs.yml index 51b507c9..a0e243a8 100644 --- a/.github/workflows/build-and-publish-kjs.yml +++ b/.github/workflows/build-and-publish-kjs.yml @@ -26,6 +26,7 @@ jobs: uses: JamesIves/github-pages-deploy-action@4.1.0 with: repository-name: Shabinder/SpotiFlyer + token: adea5c27d4c7ee42dc4010cb8adef92538f6e52d # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} BRANCH: gh-pages # The branch the action should deploy to. FOLDER: web-app/build/distributions # The folder the action should deploy. From b5387d8abdb8c4bdcd3df9aac37ea8097eb518be Mon Sep 17 00:00:00 2001 From: shabinder Date: Tue, 16 Mar 2021 18:24:23 +0530 Subject: [PATCH 07/10] Google Ads and Mobile Search Box enhancement --- web-app/src/main/kotlin/home/IconList.kt | 2 +- web-app/src/main/kotlin/navbar/NavBar.kt | 2 +- web-app/src/main/resources/index.html | 9 +++++++ web-app/src/main/resources/styles.css | 34 +++++++++++++++--------- 4 files changed, 33 insertions(+), 14 deletions(-) diff --git a/web-app/src/main/kotlin/home/IconList.kt b/web-app/src/main/kotlin/home/IconList.kt index cb21ad7b..8bd17319 100644 --- a/web-app/src/main/kotlin/home/IconList.kt +++ b/web-app/src/main/kotlin/home/IconList.kt @@ -31,7 +31,7 @@ private val iconList = functionalComponent("IconList") { props -> + Styles.makeRow } for((icon,platformLink) in props.iconsAndPlatforms){ - styledA(href = platformLink){ + styledA(href = platformLink,target="_blank"){ styledImg { attrs { src = icon diff --git a/web-app/src/main/kotlin/navbar/NavBar.kt b/web-app/src/main/kotlin/navbar/NavBar.kt index 8f791c68..cc5b6a3b 100644 --- a/web-app/src/main/kotlin/navbar/NavBar.kt +++ b/web-app/src/main/kotlin/navbar/NavBar.kt @@ -47,7 +47,7 @@ private val navBar = functionalComponent("NavBar") { props -> } } } - styledA(href = "TODO Website Link") { + styledA(href = "https://shabinder.github.io/SpotiFlyer/",target="_blank") { css { display = Display.flex alignItems = Align.center diff --git a/web-app/src/main/resources/index.html b/web-app/src/main/resources/index.html index 92db7273..224f7c73 100644 --- a/web-app/src/main/resources/index.html +++ b/web-app/src/main/resources/index.html @@ -10,6 +10,15 @@ + + +
diff --git a/web-app/src/main/resources/styles.css b/web-app/src/main/resources/styles.css index afa98b23..19a2a262 100644 --- a/web-app/src/main/resources/styles.css +++ b/web-app/src/main/resources/styles.css @@ -2,16 +2,7 @@ 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; @@ -102,7 +93,7 @@ body, html { } .searchBox:hover > .searchInput { - width: 30vw; + width: 35vw; padding: 0 6px; } @@ -144,6 +135,8 @@ body, html { line-height: 40px; width: 0px; } + +/*Download All Button*/ #download-all-text { color: black; display: none; @@ -295,4 +288,21 @@ input.cmn-toggle-round-flat:checked + label:after { /* margin-left = border-radius from 'input.cmn-toggle-round-flat + label' */ margin-left: 20px; background-color: #8ce196; -} \ No newline at end of file +} +@media screen and (max-width: 600px) { + /* CSS HERE ONLY ON PHONE */ + .info-banners { + flex-direction: column; + } + .searchInput { + width: 35vw; + padding: 0 6px; + } + .search-icon { + filter: none; + } + .searchButton { + background: white; + color : #2f3640; + } +} From a0b5b0d170a4f5652730e738e0f0a50d4cc0b376 Mon Sep 17 00:00:00 2001 From: shabinder Date: Tue, 16 Mar 2021 19:35:08 +0530 Subject: [PATCH 08/10] RazorPay Button Added --- web-app/src/main/kotlin/home/IconList.kt | 32 +++++++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/web-app/src/main/kotlin/home/IconList.kt b/web-app/src/main/kotlin/home/IconList.kt index 8bd17319..368ad338 100644 --- a/web-app/src/main/kotlin/home/IconList.kt +++ b/web-app/src/main/kotlin/home/IconList.kt @@ -1,11 +1,13 @@ package home +import kotlinx.browser.document import kotlinx.css.* +import kotlinx.dom.appendElement +import kotlinx.dom.createElement +import kotlinx.html.SCRIPT +import kotlinx.html.id import react.* -import styled.css -import styled.styledA -import styled.styledDiv -import styled.styledImg +import styled.* external interface IconListProps : RProps { var iconsAndPlatforms: Map @@ -22,6 +24,19 @@ fun RBuilder.IconList(handler:IconListProps.() -> Unit): ReactElement { } private val iconList = functionalComponent("IconList") { props -> + + useEffect { + val form = document.getElementById("razorpay-form")!! + repeat(form.childNodes.length){ + form.childNodes.item(it)?.let { it1 -> form.removeChild(it1) } + } + form.appendElement("script"){ + this.setAttribute("src","https://checkout.razorpay.com/v1/payment-button.js") + this.setAttribute("async", true.toString()) + this.setAttribute("data-payment_button_id", "pl_GnKuuDBdBu0ank") + } + } + styledDiv { css { margin(18.px) @@ -30,7 +45,16 @@ private val iconList = functionalComponent("IconList") { props -> } + Styles.makeRow } + val firstElem = props.iconsAndPlatforms.keys.elementAt(1) for((icon,platformLink) in props.iconsAndPlatforms){ + if(icon == firstElem && props.isBadge){ + //
+ styledForm { + attrs{ + id = "razorpay-form" + } + } + } styledA(href = platformLink,target="_blank"){ styledImg { attrs { From 31c9a2aa576a4d2541273e4f720a959479faefa7 Mon Sep 17 00:00:00 2001 From: shabinder Date: Tue, 16 Mar 2021 19:44:45 +0530 Subject: [PATCH 09/10] Spotify Auth Fix for web --- .../com/shabinder/common/di/providers/SpotifyProvider.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/SpotifyProvider.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/SpotifyProvider.kt index c99708c0..d49a2a32 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/SpotifyProvider.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/SpotifyProvider.kt @@ -20,6 +20,7 @@ import co.touchlab.kermit.Kermit import com.shabinder.common.di.* import com.shabinder.common.di.spotify.SpotifyRequests import com.shabinder.common.di.spotify.authenticateSpotify +import com.shabinder.common.models.AllPlatforms import com.shabinder.common.models.PlatformQueryResult import com.shabinder.common.models.TrackDetails import com.shabinder.common.models.spotify.Album @@ -43,7 +44,9 @@ class SpotifyProvider( init { logger.d { "Creating Spotify Provider" } GlobalScope.launch(Dispatchers.Default) { - authenticateSpotifyClient() + if(currentPlatform is AllPlatforms.Js){ + authenticateSpotifyClient(override = true) + }else authenticateSpotifyClient() } } From cf9ed5e885de23042f0e73bb353cf2d8b41e03eb Mon Sep 17 00:00:00 2001 From: shabinder Date: Tue, 16 Mar 2021 20:39:51 +0530 Subject: [PATCH 10/10] styles and some fixes --- web-app/src/main/kotlin/home/HomeScreen.kt | 19 +++++++++++++++++-- web-app/src/main/kotlin/home/IconList.kt | 12 ------------ .../src/main/kotlin/list/LoadingSpinner.kt | 2 ++ web-app/src/main/resources/styles.css | 9 +++++++-- 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/web-app/src/main/kotlin/home/HomeScreen.kt b/web-app/src/main/kotlin/home/HomeScreen.kt index 7200d516..1fc2c616 100644 --- a/web-app/src/main/kotlin/home/HomeScreen.kt +++ b/web-app/src/main/kotlin/home/HomeScreen.kt @@ -5,8 +5,10 @@ import com.shabinder.common.main.SpotiFlyerMain import com.shabinder.common.main.SpotiFlyerMain.State import com.shabinder.common.models.AllPlatforms import extras.RenderableComponent +import kotlinx.browser.document import kotlinx.coroutines.flow.Flow import kotlinx.css.* +import kotlinx.dom.appendElement import react.* import styled.css import styled.styledDiv @@ -17,6 +19,19 @@ class HomeScreen( props, initialState = State() ) { + override fun componentDidMount() { + super.componentDidMount() + val form = document.getElementById("razorpay-form")!! + repeat(form.childNodes.length){ + form.childNodes.item(0)?.let { it1 -> form.removeChild(it1) } + form.childNodes.item(it)?.let { it1 -> form.removeChild(it1) } + } + form.appendElement("script"){ + this.setAttribute("src","https://checkout.razorpay.com/v1/payment-button.js") + this.setAttribute("async", true.toString()) + this.setAttribute("data-payment_button_id", "pl_GnKuuDBdBu0ank") + } + } override val stateFlow: Flow = model.models @@ -56,8 +71,8 @@ class HomeScreen( private val platformIconList = mapOf( "spotify.svg" to "https://open.spotify.com/", "gaana.svg" to "https://www.gaana.com/", - "youtube.svg" to "https://www.youtube.com/", - "youtube_music.svg" to "https://music.youtube.com/" + //"youtube.svg" to "https://www.youtube.com/", + //"youtube_music.svg" to "https://music.youtube.com/" ) private val badges = mapOf( "https://img.shields.io/github/v/release/Shabinder/SpotiFlyer?color=7885FF&label=SpotiFlyer&logo=android&style=for-the-badge" diff --git a/web-app/src/main/kotlin/home/IconList.kt b/web-app/src/main/kotlin/home/IconList.kt index 368ad338..7f591eaa 100644 --- a/web-app/src/main/kotlin/home/IconList.kt +++ b/web-app/src/main/kotlin/home/IconList.kt @@ -25,18 +25,6 @@ fun RBuilder.IconList(handler:IconListProps.() -> Unit): ReactElement { private val iconList = functionalComponent("IconList") { props -> - useEffect { - val form = document.getElementById("razorpay-form")!! - repeat(form.childNodes.length){ - form.childNodes.item(it)?.let { it1 -> form.removeChild(it1) } - } - form.appendElement("script"){ - this.setAttribute("src","https://checkout.razorpay.com/v1/payment-button.js") - this.setAttribute("async", true.toString()) - this.setAttribute("data-payment_button_id", "pl_GnKuuDBdBu0ank") - } - } - styledDiv { css { margin(18.px) diff --git a/web-app/src/main/kotlin/list/LoadingSpinner.kt b/web-app/src/main/kotlin/list/LoadingSpinner.kt index 98adb9ba..45b57f52 100644 --- a/web-app/src/main/kotlin/list/LoadingSpinner.kt +++ b/web-app/src/main/kotlin/list/LoadingSpinner.kt @@ -1,5 +1,6 @@ package list +import kotlinx.css.marginRight import kotlinx.css.px import kotlinx.css.width import react.* @@ -24,6 +25,7 @@ private val loadingSpinner = functionalComponent("Loading-Spinner") { css{ classes = mutableListOf("lds-ring") width = 50.px + marginRight = 8.px } } } \ No newline at end of file diff --git a/web-app/src/main/resources/styles.css b/web-app/src/main/resources/styles.css index 19a2a262..372053bc 100644 --- a/web-app/src/main/resources/styles.css +++ b/web-app/src/main/resources/styles.css @@ -28,14 +28,19 @@ body, html { font-weight: 100; text-shadow: 0.3px 0.5px #ffffff; } + .headingTitle{ text-align: center; margin: 10px; font-family: 'RocknRoll One', sans-serif; } -.glow-button:hover { +.glow-button, .PaymentButton { + transition: all .2s ease-in-out; +} +.glow-button:hover, .PaymentButton:hover { color: rgba(255, 255, 255, 1); box-shadow: 0 5px 15px rgb(105, 44, 143); + transform: scale(1.1); } /*Loading Spinner*/ @@ -295,7 +300,7 @@ input.cmn-toggle-round-flat:checked + label:after { flex-direction: column; } .searchInput { - width: 35vw; + width: 60vw; padding: 0 6px; } .search-icon {