mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-22 01:04:31 +01:00
Web App Changes and Fixes
This commit is contained in:
parent
90b99cbb51
commit
d4d3835f8d
@ -0,0 +1,7 @@
|
||||
package com.shabinder.common.models
|
||||
|
||||
sealed class AllPlatforms{
|
||||
object Js:AllPlatforms()
|
||||
object Jvm:AllPlatforms()
|
||||
object Native:AllPlatforms()
|
||||
}
|
@ -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()
|
@ -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
|
||||
|
@ -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<TrackDetails>,
|
||||
fetcher: FetchPlatformQueryResult,
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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")
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 -> {
|
||||
|
@ -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){}
|
||||
|
@ -42,6 +42,7 @@ interface SpotiFlyerMain {
|
||||
val dir: Dir
|
||||
val showPopUpMessage:(String)->Unit
|
||||
}
|
||||
|
||||
sealed class Output {
|
||||
data class Search(val link: String) : Output()
|
||||
}
|
||||
|
@ -2,4 +2,5 @@ package com.shabinder.common.root.callbacks
|
||||
|
||||
interface SpotiFlyerRootCallBacks {
|
||||
fun searchLink(link:String)
|
||||
fun popBackToHomeScreen()
|
||||
}
|
||||
|
@ -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 =
|
||||
|
@ -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<SpotiFlyerMain.State> = model.models
|
||||
|
||||
override fun RBuilder.render() {
|
||||
println("Rendering New State = \"${state.data}\" ")
|
||||
styledDiv{
|
||||
css {
|
||||
display = Display.flex
|
||||
|
@ -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<SearchbarProps>("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<SearchbarProps>("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 {
|
||||
|
@ -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<CircularProgressBarProps>(
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
@ -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<DownloadAllButtonProps>("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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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{
|
||||
|
@ -22,9 +22,12 @@ fun RBuilder.TrackItem(handler: TrackItemProps.() -> Unit): ReactElement {
|
||||
}
|
||||
|
||||
private val trackItem = functionalComponent<TrackItemProps>("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<TrackItemProps>("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<TrackItemProps>("Track-Item"){ props
|
||||
DownloadStatus.Downloaded -> {
|
||||
DownloadButton {
|
||||
onClick = {}
|
||||
status = details.downloaded
|
||||
status = downloadStatus
|
||||
}
|
||||
}
|
||||
DownloadStatus.Converting -> {
|
||||
@ -120,7 +126,7 @@ private val trackItem = functionalComponent<TrackItemProps>("Track-Item"){ props
|
||||
DownloadStatus.Failed -> {
|
||||
DownloadButton {
|
||||
onClick = {}
|
||||
status = details.downloaded
|
||||
status = downloadStatus
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -128,7 +134,7 @@ private val trackItem = functionalComponent<TrackItemProps>("Track-Item"){ props
|
||||
css {
|
||||
alignItems = Align.center
|
||||
display =Display.flex
|
||||
flexGrow = 1.0
|
||||
paddingRight = 16.px
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<NavBarProps>("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
|
||||
}
|
||||
}
|
||||
|
@ -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<SpotiFlyerRoot>) : RenderableRootComponent<SpotiFlyerRo
|
||||
private val component: Child
|
||||
get() = model.routerState.value.activeChild.component
|
||||
|
||||
private val callBacks get() = model.callBacks
|
||||
|
||||
override fun RBuilder.render() {
|
||||
NavBar {
|
||||
isBackVisible = (component is Child.List)
|
||||
popBackToHomeScreen = callBacks::popBackToHomeScreen
|
||||
}
|
||||
when(component){
|
||||
is Child.Main -> renderableChild(HomeScreen::class, (component as Child.Main).component)
|
||||
@ -33,7 +35,7 @@ class RootR(props: Props<SpotiFlyerRoot>) : RenderableRootComponent<SpotiFlyerRo
|
||||
model.routerState.bindToState { routerState = it }
|
||||
}
|
||||
class State(
|
||||
var routerState: RouterState<*, Child>,
|
||||
var routerState: RouterState<*, Child>
|
||||
) : RState
|
||||
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user