From 82378d324c9455403b16b3be8055b444d396520c Mon Sep 17 00:00:00 2001 From: jozef Date: Tue, 18 May 2021 22:57:04 +0200 Subject: [PATCH 01/27] Reworking HTTP clients, prepare TokenClientProvider.kt --- .../kotlin/online/hudacek/fxradio/FxRadio.kt | 8 +-- .../{ApiClient.kt => ApiServiceProvider.kt} | 18 ++++--- .../fxradio/api/{ => http}/HttpClient.kt | 14 +++-- .../api/http/interceptors/TokenInterceptor.kt | 30 +++++++++++ .../http/interceptors/UserAgentInterceptor.kt | 36 +++++++++++++ .../providers/DefaultClientProvider.kt} | 53 +++++++++---------- .../api/http/providers/HttpClientProvider.kt | 35 ++++++++++++ .../api/http/providers/TokenClientProvider.kt | 31 +++++++++++ .../fxradio/api/{ => stations}/StationsApi.kt | 11 ++-- .../api/{ => stations}/model/AddedStation.kt | 2 +- .../api/{ => stations}/model/ClickResult.kt | 2 +- .../api/{ => stations}/model/Country.kt | 2 +- .../api/{ => stations}/model/Search.kt | 2 +- .../api/{ => stations}/model/Station.kt | 2 +- .../api/{ => stations}/model/StationBody.kt | 2 +- .../api/{ => stations}/model/StatsResult.kt | 2 +- .../api/{ => stations}/model/VoteResult.kt | 2 +- .../online/hudacek/fxradio/events/AppEvent.kt | 2 +- .../hudacek/fxradio/storage/ImageCache.kt | 2 +- .../storage/db/PinnedCountriesTable.kt | 2 +- .../fxradio/storage/db/StationTable.kt | 2 +- .../online/hudacek/fxradio/ui/Extensions.kt | 4 +- .../hudacek/fxradio/ui/ImageExtensions.kt | 4 +- .../hudacek/fxradio/ui/menu/MenuExtensions.kt | 2 +- .../fxradio/ui/modal/StationInfoFragment.kt | 2 +- .../view/library/LibraryCountriesFragment.kt | 2 +- .../fxradio/ui/view/player/PlayerView.kt | 2 +- .../ui/view/stations/StationsDataGridView.kt | 2 +- .../ui/view/stations/StationsHistoryView.kt | 2 +- .../fxradio/usecase/AddStationUseCase.kt | 4 +- .../hudacek/fxradio/usecase/BaseUseCase.kt | 2 +- .../hudacek/fxradio/usecase/ClickUseCase.kt | 4 +- .../fxradio/usecase/GetCountriesUseCase.kt | 6 +-- .../fxradio/usecase/GetServersUseCase.kt | 2 +- .../usecase/GetStationsByCountryUseCase.kt | 6 +-- .../fxradio/usecase/GetStatsUseCase.kt | 2 +- .../fxradio/usecase/GetTopStationsUseCase.kt | 2 +- .../fxradio/usecase/PinCountryUseCase.kt | 2 +- .../hudacek/fxradio/usecase/SearchUseCase.kt | 6 +-- .../fxradio/usecase/UnPinCountryUseCase.kt | 2 +- .../hudacek/fxradio/usecase/VoteUseCase.kt | 4 +- .../fxradio/viewmodel/AddStationViewModel.kt | 4 +- .../fxradio/viewmodel/FavouritesViewModel.kt | 2 +- .../fxradio/viewmodel/HistoryViewModel.kt | 2 +- .../fxradio/viewmodel/LibraryViewModel.kt | 2 +- .../fxradio/viewmodel/PlayerViewModel.kt | 2 +- .../fxradio/viewmodel/StationInfoViewModel.kt | 2 +- .../fxradio/viewmodel/StationsViewModel.kt | 2 +- .../fxradio/viewmodel/StatsViewModel.kt | 2 +- .../integration/BasicFunctionalityTests.kt | 6 +-- 50 files changed, 242 insertions(+), 104 deletions(-) rename src/main/kotlin/online/hudacek/fxradio/api/{ApiClient.kt => ApiServiceProvider.kt} (66%) rename src/main/kotlin/online/hudacek/fxradio/api/{ => http}/HttpClient.kt (82%) create mode 100644 src/main/kotlin/online/hudacek/fxradio/api/http/interceptors/TokenInterceptor.kt create mode 100644 src/main/kotlin/online/hudacek/fxradio/api/http/interceptors/UserAgentInterceptor.kt rename src/main/kotlin/online/hudacek/fxradio/api/{OkHttpHelper.kt => http/providers/DefaultClientProvider.kt} (59%) create mode 100644 src/main/kotlin/online/hudacek/fxradio/api/http/providers/HttpClientProvider.kt create mode 100644 src/main/kotlin/online/hudacek/fxradio/api/http/providers/TokenClientProvider.kt rename src/main/kotlin/online/hudacek/fxradio/api/{ => stations}/StationsApi.kt (87%) rename src/main/kotlin/online/hudacek/fxradio/api/{ => stations}/model/AddedStation.kt (93%) rename src/main/kotlin/online/hudacek/fxradio/api/{ => stations}/model/ClickResult.kt (93%) rename src/main/kotlin/online/hudacek/fxradio/api/{ => stations}/model/Country.kt (92%) rename src/main/kotlin/online/hudacek/fxradio/api/{ => stations}/model/Search.kt (72%) rename src/main/kotlin/online/hudacek/fxradio/api/{ => stations}/model/Station.kt (96%) rename src/main/kotlin/online/hudacek/fxradio/api/{ => stations}/model/StationBody.kt (88%) rename src/main/kotlin/online/hudacek/fxradio/api/{ => stations}/model/StatsResult.kt (90%) rename src/main/kotlin/online/hudacek/fxradio/api/{ => stations}/model/VoteResult.kt (93%) diff --git a/src/main/kotlin/online/hudacek/fxradio/FxRadio.kt b/src/main/kotlin/online/hudacek/fxradio/FxRadio.kt index dcff5727..46b62624 100644 --- a/src/main/kotlin/online/hudacek/fxradio/FxRadio.kt +++ b/src/main/kotlin/online/hudacek/fxradio/FxRadio.kt @@ -17,8 +17,8 @@ package online.hudacek.fxradio import javafx.stage.Stage -import online.hudacek.fxradio.api.HttpClient -import online.hudacek.fxradio.api.StationsApi +import online.hudacek.fxradio.api.http.HttpClient +import online.hudacek.fxradio.api.stations.StationsApi import online.hudacek.fxradio.ui.CustomErrorHandler import online.hudacek.fxradio.ui.style.Styles import online.hudacek.fxradio.ui.style.StylesDark @@ -114,8 +114,8 @@ open class FxRadio(stylesheet: KClass) : App(MainView::class, st //Should be called when on every place that is closing the app fun shutdownApp() { playerViewModel.releasePlayer() - StationsApi.client.shutdown() - HttpClient.shutdown() + StationsApi.serviceProvider.close() + HttpClient.close() } } } diff --git a/src/main/kotlin/online/hudacek/fxradio/api/ApiClient.kt b/src/main/kotlin/online/hudacek/fxradio/api/ApiServiceProvider.kt similarity index 66% rename from src/main/kotlin/online/hudacek/fxradio/api/ApiClient.kt rename to src/main/kotlin/online/hudacek/fxradio/api/ApiServiceProvider.kt index 8011cc30..41fdc0a9 100644 --- a/src/main/kotlin/online/hudacek/fxradio/api/ApiClient.kt +++ b/src/main/kotlin/online/hudacek/fxradio/api/ApiServiceProvider.kt @@ -16,29 +16,33 @@ package online.hudacek.fxradio.api +import online.hudacek.fxradio.api.http.providers.DefaultClientProvider +import online.hudacek.fxradio.api.http.providers.HttpClientProvider import retrofit2.Retrofit import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory import retrofit2.converter.gson.GsonConverterFactory -import kotlin.reflect.KClass /** - * ApiClient for [baseUrl] that can create instance of Retrofit Service + * Provides Retrofit instance for [baseUrl] */ -class ApiClient(private val baseUrl: String) : OkHttpHelper() { +class ApiServiceProvider(private val baseUrl: String, + private val httpClientProvider: HttpClientProvider = DefaultClientProvider()) { /** * Retrofit client instance for [baseUrl] with custom [httpClient] */ - private val client: Retrofit + val retrofit: Retrofit get() = Retrofit.Builder() .addCallAdapterFactory(RxJava2CallAdapterFactory.createAsync()) .addConverterFactory(GsonConverterFactory.create()) .baseUrl(baseUrl) - .client(httpClient) + .client(httpClientProvider.client) .build() /** - * Constructs Retrofit [service] class + * Constructs Retrofit service class of type [T] */ - fun create(service: KClass): T = client.create(service.java) + inline fun get(): T = retrofit.create(T::class.java) + + fun close() = httpClientProvider.close() } \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/api/HttpClient.kt b/src/main/kotlin/online/hudacek/fxradio/api/http/HttpClient.kt similarity index 82% rename from src/main/kotlin/online/hudacek/fxradio/api/HttpClient.kt rename to src/main/kotlin/online/hudacek/fxradio/api/http/HttpClient.kt index ccb40ef8..2a67d24f 100644 --- a/src/main/kotlin/online/hudacek/fxradio/api/HttpClient.kt +++ b/src/main/kotlin/online/hudacek/fxradio/api/http/HttpClient.kt @@ -14,13 +14,14 @@ * limitations under the License. */ -package online.hudacek.fxradio.api +package online.hudacek.fxradio.api.http import mu.KotlinLogging import okhttp3.Call import okhttp3.Callback import okhttp3.Request import okhttp3.Response +import online.hudacek.fxradio.api.http.providers.DefaultClientProvider import java.io.IOException import java.net.InetAddress @@ -30,14 +31,17 @@ private val logger = KotlinLogging.logger {} * Creates and holds single instance of OkHttpClient * Plain OkHttpClient is used mostly for downloading images of stations */ -object HttpClient : OkHttpHelper() { +object HttpClient { + + //Uses default HTTP client + private val clientProvider = DefaultClientProvider() /** * Performs DNS lookup for [address] */ fun lookup(address: String): MutableList { logger.debug { "Performing DNS lookup for $address" } - return httpClient.dns().lookup(address) + return clientProvider.client.dns().lookup(address) } /** @@ -45,7 +49,7 @@ object HttpClient : OkHttpHelper() { */ fun request(url: String, success: (Response) -> Unit, - fail: (IOException) -> Unit) = httpClient.newCall(buildRequest(url)).enqueue( + fail: (IOException) -> Unit) = clientProvider.client.newCall(buildRequest(url)).enqueue( object : Callback { override fun onResponse(call: Call, response: Response) { success(response) @@ -66,4 +70,6 @@ object HttpClient : OkHttpHelper() { private fun buildRequest(url: String) = Request.Builder() .url(url) .build() + + fun close() = clientProvider.close() } \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/api/http/interceptors/TokenInterceptor.kt b/src/main/kotlin/online/hudacek/fxradio/api/http/interceptors/TokenInterceptor.kt new file mode 100644 index 00000000..08538aea --- /dev/null +++ b/src/main/kotlin/online/hudacek/fxradio/api/http/interceptors/TokenInterceptor.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2020 FXRadio by hudacek.online + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package online.hudacek.fxradio.api.http.interceptors + +import okhttp3.Interceptor +import okhttp3.Response + +class TokenInterceptor(private val bearerToken: String) : Interceptor { + + override fun intercept(chain: Interceptor.Chain): Response = chain.proceed( + chain.request() + .newBuilder() + .addHeader("Authorization", "Bearer $bearerToken") + .build() + ) +} diff --git a/src/main/kotlin/online/hudacek/fxradio/api/http/interceptors/UserAgentInterceptor.kt b/src/main/kotlin/online/hudacek/fxradio/api/http/interceptors/UserAgentInterceptor.kt new file mode 100644 index 00000000..487fe830 --- /dev/null +++ b/src/main/kotlin/online/hudacek/fxradio/api/http/interceptors/UserAgentInterceptor.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2020 FXRadio by hudacek.online + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package online.hudacek.fxradio.api.http.interceptors + +import okhttp3.Interceptor +import okhttp3.Response +import online.hudacek.fxradio.FxRadio + +class UserAgentInterceptor : Interceptor { + + /** + * Defines what is app sending as a User Agent string + */ + private val userAgent = "${FxRadio.appName}/${FxRadio.version}" + + override fun intercept(chain: Interceptor.Chain): Response = chain.proceed( + chain.request() + .newBuilder() + .header("User-Agent", userAgent) + .build() + ) +} \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/api/OkHttpHelper.kt b/src/main/kotlin/online/hudacek/fxradio/api/http/providers/DefaultClientProvider.kt similarity index 59% rename from src/main/kotlin/online/hudacek/fxradio/api/OkHttpHelper.kt rename to src/main/kotlin/online/hudacek/fxradio/api/http/providers/DefaultClientProvider.kt index 5a88c023..3be9f626 100644 --- a/src/main/kotlin/online/hudacek/fxradio/api/OkHttpHelper.kt +++ b/src/main/kotlin/online/hudacek/fxradio/api/http/providers/DefaultClientProvider.kt @@ -14,31 +14,32 @@ * limitations under the License. */ -package online.hudacek.fxradio.api +package online.hudacek.fxradio.api.http.providers import mu.KotlinLogging import okhttp3.ConnectionPool +import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor -import online.hudacek.fxradio.FxRadio +import online.hudacek.fxradio.api.http.interceptors.UserAgentInterceptor import java.util.concurrent.TimeUnit private val logger = KotlinLogging.logger {} +/** + * Defines Connection timeout for the duration og w in seconds + */ +private const val timeoutInSeconds: Long = 20 + /** * Helper variables and methods for OkHttpClient */ -open class OkHttpHelper { +open class DefaultClientProvider : HttpClientProvider { /** * Defines the limits for the active connections */ - private val connectionPool = ConnectionPool(5, 20, TimeUnit.SECONDS) - - /** - * Defines what is app sending as a User Agent string - */ - private val userAgent = "${FxRadio.appName}/${FxRadio.version}" + private val connectionPool = ConnectionPool(5, timeoutInSeconds, TimeUnit.SECONDS) /** * Enables Logging of http requests in app logger on debug level @@ -50,36 +51,30 @@ open class OkHttpHelper { level = HttpLoggingInterceptor.Level.BODY } + override val interceptors: MutableList = mutableListOf(loggerInterceptor) + /** * Construct http client with custom user agent, connection pool and timeouts */ - protected val httpClient: OkHttpClient by lazy { + override val client: OkHttpClient by lazy { OkHttpClient.Builder() //The whole call should not take longer than 20 secs - .callTimeout(20, TimeUnit.SECONDS) - .addNetworkInterceptor { chain -> - chain.proceed( - chain.request() - .newBuilder() - .header("User-Agent", userAgent) - .build() - ) - } - .addInterceptor(loggerInterceptor) + .callTimeout(timeoutInSeconds, TimeUnit.SECONDS) + .addNetworkInterceptor(UserAgentInterceptor()) .connectionPool(connectionPool) - .build() + .apply { + interceptors.forEach { + addInterceptor(it) + } + }.build() } /** * Correctly kill all OkHttpClient threads */ - fun shutdown() { - logger.info { "Shutting down OkHttpClient $httpClient" } - logger.debug { - "Idle connections: ${connectionPool.idleConnectionCount()} " + - "All connections: ${connectionPool.connectionCount()}" - } - httpClient.dispatcher().executorService().shutdownNow() - httpClient.connectionPool().evictAll() + override fun close() { + logger.info { "Shutting down OkHttpClient $client" } + logger.debug { "Idle/All: ${connectionPool.idleConnectionCount()}/${connectionPool.connectionCount()}" } + super.close() } } \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/api/http/providers/HttpClientProvider.kt b/src/main/kotlin/online/hudacek/fxradio/api/http/providers/HttpClientProvider.kt new file mode 100644 index 00000000..63dde1bd --- /dev/null +++ b/src/main/kotlin/online/hudacek/fxradio/api/http/providers/HttpClientProvider.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2020 FXRadio by hudacek.online + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package online.hudacek.fxradio.api.http.providers + +import okhttp3.Interceptor +import okhttp3.OkHttpClient + +interface HttpClientProvider { + + val client: OkHttpClient + + val interceptors: List + + /** + * Default implementation of closing the OkHttpClient + */ + fun close() { + client.dispatcher().executorService().shutdownNow() + client.connectionPool().evictAll() + } +} \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/api/http/providers/TokenClientProvider.kt b/src/main/kotlin/online/hudacek/fxradio/api/http/providers/TokenClientProvider.kt new file mode 100644 index 00000000..e3ac773c --- /dev/null +++ b/src/main/kotlin/online/hudacek/fxradio/api/http/providers/TokenClientProvider.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2020 FXRadio by hudacek.online + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package online.hudacek.fxradio.api.http.providers + +import online.hudacek.fxradio.api.http.interceptors.TokenInterceptor + +/** + * OkHttpClient with token authentication + */ +class TokenClientProvider(bearerToken: String) : DefaultClientProvider() { + + private val tokenInterceptor = TokenInterceptor(bearerToken) + + init { + interceptors += tokenInterceptor + } +} \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/api/StationsApi.kt b/src/main/kotlin/online/hudacek/fxradio/api/stations/StationsApi.kt similarity index 87% rename from src/main/kotlin/online/hudacek/fxradio/api/StationsApi.kt rename to src/main/kotlin/online/hudacek/fxradio/api/stations/StationsApi.kt index 377a0fc4..f2068eae 100644 --- a/src/main/kotlin/online/hudacek/fxradio/api/StationsApi.kt +++ b/src/main/kotlin/online/hudacek/fxradio/api/stations/StationsApi.kt @@ -14,10 +14,11 @@ * limitations under the License. */ -package online.hudacek.fxradio.api +package online.hudacek.fxradio.api.stations import io.reactivex.Single -import online.hudacek.fxradio.api.model.* +import online.hudacek.fxradio.api.ApiServiceProvider +import online.hudacek.fxradio.api.stations.model.* import online.hudacek.fxradio.utils.Properties import online.hudacek.fxradio.utils.Property import online.hudacek.fxradio.viewmodel.Servers @@ -66,13 +67,13 @@ interface StationsApi { private val apiServerProperty = Property(Properties.ApiServer) - val client: ApiClient by lazy { + val serviceProvider: ApiServiceProvider by lazy { if (apiServerProperty.isPresent) { viewModel.item = Servers(apiServerProperty.get()) } - ApiClient("https://${viewModel.selectedProperty.value}") + ApiServiceProvider("https://${viewModel.selectedProperty.value}") } - val service by lazy { client.create(StationsApi::class) } + val service by lazy { serviceProvider.get() } } } \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/api/model/AddedStation.kt b/src/main/kotlin/online/hudacek/fxradio/api/stations/model/AddedStation.kt similarity index 93% rename from src/main/kotlin/online/hudacek/fxradio/api/model/AddedStation.kt rename to src/main/kotlin/online/hudacek/fxradio/api/stations/model/AddedStation.kt index 9e7ba927..1a24cc0b 100644 --- a/src/main/kotlin/online/hudacek/fxradio/api/model/AddedStation.kt +++ b/src/main/kotlin/online/hudacek/fxradio/api/stations/model/AddedStation.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package online.hudacek.fxradio.api.model +package online.hudacek.fxradio.api.stations.model data class AddedStation(val ok: Boolean, val message: String, diff --git a/src/main/kotlin/online/hudacek/fxradio/api/model/ClickResult.kt b/src/main/kotlin/online/hudacek/fxradio/api/stations/model/ClickResult.kt similarity index 93% rename from src/main/kotlin/online/hudacek/fxradio/api/model/ClickResult.kt rename to src/main/kotlin/online/hudacek/fxradio/api/stations/model/ClickResult.kt index 7b90e9e0..ad640f17 100644 --- a/src/main/kotlin/online/hudacek/fxradio/api/model/ClickResult.kt +++ b/src/main/kotlin/online/hudacek/fxradio/api/stations/model/ClickResult.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package online.hudacek.fxradio.api.model +package online.hudacek.fxradio.api.stations.model data class ClickResult(val ok: Boolean, val message: String, diff --git a/src/main/kotlin/online/hudacek/fxradio/api/model/Country.kt b/src/main/kotlin/online/hudacek/fxradio/api/stations/model/Country.kt similarity index 92% rename from src/main/kotlin/online/hudacek/fxradio/api/model/Country.kt rename to src/main/kotlin/online/hudacek/fxradio/api/stations/model/Country.kt index 656027f8..8d026418 100644 --- a/src/main/kotlin/online/hudacek/fxradio/api/model/Country.kt +++ b/src/main/kotlin/online/hudacek/fxradio/api/stations/model/Country.kt @@ -1,4 +1,4 @@ -package online.hudacek.fxradio.api.model +package online.hudacek.fxradio.api.stations.model import java.util.* diff --git a/src/main/kotlin/online/hudacek/fxradio/api/model/Search.kt b/src/main/kotlin/online/hudacek/fxradio/api/stations/model/Search.kt similarity index 72% rename from src/main/kotlin/online/hudacek/fxradio/api/model/Search.kt rename to src/main/kotlin/online/hudacek/fxradio/api/stations/model/Search.kt index 6f18bf11..34b6365e 100644 --- a/src/main/kotlin/online/hudacek/fxradio/api/model/Search.kt +++ b/src/main/kotlin/online/hudacek/fxradio/api/stations/model/Search.kt @@ -1,4 +1,4 @@ -package online.hudacek.fxradio.api.model +package online.hudacek.fxradio.api.stations.model data class SearchBody(val name: String, val limit: Int = 200) diff --git a/src/main/kotlin/online/hudacek/fxradio/api/model/Station.kt b/src/main/kotlin/online/hudacek/fxradio/api/stations/model/Station.kt similarity index 96% rename from src/main/kotlin/online/hudacek/fxradio/api/model/Station.kt rename to src/main/kotlin/online/hudacek/fxradio/api/stations/model/Station.kt index 3ea86caa..45d1aa65 100644 --- a/src/main/kotlin/online/hudacek/fxradio/api/model/Station.kt +++ b/src/main/kotlin/online/hudacek/fxradio/api/stations/model/Station.kt @@ -1,4 +1,4 @@ -package online.hudacek.fxradio.api.model +package online.hudacek.fxradio.api.stations.model import online.hudacek.fxradio.FxRadio diff --git a/src/main/kotlin/online/hudacek/fxradio/api/model/StationBody.kt b/src/main/kotlin/online/hudacek/fxradio/api/stations/model/StationBody.kt similarity index 88% rename from src/main/kotlin/online/hudacek/fxradio/api/model/StationBody.kt rename to src/main/kotlin/online/hudacek/fxradio/api/stations/model/StationBody.kt index 6b197f65..ea0914be 100644 --- a/src/main/kotlin/online/hudacek/fxradio/api/model/StationBody.kt +++ b/src/main/kotlin/online/hudacek/fxradio/api/stations/model/StationBody.kt @@ -1,4 +1,4 @@ -package online.hudacek.fxradio.api.model +package online.hudacek.fxradio.api.stations.model data class StationBody(val name: String = "", val url: String = "", diff --git a/src/main/kotlin/online/hudacek/fxradio/api/model/StatsResult.kt b/src/main/kotlin/online/hudacek/fxradio/api/stations/model/StatsResult.kt similarity index 90% rename from src/main/kotlin/online/hudacek/fxradio/api/model/StatsResult.kt rename to src/main/kotlin/online/hudacek/fxradio/api/stations/model/StatsResult.kt index d411c058..155fcfcb 100644 --- a/src/main/kotlin/online/hudacek/fxradio/api/model/StatsResult.kt +++ b/src/main/kotlin/online/hudacek/fxradio/api/stations/model/StatsResult.kt @@ -1,4 +1,4 @@ -package online.hudacek.fxradio.api.model +package online.hudacek.fxradio.api.stations.model data class StatsResult(val supported_version: String, val software_version: String, diff --git a/src/main/kotlin/online/hudacek/fxradio/api/model/VoteResult.kt b/src/main/kotlin/online/hudacek/fxradio/api/stations/model/VoteResult.kt similarity index 93% rename from src/main/kotlin/online/hudacek/fxradio/api/model/VoteResult.kt rename to src/main/kotlin/online/hudacek/fxradio/api/stations/model/VoteResult.kt index 9df2c408..8d679448 100644 --- a/src/main/kotlin/online/hudacek/fxradio/api/model/VoteResult.kt +++ b/src/main/kotlin/online/hudacek/fxradio/api/stations/model/VoteResult.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package online.hudacek.fxradio.api.model +package online.hudacek.fxradio.api.stations.model data class VoteResult(val ok: Boolean, val message: String) \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/events/AppEvent.kt b/src/main/kotlin/online/hudacek/fxradio/events/AppEvent.kt index 84864f66..47cb98fd 100644 --- a/src/main/kotlin/online/hudacek/fxradio/events/AppEvent.kt +++ b/src/main/kotlin/online/hudacek/fxradio/events/AppEvent.kt @@ -17,7 +17,7 @@ package online.hudacek.fxradio.events import io.reactivex.subjects.BehaviorSubject -import online.hudacek.fxradio.api.model.Station +import online.hudacek.fxradio.api.stations.model.Station import online.hudacek.fxradio.events.data.AppNotification import online.hudacek.fxradio.media.StreamMetaData import online.hudacek.fxradio.viewmodel.LibraryState diff --git a/src/main/kotlin/online/hudacek/fxradio/storage/ImageCache.kt b/src/main/kotlin/online/hudacek/fxradio/storage/ImageCache.kt index a260acc3..d930a3e5 100644 --- a/src/main/kotlin/online/hudacek/fxradio/storage/ImageCache.kt +++ b/src/main/kotlin/online/hudacek/fxradio/storage/ImageCache.kt @@ -21,7 +21,7 @@ import io.reactivex.disposables.Disposable import javafx.scene.image.Image import mu.KotlinLogging import online.hudacek.fxradio.Config -import online.hudacek.fxradio.api.model.Station +import online.hudacek.fxradio.api.stations.model.Station import online.hudacek.fxradio.ui.defaultRadioLogo import org.apache.commons.io.FileUtils import tornadofx.observableListOf diff --git a/src/main/kotlin/online/hudacek/fxradio/storage/db/PinnedCountriesTable.kt b/src/main/kotlin/online/hudacek/fxradio/storage/db/PinnedCountriesTable.kt index b9ad6558..f6a3a7f6 100644 --- a/src/main/kotlin/online/hudacek/fxradio/storage/db/PinnedCountriesTable.kt +++ b/src/main/kotlin/online/hudacek/fxradio/storage/db/PinnedCountriesTable.kt @@ -19,7 +19,7 @@ package online.hudacek.fxradio.storage.db import io.reactivex.Observable import io.reactivex.Single import mu.KotlinLogging -import online.hudacek.fxradio.api.model.Country +import online.hudacek.fxradio.api.stations.model.Country private val logger = KotlinLogging.logger {} diff --git a/src/main/kotlin/online/hudacek/fxradio/storage/db/StationTable.kt b/src/main/kotlin/online/hudacek/fxradio/storage/db/StationTable.kt index cf730791..fe1b240d 100644 --- a/src/main/kotlin/online/hudacek/fxradio/storage/db/StationTable.kt +++ b/src/main/kotlin/online/hudacek/fxradio/storage/db/StationTable.kt @@ -19,7 +19,7 @@ package online.hudacek.fxradio.storage.db import io.reactivex.Observable import io.reactivex.Single import mu.KotlinLogging -import online.hudacek.fxradio.api.model.Station +import online.hudacek.fxradio.api.stations.model.Station private val logger = KotlinLogging.logger {} diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/Extensions.kt b/src/main/kotlin/online/hudacek/fxradio/ui/Extensions.kt index 6db4ab84..0bcc57b2 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/Extensions.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/Extensions.kt @@ -36,8 +36,8 @@ import javafx.scene.paint.Color import javafx.stage.Window import javafx.util.Duration import online.hudacek.fxradio.FxRadio -import online.hudacek.fxradio.api.model.Country -import online.hudacek.fxradio.api.model.countryCode +import online.hudacek.fxradio.api.stations.model.Country +import online.hudacek.fxradio.api.stations.model.countryCode import online.hudacek.fxradio.ui.style.Styles import org.controlsfx.control.NotificationPane import org.controlsfx.control.textfield.CustomTextField diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/ImageExtensions.kt b/src/main/kotlin/online/hudacek/fxradio/ui/ImageExtensions.kt index 689eec33..461e6ca5 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/ImageExtensions.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/ImageExtensions.kt @@ -21,8 +21,8 @@ import javafx.scene.image.Image import javafx.scene.image.ImageView import mu.KotlinLogging import online.hudacek.fxradio.Config -import online.hudacek.fxradio.api.HttpClient -import online.hudacek.fxradio.api.model.Station +import online.hudacek.fxradio.api.http.HttpClient +import online.hudacek.fxradio.api.stations.model.Station import online.hudacek.fxradio.storage.ImageCache import online.hudacek.fxradio.storage.ImageCache.isCached import tornadofx.onChange diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/menu/MenuExtensions.kt b/src/main/kotlin/online/hudacek/fxradio/ui/menu/MenuExtensions.kt index b099a4bc..9a27839b 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/menu/MenuExtensions.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/menu/MenuExtensions.kt @@ -19,7 +19,7 @@ package online.hudacek.fxradio.ui.menu import javafx.beans.property.Property import javafx.scene.control.* import javafx.scene.input.KeyCodeCombination -import online.hudacek.fxradio.api.model.Station +import online.hudacek.fxradio.api.stations.model.Station import tornadofx.bind import tornadofx.booleanBinding import tornadofx.disableWhen diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/modal/StationInfoFragment.kt b/src/main/kotlin/online/hudacek/fxradio/ui/modal/StationInfoFragment.kt index 3b183196..91ccb448 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/modal/StationInfoFragment.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/modal/StationInfoFragment.kt @@ -23,7 +23,7 @@ import javafx.geometry.Pos import javafx.scene.control.Label import javafx.scene.effect.DropShadow import javafx.scene.paint.Color -import online.hudacek.fxradio.api.model.Station +import online.hudacek.fxradio.api.stations.model.Station import online.hudacek.fxradio.ui.* import online.hudacek.fxradio.ui.style.Styles import online.hudacek.fxradio.viewmodel.PlayerViewModel diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryCountriesFragment.kt b/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryCountriesFragment.kt index c5b5f004..e6d8f654 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryCountriesFragment.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryCountriesFragment.kt @@ -19,7 +19,7 @@ package online.hudacek.fxradio.ui.view.library import javafx.beans.property.BooleanProperty import javafx.beans.property.ListProperty import javafx.geometry.Pos -import online.hudacek.fxradio.api.model.Country +import online.hudacek.fxradio.api.stations.model.Country import online.hudacek.fxradio.ui.BaseFragment import online.hudacek.fxradio.ui.flagIcon import online.hudacek.fxradio.ui.showWhen diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/view/player/PlayerView.kt b/src/main/kotlin/online/hudacek/fxradio/ui/view/player/PlayerView.kt index 377dc2f4..4f459db8 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/view/player/PlayerView.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/view/player/PlayerView.kt @@ -18,7 +18,7 @@ package online.hudacek.fxradio.ui.view.player import javafx.geometry.Pos import javafx.scene.layout.Priority -import online.hudacek.fxradio.api.model.Station +import online.hudacek.fxradio.api.stations.model.Station import online.hudacek.fxradio.ui.BaseView import online.hudacek.fxradio.ui.make import online.hudacek.fxradio.ui.requestFocusOnSceneAvailable diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsDataGridView.kt b/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsDataGridView.kt index 26d30290..3920c070 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsDataGridView.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsDataGridView.kt @@ -24,7 +24,7 @@ import javafx.scene.effect.DropShadow import javafx.scene.paint.Color import javafx.stage.StageStyle import online.hudacek.fxradio.Config -import online.hudacek.fxradio.api.model.tagsSplit +import online.hudacek.fxradio.api.stations.model.tagsSplit import online.hudacek.fxradio.ui.BaseView import online.hudacek.fxradio.ui.menu.FavouritesMenu import online.hudacek.fxradio.ui.modal.Modals diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsHistoryView.kt b/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsHistoryView.kt index 88e890a5..66bc5134 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsHistoryView.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsHistoryView.kt @@ -19,7 +19,7 @@ package online.hudacek.fxradio.ui.view.stations import javafx.geometry.Pos import javafx.scene.effect.DropShadow import javafx.scene.paint.Color -import online.hudacek.fxradio.api.model.tagsSplit +import online.hudacek.fxradio.api.stations.model.tagsSplit import online.hudacek.fxradio.ui.BaseView import online.hudacek.fxradio.ui.showWhen import online.hudacek.fxradio.ui.smallLabel diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/AddStationUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/AddStationUseCase.kt index ac1f4c8f..29f6fae9 100644 --- a/src/main/kotlin/online/hudacek/fxradio/usecase/AddStationUseCase.kt +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/AddStationUseCase.kt @@ -18,8 +18,8 @@ package online.hudacek.fxradio.usecase import io.reactivex.Single import mu.KotlinLogging -import online.hudacek.fxradio.api.model.AddedStation -import online.hudacek.fxradio.api.model.StationBody +import online.hudacek.fxradio.api.stations.model.AddedStation +import online.hudacek.fxradio.api.stations.model.StationBody private val logger = KotlinLogging.logger {} diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/BaseUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/BaseUseCase.kt index bd644584..849cb01d 100644 --- a/src/main/kotlin/online/hudacek/fxradio/usecase/BaseUseCase.kt +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/BaseUseCase.kt @@ -19,7 +19,7 @@ package online.hudacek.fxradio.usecase import com.github.thomasnield.rxkotlinfx.observeOnFx import io.reactivex.SingleTransformer import io.reactivex.schedulers.Schedulers -import online.hudacek.fxradio.api.StationsApi +import online.hudacek.fxradio.api.stations.StationsApi import online.hudacek.fxradio.events.AppEvent import tornadofx.Controller diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/ClickUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/ClickUseCase.kt index 707fd1be..bc9f5161 100644 --- a/src/main/kotlin/online/hudacek/fxradio/usecase/ClickUseCase.kt +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/ClickUseCase.kt @@ -17,8 +17,8 @@ package online.hudacek.fxradio.usecase import io.reactivex.Single -import online.hudacek.fxradio.api.model.ClickResult -import online.hudacek.fxradio.api.model.Station +import online.hudacek.fxradio.api.stations.model.ClickResult +import online.hudacek.fxradio.api.stations.model.Station /** * Increases click count of the station diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/GetCountriesUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/GetCountriesUseCase.kt index e230b9be..edbfb82e 100644 --- a/src/main/kotlin/online/hudacek/fxradio/usecase/GetCountriesUseCase.kt +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/GetCountriesUseCase.kt @@ -18,9 +18,9 @@ package online.hudacek.fxradio.usecase import io.reactivex.disposables.Disposable import javafx.beans.property.ListProperty -import online.hudacek.fxradio.api.model.CountriesBody -import online.hudacek.fxradio.api.model.Country -import online.hudacek.fxradio.api.model.isValid +import online.hudacek.fxradio.api.stations.model.CountriesBody +import online.hudacek.fxradio.api.stations.model.Country +import online.hudacek.fxradio.api.stations.model.isValid /** * Gets list of valid country names and count of stations in it diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/GetServersUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/GetServersUseCase.kt index a4cd30ff..dc4cd188 100644 --- a/src/main/kotlin/online/hudacek/fxradio/usecase/GetServersUseCase.kt +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/GetServersUseCase.kt @@ -19,7 +19,7 @@ package online.hudacek.fxradio.usecase import javafx.collections.ObservableList import javafx.concurrent.Task import online.hudacek.fxradio.Config -import online.hudacek.fxradio.api.HttpClient +import online.hudacek.fxradio.api.http.HttpClient import tornadofx.asObservable /** diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/GetStationsByCountryUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/GetStationsByCountryUseCase.kt index 702b09a9..31ad4cd4 100644 --- a/src/main/kotlin/online/hudacek/fxradio/usecase/GetStationsByCountryUseCase.kt +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/GetStationsByCountryUseCase.kt @@ -17,9 +17,9 @@ package online.hudacek.fxradio.usecase import io.reactivex.Single -import online.hudacek.fxradio.api.model.CountriesBody -import online.hudacek.fxradio.api.model.Country -import online.hudacek.fxradio.api.model.Station +import online.hudacek.fxradio.api.stations.model.CountriesBody +import online.hudacek.fxradio.api.stations.model.Country +import online.hudacek.fxradio.api.stations.model.Station /** * Gets all stations from provided country name diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/GetStatsUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/GetStatsUseCase.kt index 4e0dd632..0dd4179c 100644 --- a/src/main/kotlin/online/hudacek/fxradio/usecase/GetStatsUseCase.kt +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/GetStatsUseCase.kt @@ -17,7 +17,7 @@ package online.hudacek.fxradio.usecase import io.reactivex.Single -import online.hudacek.fxradio.api.model.StatsResult +import online.hudacek.fxradio.api.stations.model.StatsResult /** * Gets status information from currently used radio-browser API server diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/GetTopStationsUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/GetTopStationsUseCase.kt index 9dfe3b40..01ca51ec 100644 --- a/src/main/kotlin/online/hudacek/fxradio/usecase/GetTopStationsUseCase.kt +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/GetTopStationsUseCase.kt @@ -17,7 +17,7 @@ package online.hudacek.fxradio.usecase import io.reactivex.Single -import online.hudacek.fxradio.api.model.Station +import online.hudacek.fxradio.api.stations.model.Station /** * Gets list of Top 50 stations from radio-browser API diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/PinCountryUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/PinCountryUseCase.kt index 70197796..fa7caca0 100644 --- a/src/main/kotlin/online/hudacek/fxradio/usecase/PinCountryUseCase.kt +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/PinCountryUseCase.kt @@ -17,7 +17,7 @@ package online.hudacek.fxradio.usecase import io.reactivex.Single -import online.hudacek.fxradio.api.model.Country +import online.hudacek.fxradio.api.stations.model.Country import online.hudacek.fxradio.storage.db.Tables class PinCountryUseCase : BaseUseCase>() { diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/SearchUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/SearchUseCase.kt index 6ae507a5..00386dfd 100644 --- a/src/main/kotlin/online/hudacek/fxradio/usecase/SearchUseCase.kt +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/SearchUseCase.kt @@ -17,9 +17,9 @@ package online.hudacek.fxradio.usecase import io.reactivex.Single -import online.hudacek.fxradio.api.model.SearchBody -import online.hudacek.fxradio.api.model.SearchByTagBody -import online.hudacek.fxradio.api.model.Station +import online.hudacek.fxradio.api.stations.model.SearchBody +import online.hudacek.fxradio.api.stations.model.SearchByTagBody +import online.hudacek.fxradio.api.stations.model.Station /** * Searches for all stations that contains provided tag diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/UnPinCountryUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/UnPinCountryUseCase.kt index b2d0ab01..0a641c31 100644 --- a/src/main/kotlin/online/hudacek/fxradio/usecase/UnPinCountryUseCase.kt +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/UnPinCountryUseCase.kt @@ -17,7 +17,7 @@ package online.hudacek.fxradio.usecase import io.reactivex.Single -import online.hudacek.fxradio.api.model.Country +import online.hudacek.fxradio.api.stations.model.Country import online.hudacek.fxradio.storage.db.Tables class UnPinCountryUseCase : BaseUseCase>() { diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/VoteUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/VoteUseCase.kt index 990e302e..100b757e 100644 --- a/src/main/kotlin/online/hudacek/fxradio/usecase/VoteUseCase.kt +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/VoteUseCase.kt @@ -17,8 +17,8 @@ package online.hudacek.fxradio.usecase import io.reactivex.Single -import online.hudacek.fxradio.api.model.Station -import online.hudacek.fxradio.api.model.VoteResult +import online.hudacek.fxradio.api.stations.model.Station +import online.hudacek.fxradio.api.stations.model.VoteResult /** * Increases vote count of the station diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/AddStationViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/AddStationViewModel.kt index a045e9eb..68722378 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/AddStationViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/AddStationViewModel.kt @@ -19,8 +19,8 @@ package online.hudacek.fxradio.viewmodel import com.github.thomasnield.rxkotlinfx.toObservable import javafx.beans.property.BooleanProperty import javafx.beans.property.StringProperty -import online.hudacek.fxradio.api.model.Station -import online.hudacek.fxradio.api.model.StationBody +import online.hudacek.fxradio.api.stations.model.Station +import online.hudacek.fxradio.api.stations.model.StationBody import online.hudacek.fxradio.usecase.AddStationUseCase import tornadofx.property import tornadofx.stringBinding diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/FavouritesViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/FavouritesViewModel.kt index 44b8af78..bca27de3 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/FavouritesViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/FavouritesViewModel.kt @@ -21,7 +21,7 @@ import io.reactivex.disposables.Disposable import javafx.beans.property.ListProperty import javafx.collections.ObservableList import mu.KotlinLogging -import online.hudacek.fxradio.api.model.Station +import online.hudacek.fxradio.api.stations.model.Station import online.hudacek.fxradio.events.data.AppNotification import online.hudacek.fxradio.storage.db.Tables import online.hudacek.fxradio.ui.formatted diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/HistoryViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/HistoryViewModel.kt index 73f4ab4c..2e40193e 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/HistoryViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/HistoryViewModel.kt @@ -19,7 +19,7 @@ package online.hudacek.fxradio.viewmodel import javafx.beans.property.ListProperty import javafx.collections.ObservableList import mu.KotlinLogging -import online.hudacek.fxradio.api.model.Station +import online.hudacek.fxradio.api.stations.model.Station import online.hudacek.fxradio.storage.db.Tables import tornadofx.observableListOf import tornadofx.property diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/LibraryViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/LibraryViewModel.kt index 99d705c5..e84848e2 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/LibraryViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/LibraryViewModel.kt @@ -21,7 +21,7 @@ import javafx.beans.property.BooleanProperty import javafx.beans.property.ListProperty import javafx.collections.ObservableList import mu.KotlinLogging -import online.hudacek.fxradio.api.model.Country +import online.hudacek.fxradio.api.stations.model.Country import online.hudacek.fxradio.usecase.GetCountriesUseCase import online.hudacek.fxradio.usecase.PinCountryUseCase import online.hudacek.fxradio.usecase.UnPinCountryUseCase diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/PlayerViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/PlayerViewModel.kt index ef5e54ce..4aea1eea 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/PlayerViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/PlayerViewModel.kt @@ -23,7 +23,7 @@ import javafx.beans.property.BooleanProperty import javafx.beans.property.DoubleProperty import javafx.beans.property.ObjectProperty import javafx.beans.property.StringProperty -import online.hudacek.fxradio.api.model.Station +import online.hudacek.fxradio.api.stations.model.Station import online.hudacek.fxradio.events.data.AppNotification import online.hudacek.fxradio.media.MediaPlayer import online.hudacek.fxradio.media.MediaPlayerFactory diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/StationInfoViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/StationInfoViewModel.kt index 9d55ee21..c902514a 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/StationInfoViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/StationInfoViewModel.kt @@ -21,7 +21,7 @@ import javafx.beans.property.ListProperty import javafx.beans.property.ObjectProperty import javafx.beans.property.StringProperty import javafx.collections.ObservableList -import online.hudacek.fxradio.api.model.Station +import online.hudacek.fxradio.api.stations.model.Station import tornadofx.observableListOf import tornadofx.property diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/StationsViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/StationsViewModel.kt index 6a937ad8..6ff9d03f 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/StationsViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/StationsViewModel.kt @@ -19,7 +19,7 @@ package online.hudacek.fxradio.viewmodel import io.reactivex.Single import javafx.beans.property.ListProperty import javafx.collections.ObservableList -import online.hudacek.fxradio.api.model.Station +import online.hudacek.fxradio.api.stations.model.Station import online.hudacek.fxradio.events.data.AppNotification import online.hudacek.fxradio.usecase.GetStationsByCountryUseCase import online.hudacek.fxradio.usecase.GetTopStationsUseCase diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/StatsViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/StatsViewModel.kt index 04869fc4..71a3f81a 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/StatsViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/StatsViewModel.kt @@ -18,7 +18,7 @@ package online.hudacek.fxradio.viewmodel import javafx.beans.property.ListProperty import javafx.collections.ObservableList -import online.hudacek.fxradio.api.model.StatsResult +import online.hudacek.fxradio.api.stations.model.StatsResult import online.hudacek.fxradio.usecase.GetStatsUseCase import tornadofx.observableListOf import tornadofx.property diff --git a/src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt b/src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt index 8baa4023..555b3f33 100644 --- a/src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt +++ b/src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt @@ -22,9 +22,9 @@ import javafx.scene.control.Slider import javafx.stage.Stage import online.hudacek.fxradio.FxRadio import online.hudacek.fxradio.FxRadioLight -import online.hudacek.fxradio.api.StationsApi -import online.hudacek.fxradio.api.model.SearchBody -import online.hudacek.fxradio.api.model.Station +import online.hudacek.fxradio.api.stations.StationsApi +import online.hudacek.fxradio.api.stations.model.SearchBody +import online.hudacek.fxradio.api.stations.model.Station import online.hudacek.fxradio.storage.db.Tables import online.hudacek.fxradio.utils.macos.MacMenu import online.hudacek.fxradio.viewmodel.LibraryItem From b28bacc68a68d870cf96f6651fcccd954d7b0620 Mon Sep 17 00:00:00 2001 From: jozef Date: Fri, 21 May 2021 00:08:48 +0200 Subject: [PATCH 02/27] Added rolling logger + refactoring of some classes --- build.gradle | 2 +- .../hudacek/fxradio/api/http/HttpClient.kt | 5 ++- .../http/providers/DefaultClientProvider.kt | 6 ++- .../fxradio/media/MediaPlayerFactory.kt | 15 ++++---- .../media/StreamUnavailableException.kt | 2 +- .../player/humble/HumbleAudioComponent.kt | 2 +- .../media/player/humble/HumbleMetaService.kt | 2 +- .../media/player/vlc/VLCAudioComponent.kt | 4 +- .../fxradio/media/player/vlc/VLCEvents.kt | 2 +- .../hudacek/fxradio/storage/ImageCache.kt | 6 +-- .../hudacek/fxradio/storage/db/Database.kt | 4 +- .../storage/db/PinnedCountriesTable.kt | 2 +- .../fxradio/storage/db/StationTable.kt | 2 +- .../hudacek/fxradio/ui/ImageExtensions.kt | 2 +- .../hudacek/fxradio/ui/menu/MenuExtensions.kt | 11 ++---- .../online/hudacek/fxradio/ui/modal/Modals.kt | 4 ++ .../fxradio/ui/modal/StationInfoFragment.kt | 5 +-- .../hudacek/fxradio/ui/view/MenuBarView.kt | 26 ++++++------- .../ui/view/stations/StationsDataGridView.kt | 5 +-- .../fxradio/usecase/AddStationUseCase.kt | 2 +- .../hudacek/fxradio/utils/macos/MacUtils.kt | 3 +- .../utils/macos/{MacMenu.kt => NSMenu.kt} | 38 +++++++++---------- .../hudacek/fxradio/utils/macos/NSMenuBar.kt | 37 ++++++++++++++++++ .../fxradio/viewmodel/FavouritesViewModel.kt | 2 +- .../fxradio/viewmodel/HistoryViewModel.kt | 4 +- .../fxradio/viewmodel/LibraryViewModel.kt | 4 +- src/main/resources/log4j2.xml | 13 ++++++- .../integration/BasicFunctionalityTests.kt | 4 +- .../fxradio/integration/DarkModeTests.kt | 4 +- 29 files changed, 132 insertions(+), 86 deletions(-) rename src/main/kotlin/online/hudacek/fxradio/utils/macos/{MacMenu.kt => NSMenu.kt} (73%) create mode 100644 src/main/kotlin/online/hudacek/fxradio/utils/macos/NSMenuBar.kt diff --git a/build.gradle b/build.gradle index fd77bffc..51ab9a44 100644 --- a/build.gradle +++ b/build.gradle @@ -139,7 +139,7 @@ jfx { if(runtimePath != null && System.getenv('FX_APPEND_PATH') != null) { runtimePath = runtimePath + System.getenv('FX_APPEND_PATH') } - logger.warn('Runtime path is: {}', runtimePath) + logger.info('Runtime path is: {}', runtimePath) bundleArguments = [ licenseType: 'ASL 2.0', licenseFile: 'LICENSE', //, diff --git a/src/main/kotlin/online/hudacek/fxradio/api/http/HttpClient.kt b/src/main/kotlin/online/hudacek/fxradio/api/http/HttpClient.kt index 2a67d24f..dc7b7d59 100644 --- a/src/main/kotlin/online/hudacek/fxradio/api/http/HttpClient.kt +++ b/src/main/kotlin/online/hudacek/fxradio/api/http/HttpClient.kt @@ -40,8 +40,9 @@ object HttpClient { * Performs DNS lookup for [address] */ fun lookup(address: String): MutableList { - logger.debug { "Performing DNS lookup for $address" } - return clientProvider.client.dns().lookup(address) + return clientProvider.client.dns().lookup(address).apply { + logger.debug { "DNS lookup for $address returned $this" } + } } /** diff --git a/src/main/kotlin/online/hudacek/fxradio/api/http/providers/DefaultClientProvider.kt b/src/main/kotlin/online/hudacek/fxradio/api/http/providers/DefaultClientProvider.kt index 3be9f626..242c744b 100644 --- a/src/main/kotlin/online/hudacek/fxradio/api/http/providers/DefaultClientProvider.kt +++ b/src/main/kotlin/online/hudacek/fxradio/api/http/providers/DefaultClientProvider.kt @@ -49,6 +49,10 @@ open class DefaultClientProvider : HttpClientProvider { //Set logging level for HTTP requests to full request and response //Http requests are logged only on trace app logger level level = HttpLoggingInterceptor.Level.BODY + + //Do not show sensitive information + redactHeader("Authorization") + redactHeader("Cookie") } override val interceptors: MutableList = mutableListOf(loggerInterceptor) @@ -73,7 +77,7 @@ open class DefaultClientProvider : HttpClientProvider { * Correctly kill all OkHttpClient threads */ override fun close() { - logger.info { "Shutting down OkHttpClient $client" } + logger.info { "Closing OkHttpClient..." } logger.debug { "Idle/All: ${connectionPool.idleConnectionCount()}/${connectionPool.connectionCount()}" } super.close() } diff --git a/src/main/kotlin/online/hudacek/fxradio/media/MediaPlayerFactory.kt b/src/main/kotlin/online/hudacek/fxradio/media/MediaPlayerFactory.kt index 7bcfdcb4..a6cfcc7d 100644 --- a/src/main/kotlin/online/hudacek/fxradio/media/MediaPlayerFactory.kt +++ b/src/main/kotlin/online/hudacek/fxradio/media/MediaPlayerFactory.kt @@ -28,7 +28,7 @@ object MediaPlayerFactory { * Create MediaPlayer from String [playerType] identifier */ fun create(playerType: String): MediaPlayer { - logger.debug { "MediaPlayer $playerType initializing" } + logger.debug { "MediaPlayer $playerType is initializing..." } return when (playerType.asPlayerType()) { MediaPlayer.Type.VLC -> tryLoadVLCPlayer() MediaPlayer.Type.Humble -> HumblePlayerImpl() @@ -39,7 +39,7 @@ object MediaPlayerFactory { * Create opposite playerType than provided in [playerType] var */ fun toggle(playerType: MediaPlayer.Type): MediaPlayer { - logger.debug { "MediaPlayer $playerType toggling" } + logger.debug { "MediaPlayer $playerType is toggling..." } return when (playerType) { MediaPlayer.Type.Humble -> tryLoadVLCPlayer() MediaPlayer.Type.VLC -> HumblePlayerImpl() @@ -50,12 +50,11 @@ object MediaPlayerFactory { * Tries to load VLCPlayer. If it is not installed on the system, * it loads the Humble player instead. */ - private fun tryLoadVLCPlayer(): MediaPlayer = try { + private fun tryLoadVLCPlayer(): MediaPlayer = runCatching { VLCPlayerImpl() - } catch (e: Exception) { - logger.error(e) { "VLC can't be initialized." } - HumblePlayerImpl() - } + }.onFailure { + logger.error(it) { "Exception when initializing VLC Player!" } + }.getOrDefault(HumblePlayerImpl()) /** * Helper for loading of playerType from app.properties file @@ -63,6 +62,6 @@ object MediaPlayerFactory { private fun String.asPlayerType() = runCatching { MediaPlayer.Type.valueOf(this) }.onFailure { - logger.error(it) { "This playerType is invalid. Using Humble MediaPlayer as fallback" } + logger.error(it) { "This playerType is invalid. Using Humble MediaPlayer as fallback!" } }.getOrDefault(MediaPlayer.Type.Humble) } \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/media/StreamUnavailableException.kt b/src/main/kotlin/online/hudacek/fxradio/media/StreamUnavailableException.kt index a4098a97..73c5c2c1 100644 --- a/src/main/kotlin/online/hudacek/fxradio/media/StreamUnavailableException.kt +++ b/src/main/kotlin/online/hudacek/fxradio/media/StreamUnavailableException.kt @@ -33,7 +33,7 @@ class StreamUnavailableException(message: String, cause: Throwable?) : Exception init { Platform.runLater { playerViewModel.stateProperty.value = PlayerState.Error(localizedMessage) - logger.error(this) { "Stream can't be played" } + logger.error(this) { "Exception when playing stream!" } } } } \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/media/player/humble/HumbleAudioComponent.kt b/src/main/kotlin/online/hudacek/fxradio/media/player/humble/HumbleAudioComponent.kt index 79ddf747..82d05a22 100644 --- a/src/main/kotlin/online/hudacek/fxradio/media/player/humble/HumbleAudioComponent.kt +++ b/src/main/kotlin/online/hudacek/fxradio/media/player/humble/HumbleAudioComponent.kt @@ -106,7 +106,7 @@ class HumbleAudioComponent { fun cancel() { job?.let { if (it.isActive) { - logger.debug { "Cancelling playback" } + logger.debug { "Cancelling current playback..." } it.cancel() } } diff --git a/src/main/kotlin/online/hudacek/fxradio/media/player/humble/HumbleMetaService.kt b/src/main/kotlin/online/hudacek/fxradio/media/player/humble/HumbleMetaService.kt index 29cf0b8f..390ae1cf 100644 --- a/src/main/kotlin/online/hudacek/fxradio/media/player/humble/HumbleMetaService.kt +++ b/src/main/kotlin/online/hudacek/fxradio/media/player/humble/HumbleMetaService.kt @@ -63,7 +63,7 @@ class HumbleMetaDataService(private var streamUrl: String = "") : ScheduledServi true, null, null) val data = deMuxer.metaData deMuxer.close() - logger.debug { "FetchDataTask: $data" } + logger.debug { "FetchDataTask retrieved MetaData: $data" } return data } diff --git a/src/main/kotlin/online/hudacek/fxradio/media/player/vlc/VLCAudioComponent.kt b/src/main/kotlin/online/hudacek/fxradio/media/player/vlc/VLCAudioComponent.kt index 7f5be522..584af28a 100644 --- a/src/main/kotlin/online/hudacek/fxradio/media/player/vlc/VLCAudioComponent.kt +++ b/src/main/kotlin/online/hudacek/fxradio/media/player/vlc/VLCAudioComponent.kt @@ -75,11 +75,11 @@ class VLCAudioComponent { it.mediaPlayer().controls().stop() } } - }.onFailure { logger.debug { "Can't stop stream" } } + }.onFailure { logger.debug { "Can't stop stream..." } } } fun release() { - logger.info { "Releasing player" } + logger.info { "Releasing VLC player..." } player?.let { if (MediaPlayer.enableMetaDataService) { it.mediaPlayer().events().removeMediaEventListener(vlcEvents.mediaMetaDataAdapter) diff --git a/src/main/kotlin/online/hudacek/fxradio/media/player/vlc/VLCEvents.kt b/src/main/kotlin/online/hudacek/fxradio/media/player/vlc/VLCEvents.kt index 6c8130e5..2f429219 100644 --- a/src/main/kotlin/online/hudacek/fxradio/media/player/vlc/VLCEvents.kt +++ b/src/main/kotlin/online/hudacek/fxradio/media/player/vlc/VLCEvents.kt @@ -43,7 +43,7 @@ class VLCEvents : Controller() { */ val nativeLogListener = LogEventListener { level, module, _, _, name, _, _, message -> lastLogMessage = message - logger.debug { "[$module] ($name) $level: $message" } + logger.info { "[$module] ($name) $level: $message" } } /** diff --git a/src/main/kotlin/online/hudacek/fxradio/storage/ImageCache.kt b/src/main/kotlin/online/hudacek/fxradio/storage/ImageCache.kt index d930a3e5..5617ebc1 100644 --- a/src/main/kotlin/online/hudacek/fxradio/storage/ImageCache.kt +++ b/src/main/kotlin/online/hudacek/fxradio/storage/ImageCache.kt @@ -79,7 +79,7 @@ object ImageCache { .flatMap { Single.just(Image("file:" + it.toFile().absolutePath, true)) } - .doOnError { logger.error(it) { "Failed to get Image from cached file" } } + .doOnError { logger.error(it) { "Exception when getting station image from file!" } } .onErrorReturnItem(defaultRadioLogo) /** @@ -87,7 +87,7 @@ object ImageCache { */ fun save(station: Station, inputStream: InputStream): Disposable = Single.just(station) .filter { !it.isCached } - .doOnError { logger.error(it) { "Cannot save downloaded image" } } + .doOnError { logger.error(it) { "Exception when saving downloaded image!" } } .map { cacheBasePath.resolve(it.stationuuid) } .subscribe { Files.copy( @@ -102,7 +102,7 @@ object ImageCache { */ fun addInvalid(station: Station) { if (station.stationuuid !in invalidStationUuids) { - logger.trace { "Image for ${station.name} is added as invalid" } + logger.trace { "Image for ${station.name} is added as invalid!" } invalidStationUuids += station.stationuuid } } diff --git a/src/main/kotlin/online/hudacek/fxradio/storage/db/Database.kt b/src/main/kotlin/online/hudacek/fxradio/storage/db/Database.kt index b4832a47..89c251e8 100644 --- a/src/main/kotlin/online/hudacek/fxradio/storage/db/Database.kt +++ b/src/main/kotlin/online/hudacek/fxradio/storage/db/Database.kt @@ -56,9 +56,9 @@ abstract class Database(open val tableName: String) { execute(member.createTableSql) .toSingle() .subscribe({ - logger.info { "Create table for ${member.tableName} returned result $it" } + logger.info { "Create Table ${member.tableName} returned result: $it" } }, { e -> - logger.error(e) { "There was an error creating ${member.tableName} table!" } + logger.error(e) { "Exception when creating ${member.tableName} table!" } }) } } diff --git a/src/main/kotlin/online/hudacek/fxradio/storage/db/PinnedCountriesTable.kt b/src/main/kotlin/online/hudacek/fxradio/storage/db/PinnedCountriesTable.kt index f6a3a7f6..04ebd4bc 100644 --- a/src/main/kotlin/online/hudacek/fxradio/storage/db/PinnedCountriesTable.kt +++ b/src/main/kotlin/online/hudacek/fxradio/storage/db/PinnedCountriesTable.kt @@ -35,7 +35,7 @@ class PinnedCountriesTable(override val tableName: String = "PINNED") : Table = removeAllQuery().toSingle() diff --git a/src/main/kotlin/online/hudacek/fxradio/storage/db/StationTable.kt b/src/main/kotlin/online/hudacek/fxradio/storage/db/StationTable.kt index fe1b240d..d976a6cb 100644 --- a/src/main/kotlin/online/hudacek/fxradio/storage/db/StationTable.kt +++ b/src/main/kotlin/online/hudacek/fxradio/storage/db/StationTable.kt @@ -50,7 +50,7 @@ class StationTable(override val tableName: String) : Table, Database(ta it.getString("codec"), it.getInt("bitrate")) } - .doOnError { logger.error(it) { "Error when getting $tableName" } } + .doOnError { logger.error(it) { "Exception when retrieving data from $tableName" } } override fun removeAll(): Single = removeAllQuery().toSingle() diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/ImageExtensions.kt b/src/main/kotlin/online/hudacek/fxradio/ui/ImageExtensions.kt index 461e6ca5..1697ec08 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/ImageExtensions.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/ImageExtensions.kt @@ -88,6 +88,6 @@ private fun Station.loadImage(view: ImageView) { } } }, { - logger.error(it) { "Failed loading image" } + logger.error(it) { "Exception when loading image from cache!" } }) } \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/menu/MenuExtensions.kt b/src/main/kotlin/online/hudacek/fxradio/ui/menu/MenuExtensions.kt index 9a27839b..9c0c4ccf 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/menu/MenuExtensions.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/menu/MenuExtensions.kt @@ -17,7 +17,10 @@ package online.hudacek.fxradio.ui.menu import javafx.beans.property.Property -import javafx.scene.control.* +import javafx.scene.control.CheckMenuItem +import javafx.scene.control.Menu +import javafx.scene.control.MenuItem +import javafx.scene.control.SeparatorMenuItem import javafx.scene.input.KeyCodeCombination import online.hudacek.fxradio.api.stations.model.Station import tornadofx.bind @@ -31,12 +34,6 @@ internal fun menu(name: String, op: Menu.() -> Unit = {}) = Menu(name).apply { op(this) } - -internal fun contextMenu(op: ContextMenu.() -> Unit = {}) = ContextMenu().apply { - op(this) -} - - internal fun item(name: String, keyCode: KeyCodeCombination? = null, op: MenuItem.() -> Unit = {}) = MenuItem(name).apply { if (keyCode != null) { diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/modal/Modals.kt b/src/main/kotlin/online/hudacek/fxradio/ui/modal/Modals.kt index 0ec1c090..e4c66c11 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/modal/Modals.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/modal/Modals.kt @@ -19,6 +19,7 @@ package online.hudacek.fxradio.ui.modal import javafx.stage.StageStyle import tornadofx.Fragment import tornadofx.find +import kotlin.reflect.full.createInstance /** * Generic modal dialog helper @@ -43,3 +44,6 @@ internal inline fun Modals.open() = find().openModa stageStyle = style, resizable = resizable) +internal inline fun Modals.new() = T::class.createInstance().openModal( + stageStyle = style, + resizable = resizable) diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/modal/StationInfoFragment.kt b/src/main/kotlin/online/hudacek/fxradio/ui/modal/StationInfoFragment.kt index 91ccb448..23a5afab 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/modal/StationInfoFragment.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/modal/StationInfoFragment.kt @@ -23,7 +23,6 @@ import javafx.geometry.Pos import javafx.scene.control.Label import javafx.scene.effect.DropShadow import javafx.scene.paint.Color -import online.hudacek.fxradio.api.stations.model.Station import online.hudacek.fxradio.ui.* import online.hudacek.fxradio.ui.style.Styles import online.hudacek.fxradio.viewmodel.PlayerViewModel @@ -34,13 +33,13 @@ import tornadofx.controlsfx.left import tornadofx.controlsfx.right import tornadofx.controlsfx.statusbar -class StationInfoFragment(station: Station? = null) : BaseFragment() { +class StationInfoFragment : BaseFragment() { private val viewModel: StationInfoViewModel by inject() private val playerViewModel: PlayerViewModel by inject() init { - viewModel.item = StationInfo(station ?: playerViewModel.stationProperty.value) + viewModel.item = StationInfo(playerViewModel.stationProperty.value) } override val root = vbox { diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/view/MenuBarView.kt b/src/main/kotlin/online/hudacek/fxradio/ui/view/MenuBarView.kt index 361e0dba..4a285bf8 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/view/MenuBarView.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/view/MenuBarView.kt @@ -18,7 +18,8 @@ package online.hudacek.fxradio.ui.view import online.hudacek.fxradio.ui.BaseView import online.hudacek.fxradio.ui.menu.* -import online.hudacek.fxradio.utils.macos.MacMenu +import online.hudacek.fxradio.utils.macos.NSMenu +import online.hudacek.fxradio.utils.macos.NSMenuBar import online.hudacek.fxradio.viewmodel.AppMenuViewModel import tornadofx.get import tornadofx.menubar @@ -54,16 +55,15 @@ class MenuBarView : BaseView() { * Platform specific menu bar working on OSX * used instead of in-app menubar */ - private fun platformMenuBar() = MacMenu.menuBar { - MacMenu.appMenu { - items.addAll(aboutMenu.aboutMainItems) - } - }.apply { - menus.addAll(stationMenu.menu, - playerMenu.menu, - favouritesMenu.menu, - historyMenu.menu, - MacMenu.windowMenu(messages["macos.menu.window"]), - helpMenu.menu) - } + private fun platformMenuBar() = NSMenuBar() + .menuBar.apply { + NSMenu().appMenu(aboutMenu.aboutMainItems) + menus.addAll( + stationMenu.menu, + playerMenu.menu, + favouritesMenu.menu, + historyMenu.menu, + NSMenu().windowMenu(messages["macos.menu.window"]), + helpMenu.menu) + } } \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsDataGridView.kt b/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsDataGridView.kt index 3920c070..044483a0 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsDataGridView.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsDataGridView.kt @@ -22,13 +22,12 @@ import javafx.geometry.Pos import javafx.scene.CacheHint import javafx.scene.effect.DropShadow import javafx.scene.paint.Color -import javafx.stage.StageStyle import online.hudacek.fxradio.Config import online.hudacek.fxradio.api.stations.model.tagsSplit import online.hudacek.fxradio.ui.BaseView import online.hudacek.fxradio.ui.menu.FavouritesMenu import online.hudacek.fxradio.ui.modal.Modals -import online.hudacek.fxradio.ui.modal.StationInfoFragment +import online.hudacek.fxradio.ui.modal.new import online.hudacek.fxradio.ui.modal.open import online.hudacek.fxradio.ui.showWhen import online.hudacek.fxradio.ui.smallLabel @@ -68,7 +67,7 @@ class StationsDataGridView : BaseView() { vbox { contextmenu { item(messages["menu.station.info"]).action { - StationInfoFragment(station).openModal(stageStyle = StageStyle.UTILITY) + Modals.StationInfo.new() } separator() diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/AddStationUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/AddStationUseCase.kt index 29f6fae9..67a44874 100644 --- a/src/main/kotlin/online/hudacek/fxradio/usecase/AddStationUseCase.kt +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/AddStationUseCase.kt @@ -32,5 +32,5 @@ class AddStationUseCase : BaseUseCase>() { .addStation(input) .compose(applySchedulers()) .onErrorResumeNext { Single.just(AddedStation(false, it.localizedMessage, "0")) } - .doOnError { logger.error { "Error while adding station: ${it.message} " } } + .doOnError { logger.error { "Error while adding station: ${it.message}" } } } \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/utils/macos/MacUtils.kt b/src/main/kotlin/online/hudacek/fxradio/utils/macos/MacUtils.kt index 6b659c9c..ec2afd99 100644 --- a/src/main/kotlin/online/hudacek/fxradio/utils/macos/MacUtils.kt +++ b/src/main/kotlin/online/hudacek/fxradio/utils/macos/MacUtils.kt @@ -31,8 +31,7 @@ object MacUtils { NSUserNotification().apply { this.title = title this.informativeText = subtitle - show() - } + }.show() val isSystemDarkMode: Boolean get() = Command("defaults read -g AppleInterfaceStyle").exec() == "Dark" diff --git a/src/main/kotlin/online/hudacek/fxradio/utils/macos/MacMenu.kt b/src/main/kotlin/online/hudacek/fxradio/utils/macos/NSMenu.kt similarity index 73% rename from src/main/kotlin/online/hudacek/fxradio/utils/macos/MacMenu.kt rename to src/main/kotlin/online/hudacek/fxradio/utils/macos/NSMenu.kt index e0e03466..e492984c 100644 --- a/src/main/kotlin/online/hudacek/fxradio/utils/macos/MacMenu.kt +++ b/src/main/kotlin/online/hudacek/fxradio/utils/macos/NSMenu.kt @@ -17,34 +17,27 @@ package online.hudacek.fxradio.utils.macos import de.codecentric.centerdevice.MenuToolkit -import javafx.scene.control.Menu -import javafx.scene.control.MenuBar +import javafx.scene.control.MenuItem import javafx.scene.control.SeparatorMenuItem import online.hudacek.fxradio.FxRadio +import online.hudacek.fxradio.ui.menu.menu import online.hudacek.fxradio.ui.menu.separator import tornadofx.FX /** - * NSMenu helpers + * NSMenu helper for MacOS only */ -object MacMenu { +open class NSMenu { - var isInTest = false + /** + * NSMenu toolkit initialization + */ + protected val menuToolkit: MenuToolkit by lazy { MenuToolkit.toolkit(FX.locale) } - //NSMenu toolkit - private val menuToolkit by lazy { MenuToolkit.toolkit(FX.locale) } - - fun menuBar(op: MenuBar.() -> Menu) = MenuBar().apply { - if (!isInTest) { - useSystemMenuBarProperty().value = true - menuToolkit.setApplicationMenu(op(this)) - menuToolkit.setMenuBar(this) - } - } - - fun appMenu(op: Menu.() -> Unit = {}) = Menu(FxRadio.appName).apply { + fun appMenu(menuItems: List) = menu(FxRadio.appName) { if (!isInTest) { - op(this) + menuToolkit.setApplicationMenu(this) + items.addAll(menuItems) items.addAll( separator(), menuToolkit.createHideMenuItem(FxRadio.appName), @@ -55,7 +48,7 @@ object MacMenu { } } - fun windowMenu(name: String) = Menu(name).apply { + fun windowMenu(name: String) = menu(name) { if (!isInTest) { menuToolkit.autoAddWindowMenuItems(this) items.addAll( @@ -66,4 +59,9 @@ object MacMenu { menuToolkit.createBringAllToFrontItem()) } } -} \ No newline at end of file + + companion object { + var isInTest: Boolean = false + } +} + diff --git a/src/main/kotlin/online/hudacek/fxradio/utils/macos/NSMenuBar.kt b/src/main/kotlin/online/hudacek/fxradio/utils/macos/NSMenuBar.kt new file mode 100644 index 00000000..215d099a --- /dev/null +++ b/src/main/kotlin/online/hudacek/fxradio/utils/macos/NSMenuBar.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2020 FXRadio by hudacek.online + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package online.hudacek.fxradio.utils.macos + +import javafx.scene.control.MenuBar + +/** + * NSMenu helper for MacOS only + */ +class NSMenuBar : NSMenu() { + + val menuBar = MenuBar().apply { + if (!isInTest) { + useSystemMenuBarProperty().value = true + menuToolkit.setMenuBar(this) + } + } + + companion object { + var isInTest: Boolean = false + } +} + diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/FavouritesViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/FavouritesViewModel.kt index bca27de3..1f56ec0b 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/FavouritesViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/FavouritesViewModel.kt @@ -74,6 +74,6 @@ class FavouritesViewModel : BaseViewModel(Favourites()) { .subscribe({ item = Favourites() }, { - logger.error(it) { "Cannot perform DB cleanup!" } + logger.error(it) { "Exception when performing DB cleanup!" } }) } \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/HistoryViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/HistoryViewModel.kt index 2e40193e..7508046d 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/HistoryViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/HistoryViewModel.kt @@ -49,7 +49,7 @@ class HistoryViewModel : BaseViewModel(History()) { appEvent.addToHistory //Add only valid stations not already present in history .filter { it.isValid() } - .doOnError { logger.error(it) { "Error adding station to history!" } } + .doOnError { logger.error(it) { "Exception when adding station to history!" } } .flatMapSingle { Tables.history.insert(it) } .subscribe { stationsProperty += it @@ -59,7 +59,7 @@ class HistoryViewModel : BaseViewModel(History()) { fun cleanupHistory() = Tables.history .removeAll() .toObservable() - .doOnError { logger.error(it) { "Cannot perform DB cleanup!" } } + .doOnError { logger.error(it) { "Exception when performing DB cleanup!" } } .doOnEach { item = History() } .map { LibraryState.History } .subscribe(appEvent.refreshLibrary) diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/LibraryViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/LibraryViewModel.kt index e84848e2..4875ce7a 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/LibraryViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/LibraryViewModel.kt @@ -92,14 +92,14 @@ class LibraryViewModel : BaseStateViewModel(Library(), Li .subscribe({ pinnedProperty.add(it) }, { - logger.error(it) { "Pinning error" } + logger.error(it) { "Exception when performing Pinning!" } }) fun unpinCountry(country: Country): Disposable = unPinCountryUseCase.execute(country) .subscribe({ pinnedProperty.remove(it) }, { - logger.error(it) { "Unpinning error" } + logger.error(it) { "Exception when performing Unpinning!" } }) fun getCountries() = getCountriesUseCase.execute(countriesProperty) diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml index 3e88b0c3..f091d5d9 100644 --- a/src/main/resources/log4j2.xml +++ b/src/main/resources/log4j2.xml @@ -1,14 +1,23 @@ + + app.log + ${sys:user.home}/.fxradio/${logname} + - + %d %p %c{1.} [%t] %m%n - + + + + + diff --git a/src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt b/src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt index 555b3f33..ae7cbd2a 100644 --- a/src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt +++ b/src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt @@ -26,7 +26,7 @@ import online.hudacek.fxradio.api.stations.StationsApi import online.hudacek.fxradio.api.stations.model.SearchBody import online.hudacek.fxradio.api.stations.model.Station import online.hudacek.fxradio.storage.db.Tables -import online.hudacek.fxradio.utils.macos.MacMenu +import online.hudacek.fxradio.utils.macos.NSMenu import online.hudacek.fxradio.viewmodel.LibraryItem import online.hudacek.fxradio.viewmodel.PlayerState import online.hudacek.fxradio.viewmodel.PlayerViewModel @@ -73,7 +73,7 @@ class BasicFunctionalityTests { @Init fun init() { - MacMenu.isInTest = true + NSMenu.isInTest = true } @Start diff --git a/src/test/kotlin/online/hudacek/fxradio/integration/DarkModeTests.kt b/src/test/kotlin/online/hudacek/fxradio/integration/DarkModeTests.kt index 336c5efd..abc30d02 100644 --- a/src/test/kotlin/online/hudacek/fxradio/integration/DarkModeTests.kt +++ b/src/test/kotlin/online/hudacek/fxradio/integration/DarkModeTests.kt @@ -19,7 +19,7 @@ package online.hudacek.fxradio.integration import javafx.stage.Stage import online.hudacek.fxradio.FxRadio import online.hudacek.fxradio.FxRadioDark -import online.hudacek.fxradio.utils.macos.MacMenu +import online.hudacek.fxradio.utils.macos.NSMenu import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.testfx.api.FxAssert @@ -39,7 +39,7 @@ class DarkModeTests { @Init fun init() { - MacMenu.isInTest = true + NSMenu.isInTest = true } @Start From 12b2f80122344c4d2e086a3931785bd39b6850ce Mon Sep 17 00:00:00 2001 From: jozef Date: Fri, 21 May 2021 22:17:59 +0200 Subject: [PATCH 03/27] Added experimental SystemTray, fixed log rotation intervals, unifying package names --- .../kotlin/online/hudacek/fxradio/Config.kt | 1 + .../kotlin/online/hudacek/fxradio/FxRadio.kt | 46 ++++++++-------- .../hudacek/fxradio/api/ApiServiceProvider.kt | 4 +- .../hudacek/fxradio/api/http/HttpClient.kt | 2 +- .../TokenInterceptor.kt | 2 +- .../UserAgentInterceptor.kt | 2 +- .../DefaultClientProvider.kt | 4 +- .../HttpClientProvider.kt | 2 +- .../TokenClientProvider.kt | 4 +- .../fxradio/api/stations/StationsApi.kt | 4 +- .../fxradio/{events => event}/AppEvent.kt | 4 +- .../{events => event}/data/AppNotification.kt | 2 +- .../hudacek/fxradio/media/MediaPlayer.kt | 4 +- .../media/player/humble/HumbleMetaService.kt | 2 +- .../fxradio/media/player/vlc/VLCEvents.kt | 2 +- .../online/hudacek/fxradio/ui/BaseFragment.kt | 2 +- .../online/hudacek/fxradio/ui/BaseView.kt | 2 +- .../online/hudacek/fxradio/ui/Extensions.kt | 2 +- .../ui/{modal => fragment}/AboutFragment.kt | 2 +- .../{modal => fragment}/AddStationFragment.kt | 2 +- .../AttributionsFragment.kt | 2 +- .../fxradio/ui/{modal => fragment}/Modals.kt | 2 +- .../ui/{modal => fragment}/ServersFragment.kt | 4 +- .../StationDebugFragment.kt | 2 +- .../StationInfoFragment.kt | 2 +- .../ui/{modal => fragment}/StatsFragment.kt | 2 +- .../hudacek/fxradio/ui/menu/AboutMenu.kt | 8 ++- .../hudacek/fxradio/ui/menu/BaseMenu.kt | 2 +- .../hudacek/fxradio/ui/menu/HelpMenu.kt | 4 +- .../hudacek/fxradio/ui/menu/PlayerMenu.kt | 2 +- .../hudacek/fxradio/ui/menu/StationMenu.kt | 4 +- .../online/hudacek/fxradio/ui/style/Colors.kt | 2 +- .../hudacek/fxradio/ui/view/MainView.kt | 9 +--- .../hudacek/fxradio/ui/view/MenuBarView.kt | 4 +- .../fxradio/ui/view/player/TickerView.kt | 2 +- .../ui/view/stations/StationsDataGridView.kt | 6 +-- .../hudacek/fxradio/usecase/BaseUseCase.kt | 2 +- .../fxradio/usecase/ClearCacheUseCase.kt | 4 +- ...CountryUseCase.kt => CountryPinUseCase.kt} | 2 +- ...untryUseCase.kt => CountryUnpinUseCase.kt} | 2 +- .../fxradio/usecase/FavouriteAddUseCase.kt | 26 ++++++++++ .../fxradio/usecase/FavouriteRemoveUseCase.kt | 26 ++++++++++ .../fxradio/usecase/FavouriteSetUseCase.kt | 30 +++++++++++ .../fxradio/usecase/FavouritesClearUseCase.kt | 38 ++++++++++++++ .../fxradio/usecase/GetServersUseCase.kt | 1 - .../fxradio/{utils => util}/Command.kt | 2 +- .../fxradio/{utils => util}/Properties.kt | 2 +- .../online/hudacek/fxradio/util/Tray.kt | 52 +++++++++++++++++++ .../fxradio/{utils => util}/macos/MacUtils.kt | 4 +- .../fxradio/{utils => util}/macos/NSMenu.kt | 2 +- .../{utils => util}/macos/NSMenuBar.kt | 2 +- .../fxradio/viewmodel/AddStationViewModel.kt | 2 +- .../fxradio/viewmodel/AppMenuViewModel.kt | 6 +-- .../hudacek/fxradio/viewmodel/Attributions.kt | 2 +- .../fxradio/viewmodel/BaseViewModel.kt | 2 +- .../fxradio/viewmodel/FavouritesViewModel.kt | 35 ++++++------- .../fxradio/viewmodel/LibraryViewModel.kt | 20 +++---- .../hudacek/fxradio/viewmodel/LogViewModel.kt | 6 +-- .../viewmodel/OsNotificationViewModel.kt | 8 +-- .../fxradio/viewmodel/PlayerViewModel.kt | 8 +-- .../fxradio/viewmodel/SearchViewModel.kt | 6 +-- .../fxradio/viewmodel/ServersViewModel.kt | 6 +-- .../fxradio/viewmodel/StationsViewModel.kt | 2 +- .../fxradio/viewmodel/StatsViewModel.kt | 2 +- src/main/resources/log4j2.xml | 1 - .../integration/BasicFunctionalityTests.kt | 2 +- .../fxradio/integration/DarkModeTests.kt | 2 +- 67 files changed, 306 insertions(+), 151 deletions(-) rename src/main/kotlin/online/hudacek/fxradio/api/http/{interceptors => interceptor}/TokenInterceptor.kt (95%) rename src/main/kotlin/online/hudacek/fxradio/api/http/{interceptors => interceptor}/UserAgentInterceptor.kt (95%) rename src/main/kotlin/online/hudacek/fxradio/api/http/{providers => provider}/DefaultClientProvider.kt (95%) rename src/main/kotlin/online/hudacek/fxradio/api/http/{providers => provider}/HttpClientProvider.kt (95%) rename src/main/kotlin/online/hudacek/fxradio/api/http/{providers => provider}/TokenClientProvider.kt (88%) rename src/main/kotlin/online/hudacek/fxradio/{events => event}/AppEvent.kt (93%) rename src/main/kotlin/online/hudacek/fxradio/{events => event}/data/AppNotification.kt (94%) rename src/main/kotlin/online/hudacek/fxradio/ui/{modal => fragment}/AboutFragment.kt (98%) rename src/main/kotlin/online/hudacek/fxradio/ui/{modal => fragment}/AddStationFragment.kt (99%) rename src/main/kotlin/online/hudacek/fxradio/ui/{modal => fragment}/AttributionsFragment.kt (98%) rename src/main/kotlin/online/hudacek/fxradio/ui/{modal => fragment}/Modals.kt (97%) rename src/main/kotlin/online/hudacek/fxradio/ui/{modal => fragment}/ServersFragment.kt (98%) rename src/main/kotlin/online/hudacek/fxradio/ui/{modal => fragment}/StationDebugFragment.kt (96%) rename src/main/kotlin/online/hudacek/fxradio/ui/{modal => fragment}/StationInfoFragment.kt (99%) rename src/main/kotlin/online/hudacek/fxradio/ui/{modal => fragment}/StatsFragment.kt (98%) rename src/main/kotlin/online/hudacek/fxradio/usecase/{PinCountryUseCase.kt => CountryPinUseCase.kt} (93%) rename src/main/kotlin/online/hudacek/fxradio/usecase/{UnPinCountryUseCase.kt => CountryUnpinUseCase.kt} (93%) create mode 100644 src/main/kotlin/online/hudacek/fxradio/usecase/FavouriteAddUseCase.kt create mode 100644 src/main/kotlin/online/hudacek/fxradio/usecase/FavouriteRemoveUseCase.kt create mode 100644 src/main/kotlin/online/hudacek/fxradio/usecase/FavouriteSetUseCase.kt create mode 100644 src/main/kotlin/online/hudacek/fxradio/usecase/FavouritesClearUseCase.kt rename src/main/kotlin/online/hudacek/fxradio/{utils => util}/Command.kt (96%) rename src/main/kotlin/online/hudacek/fxradio/{utils => util}/Properties.kt (99%) create mode 100644 src/main/kotlin/online/hudacek/fxradio/util/Tray.kt rename src/main/kotlin/online/hudacek/fxradio/{utils => util}/macos/MacUtils.kt (93%) rename src/main/kotlin/online/hudacek/fxradio/{utils => util}/macos/NSMenu.kt (98%) rename src/main/kotlin/online/hudacek/fxradio/{utils => util}/macos/NSMenuBar.kt (95%) diff --git a/src/main/kotlin/online/hudacek/fxradio/Config.kt b/src/main/kotlin/online/hudacek/fxradio/Config.kt index 1b280a05..49fe7d9f 100644 --- a/src/main/kotlin/online/hudacek/fxradio/Config.kt +++ b/src/main/kotlin/online/hudacek/fxradio/Config.kt @@ -51,6 +51,7 @@ object Config { */ object Flags { const val darkStylesEnabled = false + const val useTrayIcon = false const val enableStationDebug = true } } \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/FxRadio.kt b/src/main/kotlin/online/hudacek/fxradio/FxRadio.kt index 46b62624..1fdcb3ac 100644 --- a/src/main/kotlin/online/hudacek/fxradio/FxRadio.kt +++ b/src/main/kotlin/online/hudacek/fxradio/FxRadio.kt @@ -23,9 +23,10 @@ import online.hudacek.fxradio.ui.CustomErrorHandler import online.hudacek.fxradio.ui.style.Styles import online.hudacek.fxradio.ui.style.StylesDark import online.hudacek.fxradio.ui.view.MainView -import online.hudacek.fxradio.utils.Properties -import online.hudacek.fxradio.utils.macos.MacUtils -import online.hudacek.fxradio.utils.saveProperties +import online.hudacek.fxradio.util.Properties +import online.hudacek.fxradio.util.Tray +import online.hudacek.fxradio.util.macos.MacUtils +import online.hudacek.fxradio.util.saveProperties import online.hudacek.fxradio.viewmodel.PlayerViewModel import tornadofx.* import java.nio.file.Path @@ -35,25 +36,26 @@ import kotlin.reflect.KClass /** * Load app in Dark Mode */ -class FxRadioDark(override var useDarkModeStyle: Boolean = true) : FxRadio(StylesDark::class) +class FxRadioDark : FxRadio(darkModeEnabled = true, stylesheet = StylesDark::class) /** * Load app in Light Mode */ -class FxRadioLight(override var useDarkModeStyle: Boolean = false) : FxRadio(Styles::class) +class FxRadioLight : FxRadio(stylesheet = Styles::class) /** * Load the app with provided [stylesheet] class */ -open class FxRadio(stylesheet: KClass) : App(MainView::class, stylesheet) { +open class FxRadio(val darkModeEnabled: Boolean = false, + stylesheet: KClass) : App(MainView::class, stylesheet) { + + private val playerViewModel: PlayerViewModel by inject() /** * override app.config path to $user.home/fxradio */ override val configBasePath: Path = Paths.get(Config.Paths.confDirPath) - open var useDarkModeStyle: Boolean by singleAssign() - override fun start(stage: Stage) { Thread.setDefaultUncaughtExceptionHandler(CustomErrorHandler()) with(stage) { @@ -75,9 +77,14 @@ open class FxRadio(stylesheet: KClass) : App(MainView::class, st } super.start(this) } + Tray().addIcon() } override fun stop() { + playerViewModel.releasePlayer() + StationsApi.serviceProvider.close() + HttpClient.close() + //Save last used window width/height on close of the app to use it on next start saveProperties(mapOf( Properties.WindowWidth to primaryStage.width, @@ -93,15 +100,13 @@ open class FxRadio(stylesheet: KClass) : App(MainView::class, st */ companion object : Component() { - private val playerViewModel: PlayerViewModel by inject() - const val appName = "FXRadio" const val appDesc = "Internet radio directory" const val appUrl = "https://hudacek.online/fxradio/" const val author = "hudacek.online" const val copyright = "Copyright (c) 2020" - val isAppInDarkMode by lazy { (app as FxRadio).useDarkModeStyle } + val darkModeEnabled by lazy { (app as FxRadio).darkModeEnabled } /** * Gets version from jar MANIFEST.MF file @@ -110,26 +115,19 @@ open class FxRadio(stylesheet: KClass) : App(MainView::class, st val version: String by lazy { FxRadio::class.java.getPackage().implementationVersion ?: "0.0-DEVELOPMENT" } - - //Should be called when on every place that is closing the app - fun shutdownApp() { - playerViewModel.releasePlayer() - StationsApi.serviceProvider.close() - HttpClient.close() - } } } -private val isSystemDarkMode: Boolean = if (MacUtils.isMac) { - MacUtils.isSystemDarkMode -} else { - false -} - /** * Main starting method for the App */ fun main(args: Array) { + val isSystemDarkMode: Boolean = if (MacUtils.isMac) { + MacUtils.isSystemDarkMode + } else { + false + } + if (Config.Flags.darkStylesEnabled && isSystemDarkMode) { launch(args) } else { diff --git a/src/main/kotlin/online/hudacek/fxradio/api/ApiServiceProvider.kt b/src/main/kotlin/online/hudacek/fxradio/api/ApiServiceProvider.kt index 41fdc0a9..6499207d 100644 --- a/src/main/kotlin/online/hudacek/fxradio/api/ApiServiceProvider.kt +++ b/src/main/kotlin/online/hudacek/fxradio/api/ApiServiceProvider.kt @@ -16,8 +16,8 @@ package online.hudacek.fxradio.api -import online.hudacek.fxradio.api.http.providers.DefaultClientProvider -import online.hudacek.fxradio.api.http.providers.HttpClientProvider +import online.hudacek.fxradio.api.http.provider.DefaultClientProvider +import online.hudacek.fxradio.api.http.provider.HttpClientProvider import retrofit2.Retrofit import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory import retrofit2.converter.gson.GsonConverterFactory diff --git a/src/main/kotlin/online/hudacek/fxradio/api/http/HttpClient.kt b/src/main/kotlin/online/hudacek/fxradio/api/http/HttpClient.kt index dc7b7d59..6ccb0097 100644 --- a/src/main/kotlin/online/hudacek/fxradio/api/http/HttpClient.kt +++ b/src/main/kotlin/online/hudacek/fxradio/api/http/HttpClient.kt @@ -21,7 +21,7 @@ import okhttp3.Call import okhttp3.Callback import okhttp3.Request import okhttp3.Response -import online.hudacek.fxradio.api.http.providers.DefaultClientProvider +import online.hudacek.fxradio.api.http.provider.DefaultClientProvider import java.io.IOException import java.net.InetAddress diff --git a/src/main/kotlin/online/hudacek/fxradio/api/http/interceptors/TokenInterceptor.kt b/src/main/kotlin/online/hudacek/fxradio/api/http/interceptor/TokenInterceptor.kt similarity index 95% rename from src/main/kotlin/online/hudacek/fxradio/api/http/interceptors/TokenInterceptor.kt rename to src/main/kotlin/online/hudacek/fxradio/api/http/interceptor/TokenInterceptor.kt index 08538aea..2451196a 100644 --- a/src/main/kotlin/online/hudacek/fxradio/api/http/interceptors/TokenInterceptor.kt +++ b/src/main/kotlin/online/hudacek/fxradio/api/http/interceptor/TokenInterceptor.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package online.hudacek.fxradio.api.http.interceptors +package online.hudacek.fxradio.api.http.interceptor import okhttp3.Interceptor import okhttp3.Response diff --git a/src/main/kotlin/online/hudacek/fxradio/api/http/interceptors/UserAgentInterceptor.kt b/src/main/kotlin/online/hudacek/fxradio/api/http/interceptor/UserAgentInterceptor.kt similarity index 95% rename from src/main/kotlin/online/hudacek/fxradio/api/http/interceptors/UserAgentInterceptor.kt rename to src/main/kotlin/online/hudacek/fxradio/api/http/interceptor/UserAgentInterceptor.kt index 487fe830..90ac8863 100644 --- a/src/main/kotlin/online/hudacek/fxradio/api/http/interceptors/UserAgentInterceptor.kt +++ b/src/main/kotlin/online/hudacek/fxradio/api/http/interceptor/UserAgentInterceptor.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package online.hudacek.fxradio.api.http.interceptors +package online.hudacek.fxradio.api.http.interceptor import okhttp3.Interceptor import okhttp3.Response diff --git a/src/main/kotlin/online/hudacek/fxradio/api/http/providers/DefaultClientProvider.kt b/src/main/kotlin/online/hudacek/fxradio/api/http/provider/DefaultClientProvider.kt similarity index 95% rename from src/main/kotlin/online/hudacek/fxradio/api/http/providers/DefaultClientProvider.kt rename to src/main/kotlin/online/hudacek/fxradio/api/http/provider/DefaultClientProvider.kt index 242c744b..c288a5e0 100644 --- a/src/main/kotlin/online/hudacek/fxradio/api/http/providers/DefaultClientProvider.kt +++ b/src/main/kotlin/online/hudacek/fxradio/api/http/provider/DefaultClientProvider.kt @@ -14,14 +14,14 @@ * limitations under the License. */ -package online.hudacek.fxradio.api.http.providers +package online.hudacek.fxradio.api.http.provider import mu.KotlinLogging import okhttp3.ConnectionPool import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor -import online.hudacek.fxradio.api.http.interceptors.UserAgentInterceptor +import online.hudacek.fxradio.api.http.interceptor.UserAgentInterceptor import java.util.concurrent.TimeUnit private val logger = KotlinLogging.logger {} diff --git a/src/main/kotlin/online/hudacek/fxradio/api/http/providers/HttpClientProvider.kt b/src/main/kotlin/online/hudacek/fxradio/api/http/provider/HttpClientProvider.kt similarity index 95% rename from src/main/kotlin/online/hudacek/fxradio/api/http/providers/HttpClientProvider.kt rename to src/main/kotlin/online/hudacek/fxradio/api/http/provider/HttpClientProvider.kt index 63dde1bd..f2c4c080 100644 --- a/src/main/kotlin/online/hudacek/fxradio/api/http/providers/HttpClientProvider.kt +++ b/src/main/kotlin/online/hudacek/fxradio/api/http/provider/HttpClientProvider.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package online.hudacek.fxradio.api.http.providers +package online.hudacek.fxradio.api.http.provider import okhttp3.Interceptor import okhttp3.OkHttpClient diff --git a/src/main/kotlin/online/hudacek/fxradio/api/http/providers/TokenClientProvider.kt b/src/main/kotlin/online/hudacek/fxradio/api/http/provider/TokenClientProvider.kt similarity index 88% rename from src/main/kotlin/online/hudacek/fxradio/api/http/providers/TokenClientProvider.kt rename to src/main/kotlin/online/hudacek/fxradio/api/http/provider/TokenClientProvider.kt index e3ac773c..528e1790 100644 --- a/src/main/kotlin/online/hudacek/fxradio/api/http/providers/TokenClientProvider.kt +++ b/src/main/kotlin/online/hudacek/fxradio/api/http/provider/TokenClientProvider.kt @@ -14,9 +14,9 @@ * limitations under the License. */ -package online.hudacek.fxradio.api.http.providers +package online.hudacek.fxradio.api.http.provider -import online.hudacek.fxradio.api.http.interceptors.TokenInterceptor +import online.hudacek.fxradio.api.http.interceptor.TokenInterceptor /** * OkHttpClient with token authentication diff --git a/src/main/kotlin/online/hudacek/fxradio/api/stations/StationsApi.kt b/src/main/kotlin/online/hudacek/fxradio/api/stations/StationsApi.kt index f2068eae..b4b6c119 100644 --- a/src/main/kotlin/online/hudacek/fxradio/api/stations/StationsApi.kt +++ b/src/main/kotlin/online/hudacek/fxradio/api/stations/StationsApi.kt @@ -19,8 +19,8 @@ package online.hudacek.fxradio.api.stations import io.reactivex.Single import online.hudacek.fxradio.api.ApiServiceProvider import online.hudacek.fxradio.api.stations.model.* -import online.hudacek.fxradio.utils.Properties -import online.hudacek.fxradio.utils.Property +import online.hudacek.fxradio.util.Properties +import online.hudacek.fxradio.util.Property import online.hudacek.fxradio.viewmodel.Servers import online.hudacek.fxradio.viewmodel.ServersViewModel import retrofit2.http.Body diff --git a/src/main/kotlin/online/hudacek/fxradio/events/AppEvent.kt b/src/main/kotlin/online/hudacek/fxradio/event/AppEvent.kt similarity index 93% rename from src/main/kotlin/online/hudacek/fxradio/events/AppEvent.kt rename to src/main/kotlin/online/hudacek/fxradio/event/AppEvent.kt index 47cb98fd..4950b1c0 100644 --- a/src/main/kotlin/online/hudacek/fxradio/events/AppEvent.kt +++ b/src/main/kotlin/online/hudacek/fxradio/event/AppEvent.kt @@ -14,11 +14,11 @@ * limitations under the License. */ -package online.hudacek.fxradio.events +package online.hudacek.fxradio.event import io.reactivex.subjects.BehaviorSubject import online.hudacek.fxradio.api.stations.model.Station -import online.hudacek.fxradio.events.data.AppNotification +import online.hudacek.fxradio.event.data.AppNotification import online.hudacek.fxradio.media.StreamMetaData import online.hudacek.fxradio.viewmodel.LibraryState import tornadofx.Controller diff --git a/src/main/kotlin/online/hudacek/fxradio/events/data/AppNotification.kt b/src/main/kotlin/online/hudacek/fxradio/event/data/AppNotification.kt similarity index 94% rename from src/main/kotlin/online/hudacek/fxradio/events/data/AppNotification.kt rename to src/main/kotlin/online/hudacek/fxradio/event/data/AppNotification.kt index 0d362f77..2cba0a7f 100644 --- a/src/main/kotlin/online/hudacek/fxradio/events/data/AppNotification.kt +++ b/src/main/kotlin/online/hudacek/fxradio/event/data/AppNotification.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package online.hudacek.fxradio.events.data +package online.hudacek.fxradio.event.data import org.controlsfx.glyphfont.FontAwesome diff --git a/src/main/kotlin/online/hudacek/fxradio/media/MediaPlayer.kt b/src/main/kotlin/online/hudacek/fxradio/media/MediaPlayer.kt index 91fbaeb2..86ac507a 100644 --- a/src/main/kotlin/online/hudacek/fxradio/media/MediaPlayer.kt +++ b/src/main/kotlin/online/hudacek/fxradio/media/MediaPlayer.kt @@ -16,8 +16,8 @@ package online.hudacek.fxradio.media -import online.hudacek.fxradio.utils.Properties -import online.hudacek.fxradio.utils.value +import online.hudacek.fxradio.util.Properties +import online.hudacek.fxradio.util.value /** * Common interface for all available players diff --git a/src/main/kotlin/online/hudacek/fxradio/media/player/humble/HumbleMetaService.kt b/src/main/kotlin/online/hudacek/fxradio/media/player/humble/HumbleMetaService.kt index 390ae1cf..2010c562 100644 --- a/src/main/kotlin/online/hudacek/fxradio/media/player/humble/HumbleMetaService.kt +++ b/src/main/kotlin/online/hudacek/fxradio/media/player/humble/HumbleMetaService.kt @@ -22,7 +22,7 @@ import javafx.concurrent.ScheduledService import javafx.concurrent.Task import javafx.util.Duration import mu.KotlinLogging -import online.hudacek.fxradio.events.AppEvent +import online.hudacek.fxradio.event.AppEvent import online.hudacek.fxradio.media.StreamMetaData import tornadofx.find diff --git a/src/main/kotlin/online/hudacek/fxradio/media/player/vlc/VLCEvents.kt b/src/main/kotlin/online/hudacek/fxradio/media/player/vlc/VLCEvents.kt index 2f429219..494d1006 100644 --- a/src/main/kotlin/online/hudacek/fxradio/media/player/vlc/VLCEvents.kt +++ b/src/main/kotlin/online/hudacek/fxradio/media/player/vlc/VLCEvents.kt @@ -18,7 +18,7 @@ package online.hudacek.fxradio.media.player.vlc import io.reactivex.subjects.BehaviorSubject import mu.KotlinLogging -import online.hudacek.fxradio.events.AppEvent +import online.hudacek.fxradio.event.AppEvent import online.hudacek.fxradio.media.StreamMetaData import online.hudacek.fxradio.media.StreamUnavailableException import tornadofx.Controller diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/BaseFragment.kt b/src/main/kotlin/online/hudacek/fxradio/ui/BaseFragment.kt index a512cd2b..ab8acb74 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/BaseFragment.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/BaseFragment.kt @@ -16,7 +16,7 @@ package online.hudacek.fxradio.ui -import online.hudacek.fxradio.events.AppEvent +import online.hudacek.fxradio.event.AppEvent import tornadofx.Fragment abstract class BaseFragment(title: String? = null) : Fragment(title) { diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/BaseView.kt b/src/main/kotlin/online/hudacek/fxradio/ui/BaseView.kt index eda2abef..bea39485 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/BaseView.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/BaseView.kt @@ -16,7 +16,7 @@ package online.hudacek.fxradio.ui -import online.hudacek.fxradio.events.AppEvent +import online.hudacek.fxradio.event.AppEvent import tornadofx.View abstract class BaseView(title: String? = null) : View(title) { diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/Extensions.kt b/src/main/kotlin/online/hudacek/fxradio/ui/Extensions.kt index 0bcc57b2..37e93c52 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/Extensions.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/Extensions.kt @@ -162,7 +162,7 @@ internal fun T.showWhen(expr: () -> ObservableValue): T = */ internal fun EventTarget.stylableNotificationPane(op: (NotificationPane.() -> Unit) = {}) = notificationPane(showFromTop = true) { //Show dark notifications - if (FxRadio.isAppInDarkMode) { + if (FxRadio.darkModeEnabled) { styleClass += NotificationPane.STYLE_CLASS_DARK } op(this) diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/modal/AboutFragment.kt b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/AboutFragment.kt similarity index 98% rename from src/main/kotlin/online/hudacek/fxradio/ui/modal/AboutFragment.kt rename to src/main/kotlin/online/hudacek/fxradio/ui/fragment/AboutFragment.kt index fdc8e06f..598f5a96 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/modal/AboutFragment.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/AboutFragment.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package online.hudacek.fxradio.ui.modal +package online.hudacek.fxradio.ui.fragment import javafx.geometry.Pos import online.hudacek.fxradio.Config diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/modal/AddStationFragment.kt b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/AddStationFragment.kt similarity index 99% rename from src/main/kotlin/online/hudacek/fxradio/ui/modal/AddStationFragment.kt rename to src/main/kotlin/online/hudacek/fxradio/ui/fragment/AddStationFragment.kt index fb8c04c2..5048aa42 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/modal/AddStationFragment.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/AddStationFragment.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package online.hudacek.fxradio.ui.modal +package online.hudacek.fxradio.ui.fragment import com.github.thomasnield.rxkotlinfx.actionEvents import javafx.scene.layout.Priority diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/modal/AttributionsFragment.kt b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/AttributionsFragment.kt similarity index 98% rename from src/main/kotlin/online/hudacek/fxradio/ui/modal/AttributionsFragment.kt rename to src/main/kotlin/online/hudacek/fxradio/ui/fragment/AttributionsFragment.kt index 793e7b48..691b8953 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/modal/AttributionsFragment.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/AttributionsFragment.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package online.hudacek.fxradio.ui.modal +package online.hudacek.fxradio.ui.fragment import javafx.geometry.Pos import javafx.scene.layout.Priority diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/modal/Modals.kt b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/Modals.kt similarity index 97% rename from src/main/kotlin/online/hudacek/fxradio/ui/modal/Modals.kt rename to src/main/kotlin/online/hudacek/fxradio/ui/fragment/Modals.kt index e4c66c11..65147d6d 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/modal/Modals.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/Modals.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package online.hudacek.fxradio.ui.modal +package online.hudacek.fxradio.ui.fragment import javafx.stage.StageStyle import tornadofx.Fragment diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/modal/ServersFragment.kt b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/ServersFragment.kt similarity index 98% rename from src/main/kotlin/online/hudacek/fxradio/ui/modal/ServersFragment.kt rename to src/main/kotlin/online/hudacek/fxradio/ui/fragment/ServersFragment.kt index 4a04eb18..7b380180 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/modal/ServersFragment.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/ServersFragment.kt @@ -14,12 +14,12 @@ * limitations under the License. */ -package online.hudacek.fxradio.ui.modal +package online.hudacek.fxradio.ui.fragment import griffon.javafx.support.flagicons.FlagIcon import javafx.geometry.Pos import javafx.scene.text.TextAlignment -import online.hudacek.fxradio.events.data.AppNotification +import online.hudacek.fxradio.event.data.AppNotification import online.hudacek.fxradio.ui.BaseFragment import online.hudacek.fxradio.ui.showWhen import online.hudacek.fxradio.ui.style.Styles diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/modal/StationDebugFragment.kt b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/StationDebugFragment.kt similarity index 96% rename from src/main/kotlin/online/hudacek/fxradio/ui/modal/StationDebugFragment.kt rename to src/main/kotlin/online/hudacek/fxradio/ui/fragment/StationDebugFragment.kt index 2f6682dc..2e270044 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/modal/StationDebugFragment.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/StationDebugFragment.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package online.hudacek.fxradio.ui.modal +package online.hudacek.fxradio.ui.fragment import javafx.scene.layout.Priority import online.hudacek.fxradio.ui.BaseFragment diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/modal/StationInfoFragment.kt b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/StationInfoFragment.kt similarity index 99% rename from src/main/kotlin/online/hudacek/fxradio/ui/modal/StationInfoFragment.kt rename to src/main/kotlin/online/hudacek/fxradio/ui/fragment/StationInfoFragment.kt index 23a5afab..0dca1e27 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/modal/StationInfoFragment.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/StationInfoFragment.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package online.hudacek.fxradio.ui.modal +package online.hudacek.fxradio.ui.fragment import com.github.thomasnield.rxkotlinfx.actionEvents import javafx.beans.property.IntegerProperty diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/modal/StatsFragment.kt b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/StatsFragment.kt similarity index 98% rename from src/main/kotlin/online/hudacek/fxradio/ui/modal/StatsFragment.kt rename to src/main/kotlin/online/hudacek/fxradio/ui/fragment/StatsFragment.kt index a97c6099..65568997 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/modal/StatsFragment.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/StatsFragment.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package online.hudacek.fxradio.ui.modal +package online.hudacek.fxradio.ui.fragment import javafx.geometry.Pos import online.hudacek.fxradio.ui.BaseFragment diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/menu/AboutMenu.kt b/src/main/kotlin/online/hudacek/fxradio/ui/menu/AboutMenu.kt index e0c6fa1c..6352bbc1 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/menu/AboutMenu.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/menu/AboutMenu.kt @@ -18,9 +18,8 @@ package online.hudacek.fxradio.ui.menu import javafx.scene.control.MenuItem import online.hudacek.fxradio.FxRadio -import online.hudacek.fxradio.ui.modal.Modals -import online.hudacek.fxradio.ui.modal.open -import tornadofx.FX +import online.hudacek.fxradio.ui.fragment.Modals +import online.hudacek.fxradio.ui.fragment.open import tornadofx.action import tornadofx.get @@ -47,8 +46,7 @@ class AboutMenu : BaseMenu(FxRadio.appName) { separator(), item(messages["menu.app.quit"]) { action { - FX.primaryStage.close() - FxRadio.shutdownApp() + primaryStage.close() } } )) diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/menu/BaseMenu.kt b/src/main/kotlin/online/hudacek/fxradio/ui/menu/BaseMenu.kt index e0042745..91cd052e 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/menu/BaseMenu.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/menu/BaseMenu.kt @@ -21,7 +21,7 @@ import javafx.scene.control.MenuItem import javafx.scene.input.KeyCode import javafx.scene.input.KeyCodeCombination import javafx.scene.input.KeyCombination -import online.hudacek.fxradio.events.AppEvent +import online.hudacek.fxradio.event.AppEvent import online.hudacek.fxradio.viewmodel.AppMenuViewModel import tornadofx.Controller import tornadofx.get diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/menu/HelpMenu.kt b/src/main/kotlin/online/hudacek/fxradio/ui/menu/HelpMenu.kt index 354cd3e3..b3fbcead 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/menu/HelpMenu.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/menu/HelpMenu.kt @@ -17,8 +17,8 @@ package online.hudacek.fxradio.ui.menu import online.hudacek.fxradio.Config -import online.hudacek.fxradio.ui.modal.Modals -import online.hudacek.fxradio.ui.modal.open +import online.hudacek.fxradio.ui.fragment.Modals +import online.hudacek.fxradio.ui.fragment.open import online.hudacek.fxradio.ui.openUrl import tornadofx.action import tornadofx.get diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/menu/PlayerMenu.kt b/src/main/kotlin/online/hudacek/fxradio/ui/menu/PlayerMenu.kt index d3312a38..86a4e944 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/menu/PlayerMenu.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/menu/PlayerMenu.kt @@ -18,7 +18,7 @@ package online.hudacek.fxradio.ui.menu import online.hudacek.fxradio.media.MediaPlayer import online.hudacek.fxradio.media.MediaPlayerFactory -import online.hudacek.fxradio.utils.macos.MacUtils +import online.hudacek.fxradio.util.macos.MacUtils import online.hudacek.fxradio.viewmodel.OsNotificationViewModel import online.hudacek.fxradio.viewmodel.PlayerState import online.hudacek.fxradio.viewmodel.PlayerViewModel diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/menu/StationMenu.kt b/src/main/kotlin/online/hudacek/fxradio/ui/menu/StationMenu.kt index e58ebb0a..05785d6a 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/menu/StationMenu.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/menu/StationMenu.kt @@ -16,8 +16,8 @@ package online.hudacek.fxradio.ui.menu -import online.hudacek.fxradio.ui.modal.Modals -import online.hudacek.fxradio.ui.modal.open +import online.hudacek.fxradio.ui.fragment.Modals +import online.hudacek.fxradio.ui.fragment.open import online.hudacek.fxradio.ui.update import online.hudacek.fxradio.viewmodel.PlayerViewModel import tornadofx.action diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/style/Colors.kt b/src/main/kotlin/online/hudacek/fxradio/ui/style/Colors.kt index 209de157..a66af487 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/style/Colors.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/style/Colors.kt @@ -20,7 +20,7 @@ import online.hudacek.fxradio.FxRadio object Colors { val values: ColorValues by lazy { - if (FxRadio.isAppInDarkMode) DarkColorValues() else ColorValues() + if (FxRadio.darkModeEnabled) DarkColorValues() else ColorValues() } } diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/view/MainView.kt b/src/main/kotlin/online/hudacek/fxradio/ui/view/MainView.kt index 533eef81..32fd0b47 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/view/MainView.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/view/MainView.kt @@ -28,8 +28,8 @@ import online.hudacek.fxradio.ui.style.Styles import online.hudacek.fxradio.ui.view.library.LibraryView import online.hudacek.fxradio.ui.view.player.PlayerView import online.hudacek.fxradio.ui.view.stations.StationsView -import online.hudacek.fxradio.utils.Properties -import online.hudacek.fxradio.utils.Property +import online.hudacek.fxradio.util.Properties +import online.hudacek.fxradio.util.Property import tornadofx.* import tornadofx.controlsfx.content @@ -51,11 +51,6 @@ class MainView : BaseView(FxRadio.appName) { } override fun onDock() { - //Correctly shutdown all classes - currentStage?.setOnCloseRequest { - FxRadio.shutdownApp() - } - with(leftPane) { //Workaround for setting correct position of divider after restart of app setDividerPositions(windowDividerProperty.get(0.30)) diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/view/MenuBarView.kt b/src/main/kotlin/online/hudacek/fxradio/ui/view/MenuBarView.kt index 4a285bf8..618e6288 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/view/MenuBarView.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/view/MenuBarView.kt @@ -18,8 +18,8 @@ package online.hudacek.fxradio.ui.view import online.hudacek.fxradio.ui.BaseView import online.hudacek.fxradio.ui.menu.* -import online.hudacek.fxradio.utils.macos.NSMenu -import online.hudacek.fxradio.utils.macos.NSMenuBar +import online.hudacek.fxradio.util.macos.NSMenu +import online.hudacek.fxradio.util.macos.NSMenuBar import online.hudacek.fxradio.viewmodel.AppMenuViewModel import tornadofx.get import tornadofx.menubar diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/view/player/TickerView.kt b/src/main/kotlin/online/hudacek/fxradio/ui/view/player/TickerView.kt index ef4cd011..289ed848 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/view/player/TickerView.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/view/player/TickerView.kt @@ -153,7 +153,7 @@ class TickerView : BaseView() { if (layoutX <= 0 - textWidth - (2 * offset)) { //Now I ned to figure out how to remove it entry.content.removeFromParent() //Is this legit? - activeTicks.remove(active) //no longer here, shouldn't ruin the loop + activeTicks -= active //no longer here, shouldn't ruin the loop if (entry in subscriptions) { subscriptions.remove(entry)!!.dispose() //This should cancel it } diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsDataGridView.kt b/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsDataGridView.kt index 044483a0..dae78e52 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsDataGridView.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsDataGridView.kt @@ -25,10 +25,10 @@ import javafx.scene.paint.Color import online.hudacek.fxradio.Config import online.hudacek.fxradio.api.stations.model.tagsSplit import online.hudacek.fxradio.ui.BaseView +import online.hudacek.fxradio.ui.fragment.Modals +import online.hudacek.fxradio.ui.fragment.new +import online.hudacek.fxradio.ui.fragment.open import online.hudacek.fxradio.ui.menu.FavouritesMenu -import online.hudacek.fxradio.ui.modal.Modals -import online.hudacek.fxradio.ui.modal.new -import online.hudacek.fxradio.ui.modal.open import online.hudacek.fxradio.ui.showWhen import online.hudacek.fxradio.ui.smallLabel import online.hudacek.fxradio.ui.stationImage diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/BaseUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/BaseUseCase.kt index 849cb01d..38116576 100644 --- a/src/main/kotlin/online/hudacek/fxradio/usecase/BaseUseCase.kt +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/BaseUseCase.kt @@ -20,7 +20,7 @@ import com.github.thomasnield.rxkotlinfx.observeOnFx import io.reactivex.SingleTransformer import io.reactivex.schedulers.Schedulers import online.hudacek.fxradio.api.stations.StationsApi -import online.hudacek.fxradio.events.AppEvent +import online.hudacek.fxradio.event.AppEvent import tornadofx.Controller /** diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/ClearCacheUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/ClearCacheUseCase.kt index 289006e9..3eb96c27 100644 --- a/src/main/kotlin/online/hudacek/fxradio/usecase/ClearCacheUseCase.kt +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/ClearCacheUseCase.kt @@ -16,14 +16,13 @@ package online.hudacek.fxradio.usecase -import online.hudacek.fxradio.events.data.AppNotification +import online.hudacek.fxradio.event.data.AppNotification import online.hudacek.fxradio.storage.ImageCache import online.hudacek.fxradio.ui.formatted import org.controlsfx.glyphfont.FontAwesome import tornadofx.confirm import tornadofx.fail import tornadofx.get -import tornadofx.success /** * Clears FxRadio image cache directory @@ -38,7 +37,6 @@ class ClearCacheUseCase : BaseUseCase() { messages["cache.clear.text"].formatted(ImageCache.totalSize), owner = primaryStage) { runAsync(daemon = true) { ImageCache.clear() - } success { appEvent.appNotification.onNext( AppNotification(messages["cache.clear.ok"], FontAwesome.Glyph.CHECK)) } fail { diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/PinCountryUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/CountryPinUseCase.kt similarity index 93% rename from src/main/kotlin/online/hudacek/fxradio/usecase/PinCountryUseCase.kt rename to src/main/kotlin/online/hudacek/fxradio/usecase/CountryPinUseCase.kt index fa7caca0..628b1c09 100644 --- a/src/main/kotlin/online/hudacek/fxradio/usecase/PinCountryUseCase.kt +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/CountryPinUseCase.kt @@ -20,7 +20,7 @@ import io.reactivex.Single import online.hudacek.fxradio.api.stations.model.Country import online.hudacek.fxradio.storage.db.Tables -class PinCountryUseCase : BaseUseCase>() { +class CountryPinUseCase : BaseUseCase>() { override fun execute(input: Country) = Tables.pinnedCountries.insert(input) } \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/UnPinCountryUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/CountryUnpinUseCase.kt similarity index 93% rename from src/main/kotlin/online/hudacek/fxradio/usecase/UnPinCountryUseCase.kt rename to src/main/kotlin/online/hudacek/fxradio/usecase/CountryUnpinUseCase.kt index 0a641c31..8094f234 100644 --- a/src/main/kotlin/online/hudacek/fxradio/usecase/UnPinCountryUseCase.kt +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/CountryUnpinUseCase.kt @@ -20,7 +20,7 @@ import io.reactivex.Single import online.hudacek.fxradio.api.stations.model.Country import online.hudacek.fxradio.storage.db.Tables -class UnPinCountryUseCase : BaseUseCase>() { +class CountryUnpinUseCase : BaseUseCase>() { override fun execute(input: Country) = Tables.pinnedCountries.remove(input) } \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/FavouriteAddUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/FavouriteAddUseCase.kt new file mode 100644 index 00000000..d17d837f --- /dev/null +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/FavouriteAddUseCase.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2020 FXRadio by hudacek.online + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package online.hudacek.fxradio.usecase + +import io.reactivex.Single +import online.hudacek.fxradio.api.stations.model.Station +import online.hudacek.fxradio.storage.db.Tables + +class FavouriteAddUseCase : BaseUseCase>() { + + override fun execute(input: Station) = Tables.favourites.insert(input) +} \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/FavouriteRemoveUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/FavouriteRemoveUseCase.kt new file mode 100644 index 00000000..0a4645c5 --- /dev/null +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/FavouriteRemoveUseCase.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2020 FXRadio by hudacek.online + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package online.hudacek.fxradio.usecase + +import io.reactivex.Single +import online.hudacek.fxradio.api.stations.model.Station +import online.hudacek.fxradio.storage.db.Tables + +class FavouriteRemoveUseCase : BaseUseCase>() { + + override fun execute(input: Station) = Tables.favourites.remove(input) +} \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/FavouriteSetUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/FavouriteSetUseCase.kt new file mode 100644 index 00000000..569d1188 --- /dev/null +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/FavouriteSetUseCase.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2020 FXRadio by hudacek.online + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package online.hudacek.fxradio.usecase + +import io.reactivex.disposables.Disposable +import javafx.beans.property.ListProperty +import online.hudacek.fxradio.api.stations.model.Station +import online.hudacek.fxradio.storage.db.Tables + +class FavouriteSetUseCase : BaseUseCase, Disposable>() { + + override fun execute(input: ListProperty): Disposable = Tables.favourites.selectAll() + .subscribe { + input += it + } +} \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/FavouritesClearUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/FavouritesClearUseCase.kt new file mode 100644 index 00000000..b1034b90 --- /dev/null +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/FavouritesClearUseCase.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2020 FXRadio by hudacek.online + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package online.hudacek.fxradio.usecase + +import io.reactivex.disposables.Disposable +import mu.KotlinLogging +import online.hudacek.fxradio.storage.db.Tables +import online.hudacek.fxradio.viewmodel.Favourites +import online.hudacek.fxradio.viewmodel.FavouritesViewModel + +private val logger = KotlinLogging.logger {} + +/** + * Removes favourites DB and resets viewmodel + */ +class FavouritesClearUseCase : BaseUseCase() { + + override fun execute(input: FavouritesViewModel): Disposable = Tables.favourites.removeAll() + .subscribe({ + input.item = Favourites() + }, { + logger.error(it) { "Exception when performing DB cleanup!" } + }) +} \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/GetServersUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/GetServersUseCase.kt index dc4cd188..2afd69d9 100644 --- a/src/main/kotlin/online/hudacek/fxradio/usecase/GetServersUseCase.kt +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/GetServersUseCase.kt @@ -27,7 +27,6 @@ import tornadofx.asObservable */ class GetServersUseCase : BaseUseCase>>() { - //API lookup URL private val lookupUrl = Config.API.dnsLookupURL override fun execute(input: Unit): Task> = runAsync(daemon = true) { diff --git a/src/main/kotlin/online/hudacek/fxradio/utils/Command.kt b/src/main/kotlin/online/hudacek/fxradio/util/Command.kt similarity index 96% rename from src/main/kotlin/online/hudacek/fxradio/utils/Command.kt rename to src/main/kotlin/online/hudacek/fxradio/util/Command.kt index 8976e063..47113140 100644 --- a/src/main/kotlin/online/hudacek/fxradio/utils/Command.kt +++ b/src/main/kotlin/online/hudacek/fxradio/util/Command.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package online.hudacek.fxradio.utils +package online.hudacek.fxradio.util import java.io.BufferedReader import java.io.InputStreamReader diff --git a/src/main/kotlin/online/hudacek/fxradio/utils/Properties.kt b/src/main/kotlin/online/hudacek/fxradio/util/Properties.kt similarity index 99% rename from src/main/kotlin/online/hudacek/fxradio/utils/Properties.kt rename to src/main/kotlin/online/hudacek/fxradio/util/Properties.kt index 919e5c1d..68e190e5 100644 --- a/src/main/kotlin/online/hudacek/fxradio/utils/Properties.kt +++ b/src/main/kotlin/online/hudacek/fxradio/util/Properties.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package online.hudacek.fxradio.utils +package online.hudacek.fxradio.util import mu.KotlinLogging import tornadofx.Component diff --git a/src/main/kotlin/online/hudacek/fxradio/util/Tray.kt b/src/main/kotlin/online/hudacek/fxradio/util/Tray.kt new file mode 100644 index 00000000..a9d2bb71 --- /dev/null +++ b/src/main/kotlin/online/hudacek/fxradio/util/Tray.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2020 FXRadio by hudacek.online + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package online.hudacek.fxradio.util + +import online.hudacek.fxradio.Config +import online.hudacek.fxradio.FxRadio +import tornadofx.Component +import tornadofx.get + +/** + * Adds system tray. Experimental, enabled under flag + */ +class Tray : Component() { + + fun addIcon() = with(app) { + if (Config.Flags.useTrayIcon) { + trayicon(resources.stream("/" + Config.Resources.stageIcon)) { + setOnMouseClicked(fxThread = true) { + primaryStage.show() + primaryStage.toFront() + } + menu(FxRadio.appName) { + item(messages["show"]) { + setOnAction(fxThread = true) { + primaryStage.show() + primaryStage.toFront() + } + } + item(messages["exit"]) { + setOnAction(fxThread = true) { + primaryStage.close() + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/utils/macos/MacUtils.kt b/src/main/kotlin/online/hudacek/fxradio/util/macos/MacUtils.kt similarity index 93% rename from src/main/kotlin/online/hudacek/fxradio/utils/macos/MacUtils.kt rename to src/main/kotlin/online/hudacek/fxradio/util/macos/MacUtils.kt index ec2afd99..a044ca45 100644 --- a/src/main/kotlin/online/hudacek/fxradio/utils/macos/MacUtils.kt +++ b/src/main/kotlin/online/hudacek/fxradio/util/macos/MacUtils.kt @@ -14,10 +14,10 @@ * limitations under the License. */ -package online.hudacek.fxradio.utils.macos +package online.hudacek.fxradio.util.macos import airsquared.JMacNotification.NSUserNotification -import online.hudacek.fxradio.utils.Command +import online.hudacek.fxradio.util.Command import org.controlsfx.tools.Platform object MacUtils { diff --git a/src/main/kotlin/online/hudacek/fxradio/utils/macos/NSMenu.kt b/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenu.kt similarity index 98% rename from src/main/kotlin/online/hudacek/fxradio/utils/macos/NSMenu.kt rename to src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenu.kt index e492984c..426c3280 100644 --- a/src/main/kotlin/online/hudacek/fxradio/utils/macos/NSMenu.kt +++ b/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenu.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package online.hudacek.fxradio.utils.macos +package online.hudacek.fxradio.util.macos import de.codecentric.centerdevice.MenuToolkit import javafx.scene.control.MenuItem diff --git a/src/main/kotlin/online/hudacek/fxradio/utils/macos/NSMenuBar.kt b/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenuBar.kt similarity index 95% rename from src/main/kotlin/online/hudacek/fxradio/utils/macos/NSMenuBar.kt rename to src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenuBar.kt index 215d099a..a30bba4e 100644 --- a/src/main/kotlin/online/hudacek/fxradio/utils/macos/NSMenuBar.kt +++ b/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenuBar.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package online.hudacek.fxradio.utils.macos +package online.hudacek.fxradio.util.macos import javafx.scene.control.MenuBar diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/AddStationViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/AddStationViewModel.kt index 68722378..0eb5b1d4 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/AddStationViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/AddStationViewModel.kt @@ -27,7 +27,7 @@ import tornadofx.stringBinding import java.util.* /** - * Stores entered information into the form in [online.hudacek.fxradio.ui.modal.AddStationFragment] + * Stores entered information into the form in [online.hudacek.fxradio.ui.fragment.AddStationFragment] * Handles logic for adding new station into the API */ class AddStationModel(name: String = "", diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/AppMenuViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/AppMenuViewModel.kt index 8d8cabc3..9e214075 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/AppMenuViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/AppMenuViewModel.kt @@ -20,9 +20,9 @@ import javafx.beans.property.BooleanProperty import online.hudacek.fxradio.FxRadio import online.hudacek.fxradio.ui.openUrl import online.hudacek.fxradio.usecase.ClearCacheUseCase -import online.hudacek.fxradio.utils.Properties -import online.hudacek.fxradio.utils.macos.MacUtils -import online.hudacek.fxradio.utils.value +import online.hudacek.fxradio.util.Properties +import online.hudacek.fxradio.util.macos.MacUtils +import online.hudacek.fxradio.util.value import tornadofx.property class AppMenu(usePlatform: Boolean = MacUtils.isMac && Properties.UseNativeMenuBar.value(true)) { diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/Attributions.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/Attributions.kt index 1188835b..f2e46169 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/Attributions.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/Attributions.kt @@ -57,7 +57,7 @@ class Attribution(name: String, version: String = "", license: License) { * Attribution view model * ------------------- * Handles information about Licensing - * Used in [online.hudacek.fxradio.ui.modal.AttributionsFragment] + * Used in [online.hudacek.fxradio.ui.fragment.AttributionsFragment] */ class AttributionViewModel : BaseViewModel() { val nameProperty = bind(Attribution::name) as StringProperty diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/BaseViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/BaseViewModel.kt index aac2e0c6..076a5714 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/BaseViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/BaseViewModel.kt @@ -16,7 +16,7 @@ package online.hudacek.fxradio.viewmodel -import online.hudacek.fxradio.events.AppEvent +import online.hudacek.fxradio.event.AppEvent import tornadofx.ItemViewModel abstract class BaseViewModel(initialItem: Item? = null) : diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/FavouritesViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/FavouritesViewModel.kt index 1f56ec0b..d8725f4a 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/FavouritesViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/FavouritesViewModel.kt @@ -20,18 +20,18 @@ import io.reactivex.Single import io.reactivex.disposables.Disposable import javafx.beans.property.ListProperty import javafx.collections.ObservableList -import mu.KotlinLogging import online.hudacek.fxradio.api.stations.model.Station -import online.hudacek.fxradio.events.data.AppNotification -import online.hudacek.fxradio.storage.db.Tables +import online.hudacek.fxradio.event.data.AppNotification import online.hudacek.fxradio.ui.formatted +import online.hudacek.fxradio.usecase.FavouriteAddUseCase +import online.hudacek.fxradio.usecase.FavouriteRemoveUseCase +import online.hudacek.fxradio.usecase.FavouriteSetUseCase +import online.hudacek.fxradio.usecase.FavouritesClearUseCase import org.controlsfx.glyphfont.FontAwesome import tornadofx.get import tornadofx.observableListOf import tornadofx.property -private val logger = KotlinLogging.logger {} - class Favourites(stations: ObservableList = observableListOf()) { var stations: ObservableList by property(stations) } @@ -42,18 +42,19 @@ class Favourites(stations: ObservableList = observableListOf()) { */ class FavouritesViewModel : BaseViewModel(Favourites()) { + private val favouriteAddUseCase: FavouriteAddUseCase by inject() + private val favouriteRemoveUseCase: FavouriteRemoveUseCase by inject() + private val cleanFavouritesClearUseCase: FavouritesClearUseCase by inject() + private val favouriteSetUseCase: FavouriteSetUseCase by inject() + val stationsProperty = bind(Favourites::stations) as ListProperty init { - Tables.favourites - .selectAll() - .subscribe { - stationsProperty += it - } + favouriteSetUseCase.execute(stationsProperty) appEvent.addFavourite .filter { it.isValid() && it !in stationsProperty } - .flatMapSingle { Tables.favourites.insert(it) } + .flatMapSingle { favouriteAddUseCase.execute(it) } .flatMapSingle { stationsProperty += it Single.just(AppNotification(messages["menu.station.favouriteAdded"].formatted(it.name), @@ -61,19 +62,13 @@ class FavouritesViewModel : BaseViewModel(Favourites()) { }.subscribe(appEvent.appNotification) appEvent.removeFavourite - .flatMapSingle { Tables.favourites.remove(it) } + .flatMapSingle { favouriteRemoveUseCase.execute(it) } .flatMapSingle { - stationsProperty.remove(it) + stationsProperty -= it Single.just(AppNotification(messages["menu.station.favouriteRemoved"].formatted(it.name), FontAwesome.Glyph.CHECK)) }.subscribe(appEvent.appNotification) } - fun cleanupFavourites(): Disposable = Tables.favourites - .removeAll() - .subscribe({ - item = Favourites() - }, { - logger.error(it) { "Exception when performing DB cleanup!" } - }) + fun cleanupFavourites(): Disposable = cleanFavouritesClearUseCase.execute(this) } \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/LibraryViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/LibraryViewModel.kt index 4875ce7a..d91d3f91 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/LibraryViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/LibraryViewModel.kt @@ -22,12 +22,12 @@ import javafx.beans.property.ListProperty import javafx.collections.ObservableList import mu.KotlinLogging import online.hudacek.fxradio.api.stations.model.Country +import online.hudacek.fxradio.usecase.CountryPinUseCase +import online.hudacek.fxradio.usecase.CountryUnpinUseCase import online.hudacek.fxradio.usecase.GetCountriesUseCase -import online.hudacek.fxradio.usecase.PinCountryUseCase -import online.hudacek.fxradio.usecase.UnPinCountryUseCase -import online.hudacek.fxradio.utils.Properties -import online.hudacek.fxradio.utils.saveProperties -import online.hudacek.fxradio.utils.value +import online.hudacek.fxradio.util.Properties +import online.hudacek.fxradio.util.saveProperties +import online.hudacek.fxradio.util.value import org.controlsfx.glyphfont.FontAwesome import tornadofx.observableListOf import tornadofx.property @@ -77,8 +77,8 @@ class Library(countries: ObservableList = observableListOf(), class LibraryViewModel : BaseStateViewModel(Library(), LibraryState.TopStations) { private val getCountriesUseCase: GetCountriesUseCase by inject() - private val pinCountryUseCase: PinCountryUseCase by inject() - private val unPinCountryUseCase: UnPinCountryUseCase by inject() + private val countryPinUseCase: CountryPinUseCase by inject() + private val countryUnpinUseCase: CountryUnpinUseCase by inject() val countriesProperty = bind(Library::countries) as ListProperty val librariesProperty = bind(Library::libraries) as ListProperty @@ -88,16 +88,16 @@ class LibraryViewModel : BaseStateViewModel(Library(), Li val showCountriesProperty = bind(Library::showCountries) as BooleanProperty val showPinnedProperty = bind(Library::showPinned) as BooleanProperty - fun pinCountry(country: Country): Disposable = pinCountryUseCase.execute(country) + fun pinCountry(country: Country): Disposable = countryPinUseCase.execute(country) .subscribe({ pinnedProperty.add(it) }, { logger.error(it) { "Exception when performing Pinning!" } }) - fun unpinCountry(country: Country): Disposable = unPinCountryUseCase.execute(country) + fun unpinCountry(country: Country): Disposable = countryUnpinUseCase.execute(country) .subscribe({ - pinnedProperty.remove(it) + pinnedProperty -= it }, { logger.error(it) { "Exception when performing Unpinning!" } }) diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/LogViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/LogViewModel.kt index 1bdda000..18e6f918 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/LogViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/LogViewModel.kt @@ -17,9 +17,9 @@ package online.hudacek.fxradio.viewmodel import javafx.beans.property.ObjectProperty -import online.hudacek.fxradio.utils.Properties -import online.hudacek.fxradio.utils.save -import online.hudacek.fxradio.utils.value +import online.hudacek.fxradio.util.Properties +import online.hudacek.fxradio.util.save +import online.hudacek.fxradio.util.value import org.apache.logging.log4j.Level import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.core.config.Configurator diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/OsNotificationViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/OsNotificationViewModel.kt index 440d883d..17513059 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/OsNotificationViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/OsNotificationViewModel.kt @@ -17,10 +17,10 @@ package online.hudacek.fxradio.viewmodel import javafx.beans.property.BooleanProperty -import online.hudacek.fxradio.utils.Properties -import online.hudacek.fxradio.utils.macos.MacUtils -import online.hudacek.fxradio.utils.save -import online.hudacek.fxradio.utils.value +import online.hudacek.fxradio.util.Properties +import online.hudacek.fxradio.util.macos.MacUtils +import online.hudacek.fxradio.util.save +import online.hudacek.fxradio.util.value import tornadofx.property //Notifications are currently enabled only on macOS diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/PlayerViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/PlayerViewModel.kt index 4aea1eea..f4463629 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/PlayerViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/PlayerViewModel.kt @@ -24,14 +24,14 @@ import javafx.beans.property.DoubleProperty import javafx.beans.property.ObjectProperty import javafx.beans.property.StringProperty import online.hudacek.fxradio.api.stations.model.Station -import online.hudacek.fxradio.events.data.AppNotification +import online.hudacek.fxradio.event.data.AppNotification import online.hudacek.fxradio.media.MediaPlayer import online.hudacek.fxradio.media.MediaPlayerFactory import online.hudacek.fxradio.media.StreamMetaData import online.hudacek.fxradio.usecase.ClickUseCase -import online.hudacek.fxradio.utils.Properties -import online.hudacek.fxradio.utils.saveProperties -import online.hudacek.fxradio.utils.value +import online.hudacek.fxradio.util.Properties +import online.hudacek.fxradio.util.saveProperties +import online.hudacek.fxradio.util.value import org.controlsfx.glyphfont.FontAwesome import tornadofx.get import tornadofx.onChange diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/SearchViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/SearchViewModel.kt index f54bac9e..04c560e7 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/SearchViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/SearchViewModel.kt @@ -23,9 +23,9 @@ import io.reactivex.Observable import javafx.beans.property.BooleanProperty import javafx.beans.property.StringProperty import online.hudacek.fxradio.usecase.SearchUseCase -import online.hudacek.fxradio.utils.Properties -import online.hudacek.fxradio.utils.save -import online.hudacek.fxradio.utils.value +import online.hudacek.fxradio.util.Properties +import online.hudacek.fxradio.util.save +import online.hudacek.fxradio.util.value import tornadofx.property class Search(query: String = Properties.SearchQuery.value(""), diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/ServersViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/ServersViewModel.kt index 6895de70..6b8ff789 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/ServersViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/ServersViewModel.kt @@ -21,8 +21,8 @@ import javafx.beans.property.StringProperty import javafx.collections.ObservableList import online.hudacek.fxradio.Config import online.hudacek.fxradio.usecase.GetServersUseCase -import online.hudacek.fxradio.utils.Properties -import online.hudacek.fxradio.utils.save +import online.hudacek.fxradio.util.Properties +import online.hudacek.fxradio.util.save import tornadofx.fail import tornadofx.observableListOf import tornadofx.property @@ -46,7 +46,7 @@ class Servers(selectedServer: String = Config.API.fallbackApiServerURL, * Item is set in [online.hudacek.fxradio.api.StationsApi.Companion] * * Search for available servers is performed only on first start of the app or when opening - * [online.hudacek.fxradio.ui.modal.ServersFragment] + * [online.hudacek.fxradio.ui.fragment.ServersFragment] */ class ServersViewModel : BaseStateViewModel(Servers()) { diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/StationsViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/StationsViewModel.kt index 6ff9d03f..861214df 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/StationsViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/StationsViewModel.kt @@ -20,7 +20,7 @@ import io.reactivex.Single import javafx.beans.property.ListProperty import javafx.collections.ObservableList import online.hudacek.fxradio.api.stations.model.Station -import online.hudacek.fxradio.events.data.AppNotification +import online.hudacek.fxradio.event.data.AppNotification import online.hudacek.fxradio.usecase.GetStationsByCountryUseCase import online.hudacek.fxradio.usecase.GetTopStationsUseCase import online.hudacek.fxradio.usecase.VoteUseCase diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/StatsViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/StatsViewModel.kt index 71a3f81a..cb8360ae 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/StatsViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/StatsViewModel.kt @@ -35,7 +35,7 @@ class Stats(stats: ObservableList> = observableListOf()) { /** * Holds information about radio-browser API stats and health - * Shown inside [online.hudacek.fxradio.ui.modal.StatsFragment] + * Shown inside [online.hudacek.fxradio.ui.fragment.StatsFragment] */ class StatsViewModel : BaseStateViewModel(Stats()) { diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml index f091d5d9..3ec18c54 100644 --- a/src/main/resources/log4j2.xml +++ b/src/main/resources/log4j2.xml @@ -14,7 +14,6 @@ %d %p %c{1.} [%t] %m%n - diff --git a/src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt b/src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt index ae7cbd2a..4bcf81e2 100644 --- a/src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt +++ b/src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt @@ -26,7 +26,7 @@ import online.hudacek.fxradio.api.stations.StationsApi import online.hudacek.fxradio.api.stations.model.SearchBody import online.hudacek.fxradio.api.stations.model.Station import online.hudacek.fxradio.storage.db.Tables -import online.hudacek.fxradio.utils.macos.NSMenu +import online.hudacek.fxradio.util.macos.NSMenu import online.hudacek.fxradio.viewmodel.LibraryItem import online.hudacek.fxradio.viewmodel.PlayerState import online.hudacek.fxradio.viewmodel.PlayerViewModel diff --git a/src/test/kotlin/online/hudacek/fxradio/integration/DarkModeTests.kt b/src/test/kotlin/online/hudacek/fxradio/integration/DarkModeTests.kt index abc30d02..6a60ef23 100644 --- a/src/test/kotlin/online/hudacek/fxradio/integration/DarkModeTests.kt +++ b/src/test/kotlin/online/hudacek/fxradio/integration/DarkModeTests.kt @@ -19,7 +19,7 @@ package online.hudacek.fxradio.integration import javafx.stage.Stage import online.hudacek.fxradio.FxRadio import online.hudacek.fxradio.FxRadioDark -import online.hudacek.fxradio.utils.macos.NSMenu +import online.hudacek.fxradio.util.macos.NSMenu import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.testfx.api.FxAssert From d95e33c2c05176c8be75f58ffe2b7cba2debe08d Mon Sep 17 00:00:00 2001 From: jozef Date: Fri, 21 May 2021 22:50:29 +0200 Subject: [PATCH 04/27] test --- .../hudacek/fxradio/api/stations/StationsApi.kt | 3 ++- .../hudacek/fxradio/usecase/BaseUseCase.kt | 3 ++- .../fxradio/usecase/GetCountriesUseCase.kt | 4 ++++ .../hudacek/fxradio/util/macos/MacUtils.kt | 2 ++ .../online/hudacek/fxradio/util/macos/NSMenu.kt | 8 ++------ .../hudacek/fxradio/util/macos/NSMenuBar.kt | 6 +----- .../fxradio/viewmodel/PlayerViewModel.kt | 6 ++++-- .../integration/BasicFunctionalityTests.kt | 17 +++++++++++++---- .../fxradio/integration/DarkModeTests.kt | 5 +++-- 9 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/main/kotlin/online/hudacek/fxradio/api/stations/StationsApi.kt b/src/main/kotlin/online/hudacek/fxradio/api/stations/StationsApi.kt index b4b6c119..cece3690 100644 --- a/src/main/kotlin/online/hudacek/fxradio/api/stations/StationsApi.kt +++ b/src/main/kotlin/online/hudacek/fxradio/api/stations/StationsApi.kt @@ -74,6 +74,7 @@ interface StationsApi { ApiServiceProvider("https://${viewModel.selectedProperty.value}") } - val service by lazy { serviceProvider.get() } + val service + get() = serviceProvider.get() } } \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/BaseUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/BaseUseCase.kt index 38116576..a34b2404 100644 --- a/src/main/kotlin/online/hudacek/fxradio/usecase/BaseUseCase.kt +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/BaseUseCase.kt @@ -30,7 +30,8 @@ abstract class BaseUseCase : Controller() { protected val appEvent: AppEvent by inject() - protected val apiService by lazy { StationsApi.service } + protected val apiService + get() = StationsApi.service abstract fun execute(input: InputType): OutputType diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/GetCountriesUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/GetCountriesUseCase.kt index edbfb82e..422be281 100644 --- a/src/main/kotlin/online/hudacek/fxradio/usecase/GetCountriesUseCase.kt +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/GetCountriesUseCase.kt @@ -18,10 +18,13 @@ package online.hudacek.fxradio.usecase import io.reactivex.disposables.Disposable import javafx.beans.property.ListProperty +import mu.KotlinLogging import online.hudacek.fxradio.api.stations.model.CountriesBody import online.hudacek.fxradio.api.stations.model.Country import online.hudacek.fxradio.api.stations.model.isValid +private val logger = KotlinLogging.logger {} + /** * Gets list of valid country names and count of stations in it */ @@ -32,6 +35,7 @@ class GetCountriesUseCase : BaseUseCase, Disposable>() { .compose(applySchedulers()) .flattenAsObservable { it } .filter { it.isValid } + .doOnError { logger.error(it) { "Exception while getting Countries!" } } .subscribe { input.add(it) } diff --git a/src/main/kotlin/online/hudacek/fxradio/util/macos/MacUtils.kt b/src/main/kotlin/online/hudacek/fxradio/util/macos/MacUtils.kt index a044ca45..ef8960b6 100644 --- a/src/main/kotlin/online/hudacek/fxradio/util/macos/MacUtils.kt +++ b/src/main/kotlin/online/hudacek/fxradio/util/macos/MacUtils.kt @@ -24,6 +24,8 @@ object MacUtils { val isMac = Platform.getCurrent() == Platform.OSX + var useNSMenu = true + /** * Shows MacOS native system notification */ diff --git a/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenu.kt b/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenu.kt index 426c3280..0b5c97ea 100644 --- a/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenu.kt +++ b/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenu.kt @@ -35,7 +35,7 @@ open class NSMenu { protected val menuToolkit: MenuToolkit by lazy { MenuToolkit.toolkit(FX.locale) } fun appMenu(menuItems: List) = menu(FxRadio.appName) { - if (!isInTest) { + if (MacUtils.useNSMenu) { menuToolkit.setApplicationMenu(this) items.addAll(menuItems) items.addAll( @@ -49,7 +49,7 @@ open class NSMenu { } fun windowMenu(name: String) = menu(name) { - if (!isInTest) { + if (MacUtils.useNSMenu) { menuToolkit.autoAddWindowMenuItems(this) items.addAll( menuToolkit.createMinimizeMenuItem(), @@ -59,9 +59,5 @@ open class NSMenu { menuToolkit.createBringAllToFrontItem()) } } - - companion object { - var isInTest: Boolean = false - } } diff --git a/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenuBar.kt b/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenuBar.kt index a30bba4e..4093ea41 100644 --- a/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenuBar.kt +++ b/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenuBar.kt @@ -24,14 +24,10 @@ import javafx.scene.control.MenuBar class NSMenuBar : NSMenu() { val menuBar = MenuBar().apply { - if (!isInTest) { + if (MacUtils.useNSMenu) { useSystemMenuBarProperty().value = true menuToolkit.setMenuBar(this) } } - - companion object { - var isInTest: Boolean = false - } } diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/PlayerViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/PlayerViewModel.kt index f4463629..795976db 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/PlayerViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/PlayerViewModel.kt @@ -78,14 +78,16 @@ class PlayerViewModel : BaseStateViewModel(initialState = P .filter { it.isValid() } .doOnEach(appEvent.addToHistory) //Send the new history item event .flatMapSingle(clickUseCase::execute) - .subscribe { + .subscribe({ //Update the name of the station trackNameProperty.value = it.name + " - " + messages["player.noMetaData"] //Restart playing status stateProperty.value = PlayerState.Stopped stateProperty.value = PlayerState.Playing - } + }, { + stateProperty.value = PlayerState.Error(it.localizedMessage) + }) //Set volume for current player volumeProperty.onChange { mediaPlayerProperty.value?.changeVolume(it) } diff --git a/src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt b/src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt index 4bcf81e2..3fa1d33c 100644 --- a/src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt +++ b/src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt @@ -20,13 +20,15 @@ import javafx.scene.control.Button import javafx.scene.control.ListView import javafx.scene.control.Slider import javafx.stage.Stage +import online.hudacek.fxradio.Config import online.hudacek.fxradio.FxRadio import online.hudacek.fxradio.FxRadioLight +import online.hudacek.fxradio.api.ApiServiceProvider import online.hudacek.fxradio.api.stations.StationsApi import online.hudacek.fxradio.api.stations.model.SearchBody import online.hudacek.fxradio.api.stations.model.Station import online.hudacek.fxradio.storage.db.Tables -import online.hudacek.fxradio.util.macos.NSMenu +import online.hudacek.fxradio.util.macos.MacUtils import online.hudacek.fxradio.viewmodel.LibraryItem import online.hudacek.fxradio.viewmodel.PlayerState import online.hudacek.fxradio.viewmodel.PlayerViewModel @@ -52,6 +54,7 @@ import tornadofx.find class BasicFunctionalityTests { private lateinit var app: FxRadio + private lateinit var stage: Stage companion object { //IDs @@ -69,17 +72,19 @@ class BasicFunctionalityTests { } //Http Client, init only once needed - private val service by lazy { StationsApi.service } + private val service + get() = ApiServiceProvider("https://${Config.API.fallbackApiServerURL}").get() @Init fun init() { - NSMenu.isInTest = true + MacUtils.useNSMenu = false } @Start fun start(stage: Stage) { app = FxRadioLight() app.start(stage) + this.stage = stage } @Stop @@ -116,7 +121,11 @@ class BasicFunctionalityTests { //wait until loaded sleep(2) //Avoid station names that start with # as it is query locator for ID - val stationToClick = stations.items.filter { !it.name.startsWith("#") }.take(5).random() + val stationToClick = stations.items + .filter { !it.name.startsWith("#") } + .filter { it.name != player.stationProperty.value.name } + .take(5) + .random() robot.doubleClickOn(stationToClick.name) WaitForAsyncUtils.waitForFxEvents() diff --git a/src/test/kotlin/online/hudacek/fxradio/integration/DarkModeTests.kt b/src/test/kotlin/online/hudacek/fxradio/integration/DarkModeTests.kt index 6a60ef23..daeda16e 100644 --- a/src/test/kotlin/online/hudacek/fxradio/integration/DarkModeTests.kt +++ b/src/test/kotlin/online/hudacek/fxradio/integration/DarkModeTests.kt @@ -19,6 +19,7 @@ package online.hudacek.fxradio.integration import javafx.stage.Stage import online.hudacek.fxradio.FxRadio import online.hudacek.fxradio.FxRadioDark +import online.hudacek.fxradio.util.macos.MacUtils import online.hudacek.fxradio.util.macos.NSMenu import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -35,11 +36,11 @@ class DarkModeTests { private lateinit var app: FxRadio private val nowPlayingLabel = "#nowStreaming" - private val stationsDataGrid = "#stations" + private val stationsDatarid = "#stations" @Init fun init() { - NSMenu.isInTest = true + MacUtils.useNSMenu = false } @Start From 308cc656bb8a95c967e07a48698178504cfeff27 Mon Sep 17 00:00:00 2001 From: jozef Date: Sun, 23 May 2021 11:01:30 +0200 Subject: [PATCH 05/27] Fix tests --- .../kotlin/online/hudacek/fxradio/FxRadio.kt | 10 ++- .../http/provider/DefaultClientProvider.kt | 23 ++++--- .../fxradio/api/stations/StationsApi.kt | 3 +- .../hudacek/fxradio/usecase/BaseUseCase.kt | 3 +- .../hudacek/fxradio/util/macos/MacUtils.kt | 2 - .../hudacek/fxradio/util/macos/NSMenu.kt | 4 +- .../hudacek/fxradio/util/macos/NSMenuBar.kt | 3 +- .../integration/BasicFunctionalityTests.kt | 61 ++++++++++++------- .../fxradio/integration/DarkModeTests.kt | 6 +- 9 files changed, 64 insertions(+), 51 deletions(-) diff --git a/src/main/kotlin/online/hudacek/fxradio/FxRadio.kt b/src/main/kotlin/online/hudacek/fxradio/FxRadio.kt index 1fdcb3ac..5c459276 100644 --- a/src/main/kotlin/online/hudacek/fxradio/FxRadio.kt +++ b/src/main/kotlin/online/hudacek/fxradio/FxRadio.kt @@ -81,9 +81,11 @@ open class FxRadio(val darkModeEnabled: Boolean = false, } override fun stop() { - playerViewModel.releasePlayer() - StationsApi.serviceProvider.close() - HttpClient.close() + if (!setTestEnvironment) { + playerViewModel.releasePlayer() + StationsApi.serviceProvider.close() + HttpClient.close() + } //Save last used window width/height on close of the app to use it on next start saveProperties(mapOf( @@ -100,6 +102,8 @@ open class FxRadio(val darkModeEnabled: Boolean = false, */ companion object : Component() { + var setTestEnvironment = false + const val appName = "FXRadio" const val appDesc = "Internet radio directory" const val appUrl = "https://hudacek.online/fxradio/" diff --git a/src/main/kotlin/online/hudacek/fxradio/api/http/provider/DefaultClientProvider.kt b/src/main/kotlin/online/hudacek/fxradio/api/http/provider/DefaultClientProvider.kt index c288a5e0..186fc06c 100644 --- a/src/main/kotlin/online/hudacek/fxradio/api/http/provider/DefaultClientProvider.kt +++ b/src/main/kotlin/online/hudacek/fxradio/api/http/provider/DefaultClientProvider.kt @@ -60,18 +60,17 @@ open class DefaultClientProvider : HttpClientProvider { /** * Construct http client with custom user agent, connection pool and timeouts */ - override val client: OkHttpClient by lazy { - OkHttpClient.Builder() - //The whole call should not take longer than 20 secs - .callTimeout(timeoutInSeconds, TimeUnit.SECONDS) - .addNetworkInterceptor(UserAgentInterceptor()) - .connectionPool(connectionPool) - .apply { - interceptors.forEach { - addInterceptor(it) - } - }.build() - } + override val client: OkHttpClient = + OkHttpClient.Builder() + //The whole call should not take longer than 20 secs + .callTimeout(timeoutInSeconds, TimeUnit.SECONDS) + .addNetworkInterceptor(UserAgentInterceptor()) + .connectionPool(connectionPool) + .apply { + interceptors.forEach { + addInterceptor(it) + } + }.build() /** * Correctly kill all OkHttpClient threads diff --git a/src/main/kotlin/online/hudacek/fxradio/api/stations/StationsApi.kt b/src/main/kotlin/online/hudacek/fxradio/api/stations/StationsApi.kt index cece3690..7a59064a 100644 --- a/src/main/kotlin/online/hudacek/fxradio/api/stations/StationsApi.kt +++ b/src/main/kotlin/online/hudacek/fxradio/api/stations/StationsApi.kt @@ -74,7 +74,6 @@ interface StationsApi { ApiServiceProvider("https://${viewModel.selectedProperty.value}") } - val service - get() = serviceProvider.get() + val service = serviceProvider.get() } } \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/BaseUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/BaseUseCase.kt index a34b2404..b4cd38f3 100644 --- a/src/main/kotlin/online/hudacek/fxradio/usecase/BaseUseCase.kt +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/BaseUseCase.kt @@ -30,8 +30,7 @@ abstract class BaseUseCase : Controller() { protected val appEvent: AppEvent by inject() - protected val apiService - get() = StationsApi.service + protected val apiService = StationsApi.service abstract fun execute(input: InputType): OutputType diff --git a/src/main/kotlin/online/hudacek/fxradio/util/macos/MacUtils.kt b/src/main/kotlin/online/hudacek/fxradio/util/macos/MacUtils.kt index ef8960b6..a044ca45 100644 --- a/src/main/kotlin/online/hudacek/fxradio/util/macos/MacUtils.kt +++ b/src/main/kotlin/online/hudacek/fxradio/util/macos/MacUtils.kt @@ -24,8 +24,6 @@ object MacUtils { val isMac = Platform.getCurrent() == Platform.OSX - var useNSMenu = true - /** * Shows MacOS native system notification */ diff --git a/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenu.kt b/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenu.kt index 0b5c97ea..cd910514 100644 --- a/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenu.kt +++ b/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenu.kt @@ -35,7 +35,7 @@ open class NSMenu { protected val menuToolkit: MenuToolkit by lazy { MenuToolkit.toolkit(FX.locale) } fun appMenu(menuItems: List) = menu(FxRadio.appName) { - if (MacUtils.useNSMenu) { + if (!FxRadio.setTestEnvironment) { menuToolkit.setApplicationMenu(this) items.addAll(menuItems) items.addAll( @@ -49,7 +49,7 @@ open class NSMenu { } fun windowMenu(name: String) = menu(name) { - if (MacUtils.useNSMenu) { + if (!FxRadio.setTestEnvironment) { menuToolkit.autoAddWindowMenuItems(this) items.addAll( menuToolkit.createMinimizeMenuItem(), diff --git a/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenuBar.kt b/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenuBar.kt index 4093ea41..25de4b60 100644 --- a/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenuBar.kt +++ b/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenuBar.kt @@ -17,6 +17,7 @@ package online.hudacek.fxradio.util.macos import javafx.scene.control.MenuBar +import online.hudacek.fxradio.FxRadio /** * NSMenu helper for MacOS only @@ -24,7 +25,7 @@ import javafx.scene.control.MenuBar class NSMenuBar : NSMenu() { val menuBar = MenuBar().apply { - if (MacUtils.useNSMenu) { + if (!FxRadio.setTestEnvironment) { useSystemMenuBarProperty().value = true menuToolkit.setMenuBar(this) } diff --git a/src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt b/src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt index 3fa1d33c..16bef58e 100644 --- a/src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt +++ b/src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt @@ -28,7 +28,6 @@ import online.hudacek.fxradio.api.stations.StationsApi import online.hudacek.fxradio.api.stations.model.SearchBody import online.hudacek.fxradio.api.stations.model.Station import online.hudacek.fxradio.storage.db.Tables -import online.hudacek.fxradio.util.macos.MacUtils import online.hudacek.fxradio.viewmodel.LibraryItem import online.hudacek.fxradio.viewmodel.PlayerState import online.hudacek.fxradio.viewmodel.PlayerViewModel @@ -72,12 +71,11 @@ class BasicFunctionalityTests { } //Http Client, init only once needed - private val service - get() = ApiServiceProvider("https://${Config.API.fallbackApiServerURL}").get() + private val service = ApiServiceProvider("https://${Config.API.fallbackApiServerURL}").get() @Init fun init() { - MacUtils.useNSMenu = false + FxRadio.setTestEnvironment = true } @Start @@ -94,22 +92,26 @@ class BasicFunctionalityTests { fun apiTest(robot: FxRobot) { val stations = service.getTopStations().blockingGet() Assertions.assertEquals(50, stations.size) - stations.forEach { - //top 50 stations should not have empty URL and have name - Assertions.assertTrue(it.name.isNotEmpty()) - Assertions.assertTrue(it.url_resolved != null) - } //Wait for stations to load val appStations = robot.find(stationsDataGrid) as DataGrid - Assertions.assertEquals(appStations.items.toList(), stations) + + stations.forEachIndexed { index, station -> + //top 50 stations should not have empty URL and have name + + Assertions.assertTrue(station.name.isNotEmpty()) + Assertions.assertTrue(station.url_resolved != null) + Assertions.assertEquals(stations[index].name, appStations.items[index].name) + } } @Test @Order(1) fun basicPlayPauseTest(robot: FxRobot) { + //Verify app initial state verifyThat(nowPlayingLabel, hasLabel("Streaming stopped")) + //Get Instance of player val player = find() //Wait for stations to load @@ -120,6 +122,7 @@ class BasicFunctionalityTests { //wait until loaded sleep(2) + //Avoid station names that start with # as it is query locator for ID val stationToClick = stations.items .filter { !it.name.startsWith("#") } @@ -150,6 +153,7 @@ class BasicFunctionalityTests { @Test fun testHistoryTab(robot: FxRobot) { + //Verify app initial state verifyThat(nowPlayingLabel, hasLabel("Streaming stopped")) //Wait for stations to load @@ -161,14 +165,15 @@ class BasicFunctionalityTests { //wait until loaded sleep(2) - //Find in list - val historyItem = robot.from(libraries).lookup(libraries.items[2].type.key.capitalize()).query>() + //Click on History in Library List Item + val historyItem = robot.from(libraries) + .lookup(libraries.items[2].type.key.capitalize()) + .query>() robot.clickOn(historyItem) + //Find DataGrid, History List and actual db count val stations = robot.find(stationsDataGrid) as DataGrid - val historydbCount = Tables.history.selectAll().count().blockingGet() - val stationsHistory = robot.find(stationsHistory) as ListView //Stations datagrid is not visible in history @@ -176,28 +181,32 @@ class BasicFunctionalityTests { !stations.isVisible } + //Instead, list of stations is visible waitFor(2) { stationsHistory.isVisible } + //Verify DB count equals actual list items count Assertions.assertEquals(historydbCount.toInt(), stationsHistory.items.size) } @Test fun testVolumeSliderIcons(robot: FxRobot) { + //Verify app initial state verifyThat(nowPlayingLabel, hasLabel("Streaming stopped")) //Wait for stations to load verifyThat(volumeMinIcon, visible()) verifyThat(volumeMaxIcon, visible()) + //Verify volume icon click changes the slider value robot.clickOn(volumeMinIcon) - val slider = robot.find(volumeSlider) as Slider waitFor(2) { slider.value == -30.0 } + //Verify volume icon click changes the slider value robot.clickOn(volumeMaxIcon) waitFor(2) { slider.value == 5.0 @@ -206,30 +215,36 @@ class BasicFunctionalityTests { @Test fun testSearch(robot: FxRobot) { + //Verify app initial state verifyThat(nowPlayingLabel, hasLabel("Streaming stopped")) //Verify search field is present verifyThat(search, visible()) - robot.enterText(search, "st") + //Verify short query UI + robot.enterText(search, "st") verifyThat(stationMessageHeader, visible()) verifyThat(stationMessageSubHeader, visible()) verifyThat(search, hasValue("st")) - verifyThat(stationMessageHeader, hasText("Searching Radio Directory")) + //Perform API search + val stationResults = service.searchStationByName(SearchBody("station")).blockingGet() + + //Enter query into field robot.enterText(search, "station") verifyThat(search, hasValue("station")) + //Wait until datagrid is loaded with stations for the provided searchquery + sleep(5) + + //Get stations in DataGrid val stations = robot.find(stationsDataGrid) as DataGrid verifyThat(stationMessageHeader, visible()) - //Perform API search - val stationResults = service.searchStationByName(SearchBody("station")).blockingGet() - - println("search result items: " + stations.items.size) - println("search Results: " + stationResults.size) - + //Compare results from API and APP + println("Search results displayed: " + stations.items.size) + println("Search Results from API: " + stationResults.size) Assertions.assertEquals(stationResults.size, stations.items.size) } } \ No newline at end of file diff --git a/src/test/kotlin/online/hudacek/fxradio/integration/DarkModeTests.kt b/src/test/kotlin/online/hudacek/fxradio/integration/DarkModeTests.kt index daeda16e..240bbc6a 100644 --- a/src/test/kotlin/online/hudacek/fxradio/integration/DarkModeTests.kt +++ b/src/test/kotlin/online/hudacek/fxradio/integration/DarkModeTests.kt @@ -19,8 +19,6 @@ package online.hudacek.fxradio.integration import javafx.stage.Stage import online.hudacek.fxradio.FxRadio import online.hudacek.fxradio.FxRadioDark -import online.hudacek.fxradio.util.macos.MacUtils -import online.hudacek.fxradio.util.macos.NSMenu import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.testfx.api.FxAssert @@ -36,11 +34,11 @@ class DarkModeTests { private lateinit var app: FxRadio private val nowPlayingLabel = "#nowStreaming" - private val stationsDatarid = "#stations" + private val stationsDatagrid = "#stations" @Init fun init() { - MacUtils.useNSMenu = false + FxRadio.setTestEnvironment = true } @Start From 28fa81051bda8ee24a51ef931c731d16740bf996 Mon Sep 17 00:00:00 2001 From: jozef Date: Sun, 23 May 2021 16:03:22 +0200 Subject: [PATCH 06/27] Small bugfixes --- .../hudacek/fxradio/api/http/HttpClient.kt | 30 +++++++++++-------- .../hudacek/fxradio/storage/ImageCache.kt | 17 +++++------ .../{ImageExtensions.kt => StationImage.kt} | 3 +- .../fxradio/usecase/ClearCacheUseCase.kt | 2 ++ .../hudacek/fxradio/util/macos/NSMenu.kt | 3 +- 5 files changed, 29 insertions(+), 26 deletions(-) rename src/main/kotlin/online/hudacek/fxradio/ui/{ImageExtensions.kt => StationImage.kt} (97%) diff --git a/src/main/kotlin/online/hudacek/fxradio/api/http/HttpClient.kt b/src/main/kotlin/online/hudacek/fxradio/api/http/HttpClient.kt index 6ccb0097..235126bf 100644 --- a/src/main/kotlin/online/hudacek/fxradio/api/http/HttpClient.kt +++ b/src/main/kotlin/online/hudacek/fxradio/api/http/HttpClient.kt @@ -50,20 +50,24 @@ object HttpClient { */ fun request(url: String, success: (Response) -> Unit, - fail: (IOException) -> Unit) = clientProvider.client.newCall(buildRequest(url)).enqueue( - object : Callback { - override fun onResponse(call: Call, response: Response) { - success(response) - response.close() - } + fail: (Throwable) -> Unit) = runCatching { + clientProvider.client.newCall(buildRequest(url)).enqueue( + object : Callback { + override fun onResponse(call: Call, response: Response) { + success(response) + response.close() + } - override fun onFailure(call: Call, e: IOException) { - logger.error { "Request to $url failed." } - logger.trace(e) { "Request to $url failed." } - fail(e) - } - } - ) + override fun onFailure(call: Call, e: IOException) { + logger.error { "Request to $url failed." } + logger.trace(e) { "Request to $url failed." } + fail(e) + } + }) + }.onFailure { + logger.trace(it) { "Call failed." } + fail(it) + }.isSuccess /** * Constructs [Request] object for given [url] address diff --git a/src/main/kotlin/online/hudacek/fxradio/storage/ImageCache.kt b/src/main/kotlin/online/hudacek/fxradio/storage/ImageCache.kt index 5617ebc1..cf03aaf0 100644 --- a/src/main/kotlin/online/hudacek/fxradio/storage/ImageCache.kt +++ b/src/main/kotlin/online/hudacek/fxradio/storage/ImageCache.kt @@ -22,7 +22,6 @@ import javafx.scene.image.Image import mu.KotlinLogging import online.hudacek.fxradio.Config import online.hudacek.fxradio.api.stations.model.Station -import online.hudacek.fxradio.ui.defaultRadioLogo import org.apache.commons.io.FileUtils import tornadofx.observableListOf import java.io.InputStream @@ -34,9 +33,9 @@ import java.nio.file.StandardCopyOption private val logger = KotlinLogging.logger {} /** - * Simple image cache - * Images are saved according to their station id - * and loaded using fileInputStream + * Simple image cache used for station images + * Images are saved to files with /.fxradio/cache directory + * File name is the station UUID */ object ImageCache { @@ -47,7 +46,7 @@ object ImageCache { private val invalidStationUuids = observableListOf() init { - //prepare cache directory + //Prepare cache directory if (!Files.isDirectory(cacheBasePath)) { Files.createDirectories(cacheBasePath) } @@ -80,21 +79,21 @@ object ImageCache { Single.just(Image("file:" + it.toFile().absolutePath, true)) } .doOnError { logger.error(it) { "Exception when getting station image from file!" } } - .onErrorReturnItem(defaultRadioLogo) /** * Saves [inputStream] containing logo of [station] into local cache dir */ fun save(station: Station, inputStream: InputStream): Disposable = Single.just(station) .filter { !it.isCached } - .doOnError { logger.error(it) { "Exception when saving downloaded image!" } } .map { cacheBasePath.resolve(it.stationuuid) } - .subscribe { + .subscribe({ Files.copy( inputStream, it, StandardCopyOption.REPLACE_EXISTING) - } + }, { + logger.error(it) { "Exception when saving downloaded image!" } + }) /** * Adds [station] into list of invalid stations diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/ImageExtensions.kt b/src/main/kotlin/online/hudacek/fxradio/ui/StationImage.kt similarity index 97% rename from src/main/kotlin/online/hudacek/fxradio/ui/ImageExtensions.kt rename to src/main/kotlin/online/hudacek/fxradio/ui/StationImage.kt index 1697ec08..f89a535f 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/ImageExtensions.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/StationImage.kt @@ -27,7 +27,7 @@ import online.hudacek.fxradio.storage.ImageCache import online.hudacek.fxradio.storage.ImageCache.isCached import tornadofx.onChange -val defaultRadioLogo by lazy { Image(Config.Resources.waveIcon) } +private val defaultRadioLogo by lazy { Image(Config.Resources.waveIcon) } private val logger = KotlinLogging.logger {} @@ -35,7 +35,6 @@ private val logger = KotlinLogging.logger {} * This method is used for custom downloading of station's logo * and storing it in cache directory * - * * In case of error defaultRadioLogo static png file is used as station logo */ internal fun Property.stationImage(view: ImageView) { diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/ClearCacheUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/ClearCacheUseCase.kt index 3eb96c27..6741b40a 100644 --- a/src/main/kotlin/online/hudacek/fxradio/usecase/ClearCacheUseCase.kt +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/ClearCacheUseCase.kt @@ -23,6 +23,7 @@ import org.controlsfx.glyphfont.FontAwesome import tornadofx.confirm import tornadofx.fail import tornadofx.get +import tornadofx.success /** * Clears FxRadio image cache directory @@ -37,6 +38,7 @@ class ClearCacheUseCase : BaseUseCase() { messages["cache.clear.text"].formatted(ImageCache.totalSize), owner = primaryStage) { runAsync(daemon = true) { ImageCache.clear() + } success { appEvent.appNotification.onNext( AppNotification(messages["cache.clear.ok"], FontAwesome.Glyph.CHECK)) } fail { diff --git a/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenu.kt b/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenu.kt index cd910514..14c601e8 100644 --- a/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenu.kt +++ b/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenu.kt @@ -18,7 +18,6 @@ package online.hudacek.fxradio.util.macos import de.codecentric.centerdevice.MenuToolkit import javafx.scene.control.MenuItem -import javafx.scene.control.SeparatorMenuItem import online.hudacek.fxradio.FxRadio import online.hudacek.fxradio.ui.menu.menu import online.hudacek.fxradio.ui.menu.separator @@ -55,7 +54,7 @@ open class NSMenu { menuToolkit.createMinimizeMenuItem(), menuToolkit.createZoomMenuItem(), menuToolkit.createCycleWindowsItem(), - SeparatorMenuItem(), + separator(), menuToolkit.createBringAllToFrontItem()) } } From 20d5d22cb3855216578a0f6f5e593e054f8ffa60 Mon Sep 17 00:00:00 2001 From: Jozef <26581376+Joseph5610@users.noreply.github.com> Date: Wed, 26 May 2021 19:51:31 +0200 Subject: [PATCH 07/27] Create detekt-analysis.yml --- .github/workflows/detekt-analysis.yml | 101 ++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 .github/workflows/detekt-analysis.yml diff --git a/.github/workflows/detekt-analysis.yml b/.github/workflows/detekt-analysis.yml new file mode 100644 index 00000000..7b5ef5fc --- /dev/null +++ b/.github/workflows/detekt-analysis.yml @@ -0,0 +1,101 @@ +# This workflow performs a static analysis of your Kotlin source code using +# Detekt. +# +# Scans are triggered: +# 1. On every push to default and protected branches +# 2. On every Pull Request targeting the default branch +# 3. On a weekly schedule +# 4. Manually, on demand, via the "workflow_dispatch" event +# +# The workflow should work with no modifications, but you might like to use a +# later version of the Detekt CLI by modifing the $DETEKT_RELEASE_TAG +# environment variable. +name: Scan with Detekt + +on: + # Triggers the workflow on push or pull request events but only for default and protected branches + push: + branches: [ master, develop ] + pull_request: + branches: [ master ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +env: + # Release tag associated with version of Detekt to be installed + # SARIF support (required for this workflow) was introduced in Detekt v1.15.0 + DETEKT_RELEASE_TAG: v1.15.0 + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "scan" + scan: + name: Scan + # The type of runner that the job will run on + runs-on: macos-10.15 + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + + # Gets the download URL associated with the $DETEKT_RELEASE_TAG + - name: Get Detekt download URL + id: detekt_info + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + DETEKT_DOWNLOAD_URL=$( gh api graphql --field tagName=$DETEKT_RELEASE_TAG --raw-field query=' + query getReleaseAssetDownloadUrl($tagName: String!) { + repository(name: "detekt", owner: "detekt") { + release(tagName: $tagName) { + releaseAssets(name: "detekt", first: 1) { + nodes { + downloadUrl + } + } + } + } + } + ' | \ + jq --raw-output '.data.repository.release.releaseAssets.nodes[0].downloadUrl' ) + echo "::set-output name=download_url::$DETEKT_DOWNLOAD_URL" + + # Sets up the detekt cli + - name: Setup Detekt + run: | + dest=$( mktemp -d ) + curl --request GET \ + --url ${{ steps.detekt_info.outputs.download_url }} \ + --silent \ + --location \ + --output $dest/detekt + chmod a+x $dest/detekt + echo $dest >> $GITHUB_PATH + + # Performs static analysis using Detekt + - name: Run Detekt + continue-on-error: true + run: | + detekt --input ${{ github.workspace }} --report sarif:${{ github.workspace }}/detekt.sarif.json + + # Modifies the SARIF output produced by Detekt so that absolute URIs are relative + # This is so we can easily map results onto their source files + # This can be removed once relative URI support lands in Detekt: https://git.io/JLBbA + - name: Make artifact location URIs relative + continue-on-error: true + run: | + echo "$( + jq \ + --arg github_workspace ${{ github.workspace }} \ + '. | ( .runs[].results[].locations[].physicalLocation.artifactLocation.uri |= if test($github_workspace) then .[($github_workspace | length | . + 1):] else . end )' \ + ${{ github.workspace }}/detekt.sarif.json + )" > ${{ github.workspace }}/detekt.sarif.json + + # Uploads results to GitHub repository using the upload-sarif action + - uses: github/codeql-action/upload-sarif@v1 + with: + # Path to SARIF file relative to the root of the repository + sarif_file: ${{ github.workspace }}/detekt.sarif.json + checkout_path: ${{ github.workspace }} From bbc9ae995976792a148c111edac77a311accadd9 Mon Sep 17 00:00:00 2001 From: jozef Date: Sat, 29 May 2021 00:59:29 +0200 Subject: [PATCH 08/27] Small bugfixes + UI improvements --- build.gradle | 1 + .../kotlin/online/hudacek/fxradio/FxRadio.kt | 4 +- .../hudacek/fxradio/media/MediaPlayer.kt | 2 +- .../storage/db/PinnedCountriesTable.kt | 4 - .../fxradio/storage/db/StationTable.kt | 4 - .../hudacek/fxradio/storage/db/Tables.kt | 1 + .../fxradio/ui/fragment/AboutFragment.kt | 18 +-- .../ui/fragment/AttributionsFragment.kt | 4 +- .../hudacek/fxradio/ui/fragment/Modals.kt | 49 -------- .../fxradio/ui/fragment/ServersFragment.kt | 5 +- .../ui/fragment/StationInfoFragment.kt | 116 ++++++++++-------- .../hudacek/fxradio/ui/menu/AboutMenu.kt | 8 +- .../hudacek/fxradio/ui/menu/HelpMenu.kt | 6 +- .../hudacek/fxradio/ui/menu/HistoryMenu.kt | 2 +- .../hudacek/fxradio/ui/menu/StationMenu.kt | 8 +- .../online/hudacek/fxradio/ui/style/Colors.kt | 4 +- .../online/hudacek/fxradio/ui/style/Styles.kt | 18 +++ .../fxradio/ui/view/player/PlayerView.kt | 11 +- .../ui/view/stations/StationsDataGridView.kt | 10 +- .../ui/view/stations/StationsHistoryView.kt | 2 +- .../online/hudacek/fxradio/util/Modal.kt | 48 ++++++++ .../online/hudacek/fxradio/util/Properties.kt | 5 +- .../hudacek/fxradio/util/macos/NSMenu.kt | 4 +- .../hudacek/fxradio/util/macos/NSMenuBar.kt | 2 +- .../fxradio/viewmodel/HistoryViewModel.kt | 3 +- .../fxradio/viewmodel/SearchViewModel.kt | 8 +- src/main/resources/Messages.properties | 8 +- .../integration/BasicFunctionalityTests.kt | 2 +- .../fxradio/integration/DarkModeTests.kt | 2 +- 29 files changed, 198 insertions(+), 161 deletions(-) delete mode 100644 src/main/kotlin/online/hudacek/fxradio/ui/fragment/Modals.kt create mode 100644 src/main/kotlin/online/hudacek/fxradio/util/Modal.kt diff --git a/build.gradle b/build.gradle index 51ab9a44..03546e7d 100644 --- a/build.gradle +++ b/build.gradle @@ -97,6 +97,7 @@ dependencies { testCompile "org.testfx:testfx-core:$versions.testfx" testCompile "org.testfx:testfx-junit5:$versions.testfx" testCompile "org.awaitility:awaitility:$versions.awaitility" + testCompile "org.testfx:openjfx-monocle:8u76-b04" } sourceSets { diff --git a/src/main/kotlin/online/hudacek/fxradio/FxRadio.kt b/src/main/kotlin/online/hudacek/fxradio/FxRadio.kt index 5c459276..d14d1cdf 100644 --- a/src/main/kotlin/online/hudacek/fxradio/FxRadio.kt +++ b/src/main/kotlin/online/hudacek/fxradio/FxRadio.kt @@ -81,7 +81,7 @@ open class FxRadio(val darkModeEnabled: Boolean = false, } override fun stop() { - if (!setTestEnvironment) { + if (!isTestEnvironment) { playerViewModel.releasePlayer() StationsApi.serviceProvider.close() HttpClient.close() @@ -102,7 +102,7 @@ open class FxRadio(val darkModeEnabled: Boolean = false, */ companion object : Component() { - var setTestEnvironment = false + var isTestEnvironment = false const val appName = "FXRadio" const val appDesc = "Internet radio directory" diff --git a/src/main/kotlin/online/hudacek/fxradio/media/MediaPlayer.kt b/src/main/kotlin/online/hudacek/fxradio/media/MediaPlayer.kt index 86ac507a..618caa69 100644 --- a/src/main/kotlin/online/hudacek/fxradio/media/MediaPlayer.kt +++ b/src/main/kotlin/online/hudacek/fxradio/media/MediaPlayer.kt @@ -50,6 +50,6 @@ interface MediaPlayer { companion object { //The metadata service can be disabled by respective property file setting - val enableMetaDataService = Properties.PlayerRefreshMetaData.value(true) + val enableMetaDataService = Properties.PlayerMetaDataRefresh.value(true) } } \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/storage/db/PinnedCountriesTable.kt b/src/main/kotlin/online/hudacek/fxradio/storage/db/PinnedCountriesTable.kt index 04ebd4bc..52b010ee 100644 --- a/src/main/kotlin/online/hudacek/fxradio/storage/db/PinnedCountriesTable.kt +++ b/src/main/kotlin/online/hudacek/fxradio/storage/db/PinnedCountriesTable.kt @@ -18,11 +18,8 @@ package online.hudacek.fxradio.storage.db import io.reactivex.Observable import io.reactivex.Single -import mu.KotlinLogging import online.hudacek.fxradio.api.stations.model.Country -private val logger = KotlinLogging.logger {} - class PinnedCountriesTable(override val tableName: String = "PINNED") : Table, Database(tableName) { override val createTableSql = "CREATE TABLE IF NOT EXISTS $tableName (ID INTEGER PRIMARY KEY," + @@ -35,7 +32,6 @@ class PinnedCountriesTable(override val tableName: String = "PINNED") : Table = removeAllQuery().toSingle() diff --git a/src/main/kotlin/online/hudacek/fxradio/storage/db/StationTable.kt b/src/main/kotlin/online/hudacek/fxradio/storage/db/StationTable.kt index d976a6cb..bbbd81c0 100644 --- a/src/main/kotlin/online/hudacek/fxradio/storage/db/StationTable.kt +++ b/src/main/kotlin/online/hudacek/fxradio/storage/db/StationTable.kt @@ -18,11 +18,8 @@ package online.hudacek.fxradio.storage.db import io.reactivex.Observable import io.reactivex.Single -import mu.KotlinLogging import online.hudacek.fxradio.api.stations.model.Station -private val logger = KotlinLogging.logger {} - /** * Common operations on database of stations with different tables (e.g History, Favourites ..) */ @@ -50,7 +47,6 @@ class StationTable(override val tableName: String) : Table, Database(ta it.getString("codec"), it.getInt("bitrate")) } - .doOnError { logger.error(it) { "Exception when retrieving data from $tableName" } } override fun removeAll(): Single = removeAllQuery().toSingle() diff --git a/src/main/kotlin/online/hudacek/fxradio/storage/db/Tables.kt b/src/main/kotlin/online/hudacek/fxradio/storage/db/Tables.kt index 0cc9deb4..ddd5a76e 100644 --- a/src/main/kotlin/online/hudacek/fxradio/storage/db/Tables.kt +++ b/src/main/kotlin/online/hudacek/fxradio/storage/db/Tables.kt @@ -20,6 +20,7 @@ package online.hudacek.fxradio.storage.db * Object that holds kotlin classes representing SQLite tables */ object Tables { + /** * Table for stations saved as favourites */ diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/fragment/AboutFragment.kt b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/AboutFragment.kt index 598f5a96..6347406d 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/fragment/AboutFragment.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/AboutFragment.kt @@ -21,9 +21,9 @@ import online.hudacek.fxradio.Config import online.hudacek.fxradio.FxRadio import online.hudacek.fxradio.ui.BaseFragment import online.hudacek.fxradio.ui.style.Styles +import online.hudacek.fxradio.util.Modal +import online.hudacek.fxradio.util.open import tornadofx.* -import tornadofx.controlsfx.right -import tornadofx.controlsfx.statusbar /*** * Simple Information about the app @@ -59,15 +59,17 @@ class AboutFragment : BaseFragment(FxRadio.appDesc) { } } - statusbar { - right { - hyperlink(messages["menu.app.attributions"]) { - action { - Modals.Attributions.open() - } + vbox { + paddingAll = 10.0 + alignment = Pos.CENTER_RIGHT + + hyperlink(messages["menu.app.attributions"]) { + action { + Modal.Attributions.open() } } } + addClass(Styles.backgroundWhiteSmoke) } } \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/fragment/AttributionsFragment.kt b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/AttributionsFragment.kt index 691b8953..64f347c8 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/fragment/AttributionsFragment.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/AttributionsFragment.kt @@ -23,6 +23,8 @@ import online.hudacek.fxradio.ui.BaseFragment import online.hudacek.fxradio.ui.requestFocusOnSceneAvailable import online.hudacek.fxradio.ui.showWhen import online.hudacek.fxradio.ui.style.Styles +import online.hudacek.fxradio.util.Modal +import online.hudacek.fxradio.util.open import online.hudacek.fxradio.viewmodel.Attribution import online.hudacek.fxradio.viewmodel.AttributionViewModel import online.hudacek.fxradio.viewmodel.Attributions @@ -47,7 +49,7 @@ class AttributionsFragment : BaseFragment() { readonlyColumn(messages["attributions.version"], Attribution::version) onUserSelect { - Modals.License.open() + Modal.License.open() } } } diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/fragment/Modals.kt b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/Modals.kt deleted file mode 100644 index 65147d6d..00000000 --- a/src/main/kotlin/online/hudacek/fxradio/ui/fragment/Modals.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2020 FXRadio by hudacek.online - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package online.hudacek.fxradio.ui.fragment - -import javafx.stage.StageStyle -import tornadofx.Fragment -import tornadofx.find -import kotlin.reflect.full.createInstance - -/** - * Generic modal dialog helper - * Type parameter should represent the fragment class loaded into the modal window - */ -sealed class Modals(val style: StageStyle = StageStyle.UTILITY, - val resizable: Boolean = false) { - object StationInfo : Modals() - object AddNewStation : Modals() - object About : Modals(resizable = true) - object Servers : Modals(resizable = true) - object Stats : Modals() - object Attributions : Modals() - object StationDebug : Modals() - object License : Modals() -} - -/** - * Finds and opens modal window for [Modals] - */ -internal inline fun Modals.open() = find().openModal( - stageStyle = style, - resizable = resizable) - -internal inline fun Modals.new() = T::class.createInstance().openModal( - stageStyle = style, - resizable = resizable) diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/fragment/ServersFragment.kt b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/ServersFragment.kt index 7b380180..437510a5 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/fragment/ServersFragment.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/ServersFragment.kt @@ -61,7 +61,7 @@ class ServersFragment : BaseFragment() { override val root = vbox { title = messages["menu.app.server"] paddingAll = 10.0 - setPrefSize(300.0, 250.0) + setPrefSize(350.0, 250.0) vbox { vbox(alignment = Pos.CENTER) { @@ -70,9 +70,8 @@ class ServersFragment : BaseFragment() { addClass(Styles.header) } - text(messages["servers.restartNeeded"]) { + label(messages["servers.restartNeeded"]) { paddingAll = 5.0 - wrappingWidth = 270.0 textAlignment = TextAlignment.CENTER } } diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/fragment/StationInfoFragment.kt b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/StationInfoFragment.kt index 0dca1e27..7175adbf 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/fragment/StationInfoFragment.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/StationInfoFragment.kt @@ -22,6 +22,7 @@ import javafx.beans.property.StringProperty import javafx.geometry.Pos import javafx.scene.control.Label import javafx.scene.effect.DropShadow +import javafx.scene.layout.Priority import javafx.scene.paint.Color import online.hudacek.fxradio.ui.* import online.hudacek.fxradio.ui.style.Styles @@ -29,9 +30,6 @@ import online.hudacek.fxradio.viewmodel.PlayerViewModel import online.hudacek.fxradio.viewmodel.StationInfo import online.hudacek.fxradio.viewmodel.StationInfoViewModel import tornadofx.* -import tornadofx.controlsfx.left -import tornadofx.controlsfx.right -import tornadofx.controlsfx.statusbar class StationInfoFragment : BaseFragment() { @@ -43,10 +41,9 @@ class StationInfoFragment : BaseFragment() { } override val root = vbox { - prefWidth = 300.0 - titleProperty.bind(viewModel.nameProperty) + prefWidth = 400.0 - vbox { + hbox(10) { vbox(alignment = Pos.CENTER) { paddingAll = 10.0 imageview { @@ -57,50 +54,12 @@ class StationInfoFragment : BaseFragment() { isPreserveRatio = true } } - } - - flowpane { - hgap = 5.0 - vgap = 5.0 - alignment = Pos.CENTER - paddingAll = 5.0 - - createInfoLabel("info.codec", viewModel.codecProperty)?.let { add(it) } - createInfoLabel("info.bitrate", viewModel.bitrateProperty)?.let { add(it) } - createInfoLabel("info.language", viewModel.languageProperty)?.let { add(it) } - createInfoLabel("info.country", viewModel.countryProperty)?.let { add(it) } - createInfoLabel("info.votes", viewModel.votesProperty)?.let { add(it) } - } - - flowpane { - hgap = 5.0 - vgap = 5.0 - alignment = Pos.CENTER - paddingAll = 5.0 - - viewModel.tagsProperty.forEach { - label(it) { - addClass(Styles.tag) - addClass(Styles.grayLabel) - copyMenu(clipboard, - name = messages["copy"], - value = it) + vbox { + alignment = Pos.CENTER + label(viewModel.nameProperty) { + addClass(Styles.subheader) } - } - } - - statusbar { - left { - hyperlink(messages["menu.station.vote"]) { - actionEvents() - .map { viewModel.stationProperty.value } - .subscribe(appEvent.addVote) - - addClass(Styles.primaryTextColor) - } - } - right { hyperlink(viewModel.homePageProperty) { action { app.openUrl(text) @@ -112,12 +71,69 @@ class StationInfoFragment : BaseFragment() { showWhen { viewModel.homePageProperty.isNotEmpty } + } - addClass(Styles.primaryTextColor) + flowpane { + hgap = 5.0 + vgap = 5.0 + alignment = Pos.CENTER + paddingAll = 5.0 + + viewModel.tagsProperty.forEach { + label(it) { + addClass(Styles.tag) + addClass(Styles.grayLabel) + copyMenu(clipboard, + name = messages["copy"], + value = it) + } + } } } } - addClass(Styles.backgroundWhiteSmoke) + + vbox { + flowpane { + hgap = 5.0 + vgap = 5.0 + alignment = Pos.CENTER + paddingAll = 5.0 + + createInfoLabel("info.codec", viewModel.codecProperty)?.let { add(it) } + createInfoLabel("info.bitrate", viewModel.bitrateProperty)?.let { add(it) } + createInfoLabel("info.language", viewModel.languageProperty)?.let { add(it) } + createInfoLabel("info.country", viewModel.countryProperty)?.let { add(it) } + createInfoLabel("info.votes", viewModel.votesProperty)?.let { add(it) } + } + } + + + hbox { + button(messages["menu.station.vote"]) { + actionEvents() + .map { viewModel.stationProperty.value } + .subscribe(appEvent.addVote) + + addClass(Styles.primaryButton) + } + region { + hgrow = Priority.ALWAYS + } + + vbox { + alignment = Pos.CENTER_RIGHT + button(messages["close"]) { + action { + close() + } + } + } + } + + style { + paddingAll = 8 + backgroundColor += Color.WHITESMOKE + } } private fun createInfoLabel(key: String, valueProperty: StringProperty): Label? { diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/menu/AboutMenu.kt b/src/main/kotlin/online/hudacek/fxradio/ui/menu/AboutMenu.kt index 6352bbc1..4b86ee19 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/menu/AboutMenu.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/menu/AboutMenu.kt @@ -18,8 +18,8 @@ package online.hudacek.fxradio.ui.menu import javafx.scene.control.MenuItem import online.hudacek.fxradio.FxRadio -import online.hudacek.fxradio.ui.fragment.Modals -import online.hudacek.fxradio.ui.fragment.open +import online.hudacek.fxradio.util.Modal +import online.hudacek.fxradio.util.open import tornadofx.action import tornadofx.get @@ -29,13 +29,13 @@ class AboutMenu : BaseMenu(FxRadio.appName) { get() = listOf( item(messages["menu.app.about"] + " " + FxRadio.appName) { action { - Modals.About.open() + Modal.About.open() } }, separator(), item(messages["menu.app.server"]) { action { - Modals.Servers.open() + Modal.Servers.open() } } ) diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/menu/HelpMenu.kt b/src/main/kotlin/online/hudacek/fxradio/ui/menu/HelpMenu.kt index b3fbcead..b3008c5f 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/menu/HelpMenu.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/menu/HelpMenu.kt @@ -17,9 +17,9 @@ package online.hudacek.fxradio.ui.menu import online.hudacek.fxradio.Config -import online.hudacek.fxradio.ui.fragment.Modals -import online.hudacek.fxradio.ui.fragment.open import online.hudacek.fxradio.ui.openUrl +import online.hudacek.fxradio.util.Modal +import online.hudacek.fxradio.util.open import tornadofx.action import tornadofx.get @@ -38,7 +38,7 @@ class HelpMenu : BaseMenu("menu.help") { separator(), item(messages["menu.help.stats"]) { action { - Modals.Stats.open() + Modal.Stats.open() } }, item(messages["menu.help.clearCache"]) { diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/menu/HistoryMenu.kt b/src/main/kotlin/online/hudacek/fxradio/ui/menu/HistoryMenu.kt index b8ad8fb2..f45ff621 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/menu/HistoryMenu.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/menu/HistoryMenu.kt @@ -41,7 +41,7 @@ class HistoryMenu : BaseMenu("menu.history") { historyViewModel.stationsProperty.emptyProperty() } items.bind(historyViewModel.stationsProperty) { - item("${it.name} (${it.countrycode})") { + item(it.name) { //for some reason macos native menu does not respect //width/height setting so it is disabled for now if (!appMenuViewModel.usePlatformProperty.value) { diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/menu/StationMenu.kt b/src/main/kotlin/online/hudacek/fxradio/ui/menu/StationMenu.kt index 05785d6a..b1e00402 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/menu/StationMenu.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/menu/StationMenu.kt @@ -16,9 +16,9 @@ package online.hudacek.fxradio.ui.menu -import online.hudacek.fxradio.ui.fragment.Modals -import online.hudacek.fxradio.ui.fragment.open import online.hudacek.fxradio.ui.update +import online.hudacek.fxradio.util.Modal +import online.hudacek.fxradio.util.open import online.hudacek.fxradio.viewmodel.PlayerViewModel import tornadofx.action import tornadofx.get @@ -31,7 +31,7 @@ class StationMenu : BaseMenu("menu.station") { item(messages["menu.station.info"], KeyCodes.info) { disableWhenInvalidStation(playerViewModel.stationProperty) action { - Modals.StationInfo.open() + Modal.StationInfo.open() } }, item(messages["copy.stream.url"]) { @@ -43,7 +43,7 @@ class StationMenu : BaseMenu("menu.station") { separator(), item(messages["menu.station.add"], KeyCodes.add) { action { - Modals.AddNewStation.open() + Modal.AddNewStation.open() } } ) diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/style/Colors.kt b/src/main/kotlin/online/hudacek/fxradio/ui/style/Colors.kt index a66af487..00a12628 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/style/Colors.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/style/Colors.kt @@ -25,8 +25,8 @@ object Colors { } open class ColorValues { - open val primary = "#0097CE" - open val primaryHover = "#0097EA" + open val primary = "#d65458" + open val primaryHover = "#ff696b" open val background = "#E9E9E9" open val backgroundBorder = "#E8E8E8" diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/style/Styles.kt b/src/main/kotlin/online/hudacek/fxradio/ui/style/Styles.kt index df2f8fc2..625ca186 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/style/Styles.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/style/Styles.kt @@ -136,6 +136,19 @@ class Styles : Stylesheet() { textFill = c(colors.grayLabel) } + button { + prefWidth = 75.px + fontSize = 12.px + backgroundRadius += box(6.px) + borderRadius += box(6.px) + padding = box(4.px, 10.px, 4.px, 10.px) + textFill = c(colors.label) + + and(focused) { + backgroundColor += c(colors.backgroundBorder) + } + } + primaryButton { backgroundColor += c(colors.primary) textFill = Color.WHITESMOKE @@ -148,6 +161,11 @@ class Styles : Stylesheet() { textFill = c(colors.primary) } + hyperlink { + textFill = c(colors.primary) + borderColor += box(c(colors.primary)) + } + libraryListView { backgroundColor += Color.WHITESMOKE borderColor += box(Color.WHITESMOKE) diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/view/player/PlayerView.kt b/src/main/kotlin/online/hudacek/fxradio/ui/view/player/PlayerView.kt index 4f459db8..31b5abf5 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/view/player/PlayerView.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/view/player/PlayerView.kt @@ -30,6 +30,7 @@ import online.hudacek.fxradio.viewmodel.PlayerState import online.hudacek.fxradio.viewmodel.PlayerViewModel import org.controlsfx.glyphfont.FontAwesome import tornadofx.* +import tornadofx.controlsfx.glyph /** * Main player view above stations @@ -57,7 +58,7 @@ class PlayerView : BaseView() { } private val playerControls by lazy { - button { + glyph { id = "playerControls" graphicProperty().bind(playerControlsBinding) requestFocusOnSceneAvailable() @@ -66,7 +67,7 @@ class PlayerView : BaseView() { it == null || !it.isValid() } } - action { + setOnMouseClicked { viewModel.togglePlayerState() } addClass(Styles.playerControls) @@ -110,7 +111,7 @@ class PlayerView : BaseView() { //Station info box add(playerStationView) - button { + glyph { id = "playRandomStation" graphic = randomStationGlyph tooltip(messages["player.playRandomStation"]) @@ -136,7 +137,7 @@ class PlayerView : BaseView() { hbox { paddingRight = 30.0 alignment = Pos.CENTER_LEFT - button { + glyph { id = "volumeMinIcon" graphic = volumeDownGlyph onLeftClick { @@ -145,7 +146,7 @@ class PlayerView : BaseView() { addClass(Styles.playerControls) } add(volumeSlider) - button { + glyph { id = "volumeMaxIcon" graphic = volumeUpGlyph minWidth = 20.0 diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsDataGridView.kt b/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsDataGridView.kt index dae78e52..c9fe8c4b 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsDataGridView.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsDataGridView.kt @@ -25,13 +25,13 @@ import javafx.scene.paint.Color import online.hudacek.fxradio.Config import online.hudacek.fxradio.api.stations.model.tagsSplit import online.hudacek.fxradio.ui.BaseView -import online.hudacek.fxradio.ui.fragment.Modals -import online.hudacek.fxradio.ui.fragment.new -import online.hudacek.fxradio.ui.fragment.open import online.hudacek.fxradio.ui.menu.FavouritesMenu import online.hudacek.fxradio.ui.showWhen import online.hudacek.fxradio.ui.smallLabel import online.hudacek.fxradio.ui.stationImage +import online.hudacek.fxradio.util.Modal +import online.hudacek.fxradio.util.new +import online.hudacek.fxradio.util.open import online.hudacek.fxradio.viewmodel.* import tornadofx.* @@ -67,7 +67,7 @@ class StationsDataGridView : BaseView() { vbox { contextmenu { item(messages["menu.station.info"]).action { - Modals.StationInfo.new() + Modal.StationInfo.new() } separator() @@ -85,7 +85,7 @@ class StationsDataGridView : BaseView() { if (Config.Flags.enableStationDebug) { separator() item("Station Debug Info").action { - Modals.StationDebug.open() + Modal.StationDebug.open() } } } diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsHistoryView.kt b/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsHistoryView.kt index 66bc5134..145e3de5 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsHistoryView.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsHistoryView.kt @@ -37,6 +37,7 @@ class StationsHistoryView : BaseView() { private val libraryViewModel: LibraryViewModel by inject() private val playerViewModel: PlayerViewModel by inject() + override val root = listview(historyViewModel.stationsProperty) { id = "stationsHistoryList" cellFormat { @@ -57,7 +58,6 @@ class StationsHistoryView : BaseView() { onUserSelect(1) { playerViewModel.stationProperty.value = it } - addClass(Styles.historyListItem) } diff --git a/src/main/kotlin/online/hudacek/fxradio/util/Modal.kt b/src/main/kotlin/online/hudacek/fxradio/util/Modal.kt new file mode 100644 index 00000000..9f146e67 --- /dev/null +++ b/src/main/kotlin/online/hudacek/fxradio/util/Modal.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2020 FXRadio by hudacek.online + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package online.hudacek.fxradio.util + +import javafx.stage.StageStyle +import online.hudacek.fxradio.ui.fragment.* +import tornadofx.Fragment +import tornadofx.find +import kotlin.reflect.full.createInstance + +/** + * Generic modal dialog helper + * Type parameter should represent the fragment class loaded into the modal window + */ +sealed class Modal(val style: StageStyle = StageStyle.UTILITY, + val resizable: Boolean = false) { + object StationInfo : Modal(style = StageStyle.UNDECORATED) + object AddNewStation : Modal() + object About : Modal(resizable = true) + object Servers : Modal(resizable = true) + object Stats : Modal() + object Attributions : Modal() + object StationDebug : Modal() + object License : Modal() +} + +/** + * Finds and opens modal window for [Modal] + */ +internal inline fun Modal.open() = find().openModal( + stageStyle = style, resizable = resizable) + +internal inline fun Modal.new() = T::class.createInstance().openModal( + stageStyle = style, resizable = resizable) diff --git a/src/main/kotlin/online/hudacek/fxradio/util/Properties.kt b/src/main/kotlin/online/hudacek/fxradio/util/Properties.kt index 68e190e5..bfefedba 100644 --- a/src/main/kotlin/online/hudacek/fxradio/util/Properties.kt +++ b/src/main/kotlin/online/hudacek/fxradio/util/Properties.kt @@ -29,7 +29,7 @@ enum class Properties(val key: String) { Volume("player.volume"), Player("player.type"), PlayerAnimated("player.animate"), - PlayerRefreshMetaData("player.refreshMeta"), + PlayerMetaDataRefresh("player.refreshMeta"), ApiServer("app.server"), SearchQuery("search.query"), SendOsNotifications("notifications"), @@ -53,6 +53,7 @@ class Property(property: Properties) : Component() { val key = property.key val isPresent: Boolean + //runCatching handles situations where config or key fields are throwing NPE get() = runCatching { app.config.keys.any { it == key } }.getOrDefault(false) inline fun get(): T { @@ -62,7 +63,7 @@ class Property(property: Properties) : Component() { Int::class -> app.config.int(key) as T String::class -> app.config.string(key) as T else -> { - throw IllegalArgumentException("${T::class} is not supported argument") + throw IllegalArgumentException("${T::class} is not supported argument!") } } } diff --git a/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenu.kt b/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenu.kt index 14c601e8..94ff0601 100644 --- a/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenu.kt +++ b/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenu.kt @@ -34,7 +34,7 @@ open class NSMenu { protected val menuToolkit: MenuToolkit by lazy { MenuToolkit.toolkit(FX.locale) } fun appMenu(menuItems: List) = menu(FxRadio.appName) { - if (!FxRadio.setTestEnvironment) { + if (!FxRadio.isTestEnvironment) { menuToolkit.setApplicationMenu(this) items.addAll(menuItems) items.addAll( @@ -48,7 +48,7 @@ open class NSMenu { } fun windowMenu(name: String) = menu(name) { - if (!FxRadio.setTestEnvironment) { + if (!FxRadio.isTestEnvironment) { menuToolkit.autoAddWindowMenuItems(this) items.addAll( menuToolkit.createMinimizeMenuItem(), diff --git a/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenuBar.kt b/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenuBar.kt index 25de4b60..a7fd8388 100644 --- a/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenuBar.kt +++ b/src/main/kotlin/online/hudacek/fxradio/util/macos/NSMenuBar.kt @@ -25,7 +25,7 @@ import online.hudacek.fxradio.FxRadio class NSMenuBar : NSMenu() { val menuBar = MenuBar().apply { - if (!FxRadio.setTestEnvironment) { + if (!FxRadio.isTestEnvironment) { useSystemMenuBarProperty().value = true menuToolkit.setMenuBar(this) } diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/HistoryViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/HistoryViewModel.kt index 7508046d..37e74cc4 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/HistoryViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/HistoryViewModel.kt @@ -48,9 +48,10 @@ class HistoryViewModel : BaseViewModel(History()) { //Add currently listened station to history appEvent.addToHistory //Add only valid stations not already present in history - .filter { it.isValid() } + .filter { it.isValid() && it !in stationsProperty } .doOnError { logger.error(it) { "Exception when adding station to history!" } } .flatMapSingle { Tables.history.insert(it) } + .doOnEach { println("new stuff") } .subscribe { stationsProperty += it } diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/SearchViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/SearchViewModel.kt index 04c560e7..c3260fe7 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/SearchViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/SearchViewModel.kt @@ -47,13 +47,17 @@ class SearchViewModel : BaseViewModel(Search()) { val queryChanges: Observable = bindQueryProperty .toObservableChangesNonNull() .map { it.newVal } - .map { if (it.length > 50) it.substring(0, 50).trim() else it.trim() } + .map { if (it.length > maxQueryLength) it.substring(0, maxQueryLength).trim() else it.trim() } val queryBinding = bindQueryProperty.toObservable() - .map { if (it.length > 50) it.substring(0, 50).trim() else it.trim() } + .map { if (it.length > maxQueryLength) it.substring(0, maxQueryLength).trim() else it.trim() } .toBinding() fun search() = searchUseCase.execute(searchByTagProperty.value to queryBinding.value) override fun onCommit() = Properties.SearchQuery.save(bindQueryProperty.value) + + companion object { + private const val maxQueryLength = 50 + } } \ No newline at end of file diff --git a/src/main/resources/Messages.properties b/src/main/resources/Messages.properties index 11b787a5..22f99b9a 100644 --- a/src/main/resources/Messages.properties +++ b/src/main/resources/Messages.properties @@ -44,7 +44,7 @@ menu.station.favouriteRemove=Remove from favourites menu.station.favouriteRemove.error=There was an error removing favourite. menu.station.favouriteRemoved={0} was removed from favourites menu.station.favourite.clear=Clear favourites... -menu.station.vote=Vote for the station +menu.station.vote=Add vote menu.station=_Station menu.station.info=Get info menu.help=_Help @@ -115,7 +115,7 @@ add.invalid.file=Invalid file selected! add.favourites=Also save to my favourites library add.success=Station has been added! It might take a significant time to show in the list. add.error=There was an error when adding the station. -add.cleanupForm=Clear form +add.cleanupForm=Clear info.country=Country info.language=Language info.votes=Votes @@ -128,8 +128,8 @@ servers.title=Available API servers servers.selected=selected servers.error=Error when getting servers:\n servers.notAvailable=No available servers right now. Try again later. -servers.reload=Reload list -servers.restartNeeded=Selected server will be applied after restart of the app! +servers.reload=Reload +servers.restartNeeded=Selected server will be applied after restart of the app. server.save.ok=Server selected. Please restart the app to apply changes. server.save.error=Can't save selected server. about.datasource=Data provided by http://radio-browser.info public API diff --git a/src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt b/src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt index 16bef58e..7f5db312 100644 --- a/src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt +++ b/src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt @@ -75,7 +75,7 @@ class BasicFunctionalityTests { @Init fun init() { - FxRadio.setTestEnvironment = true + FxRadio.isTestEnvironment = true } @Start diff --git a/src/test/kotlin/online/hudacek/fxradio/integration/DarkModeTests.kt b/src/test/kotlin/online/hudacek/fxradio/integration/DarkModeTests.kt index 240bbc6a..f03998e7 100644 --- a/src/test/kotlin/online/hudacek/fxradio/integration/DarkModeTests.kt +++ b/src/test/kotlin/online/hudacek/fxradio/integration/DarkModeTests.kt @@ -38,7 +38,7 @@ class DarkModeTests { @Init fun init() { - FxRadio.setTestEnvironment = true + FxRadio.isTestEnvironment = true } @Start From e8bf036414caa4f26d747c1bc74eed9c38b4fb1b Mon Sep 17 00:00:00 2001 From: jozef Date: Sat, 29 May 2021 01:17:04 +0200 Subject: [PATCH 09/27] Small bugfixes + UI improvements --- .../online/hudacek/fxradio/ui/style/Styles.kt | 4 ---- .../hudacek/fxradio/ui/view/player/PlayerView.kt | 16 +++++----------- .../fxradio/viewmodel/HistoryViewModel.kt | 1 - .../hudacek/fxradio/viewmodel/PlayerViewModel.kt | 6 ++++-- 4 files changed, 9 insertions(+), 18 deletions(-) diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/style/Styles.kt b/src/main/kotlin/online/hudacek/fxradio/ui/style/Styles.kt index 625ca186..87f0aa3b 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/style/Styles.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/style/Styles.kt @@ -143,10 +143,6 @@ class Styles : Stylesheet() { borderRadius += box(6.px) padding = box(4.px, 10.px, 4.px, 10.px) textFill = c(colors.label) - - and(focused) { - backgroundColor += c(colors.backgroundBorder) - } } primaryButton { diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/view/player/PlayerView.kt b/src/main/kotlin/online/hudacek/fxradio/ui/view/player/PlayerView.kt index 31b5abf5..d0cb2a4b 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/view/player/PlayerView.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/view/player/PlayerView.kt @@ -18,14 +18,12 @@ package online.hudacek.fxradio.ui.view.player import javafx.geometry.Pos import javafx.scene.layout.Priority -import online.hudacek.fxradio.api.stations.model.Station import online.hudacek.fxradio.ui.BaseView import online.hudacek.fxradio.ui.make import online.hudacek.fxradio.ui.requestFocusOnSceneAvailable import online.hudacek.fxradio.ui.setOnSpacePressed import online.hudacek.fxradio.ui.style.Styles import online.hudacek.fxradio.viewmodel.HistoryViewModel -import online.hudacek.fxradio.viewmodel.Player import online.hudacek.fxradio.viewmodel.PlayerState import online.hudacek.fxradio.viewmodel.PlayerViewModel import org.controlsfx.glyphfont.FontAwesome @@ -81,8 +79,8 @@ class PlayerView : BaseView() { maxWidth = 90.0 majorTickUnit = 8.0 isSnapToTicks = true - isShowTickMarks = true - paddingTop = 10.0 + //isShowTickMarks = true + paddingTop = 3.0 //Save new value valueProperty().onChange { @@ -91,10 +89,6 @@ class PlayerView : BaseView() { } } - init { - viewModel.item = Player(station = historyViewModel.stationsProperty.lastOrNull() ?: Station.dummy) - } - override val root = vbox { hbox(12) { vgrow = Priority.NEVER @@ -136,11 +130,11 @@ class PlayerView : BaseView() { //Volume controls hbox { paddingRight = 30.0 - alignment = Pos.CENTER_LEFT + alignment = Pos.CENTER glyph { id = "volumeMinIcon" graphic = volumeDownGlyph - onLeftClick { + setOnMouseClicked { volumeSlider.value = volumeSlider.min } addClass(Styles.playerControls) @@ -150,7 +144,7 @@ class PlayerView : BaseView() { id = "volumeMaxIcon" graphic = volumeUpGlyph minWidth = 20.0 - onLeftClick { + setOnMouseClicked { volumeSlider.value = volumeSlider.max } addClass(Styles.playerControls) diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/HistoryViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/HistoryViewModel.kt index 37e74cc4..66ae1852 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/HistoryViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/HistoryViewModel.kt @@ -51,7 +51,6 @@ class HistoryViewModel : BaseViewModel(History()) { .filter { it.isValid() && it !in stationsProperty } .doOnError { logger.error(it) { "Exception when adding station to history!" } } .flatMapSingle { Tables.history.insert(it) } - .doOnEach { println("new stuff") } .subscribe { stationsProperty += it } diff --git a/src/main/kotlin/online/hudacek/fxradio/viewmodel/PlayerViewModel.kt b/src/main/kotlin/online/hudacek/fxradio/viewmodel/PlayerViewModel.kt index 795976db..287faef3 100644 --- a/src/main/kotlin/online/hudacek/fxradio/viewmodel/PlayerViewModel.kt +++ b/src/main/kotlin/online/hudacek/fxradio/viewmodel/PlayerViewModel.kt @@ -43,7 +43,7 @@ sealed class PlayerState { data class Error(val cause: String) : PlayerState() } -class Player(station: Station, +class Player(station: Station = Station.dummy, animate: Boolean = Properties.PlayerAnimated.value(true), volume: Double = Properties.Volume.value(0.0), trackName: String = "", @@ -58,7 +58,9 @@ class Player(station: Station, /** * Handles station playing logic */ -class PlayerViewModel : BaseStateViewModel(initialState = PlayerState.Stopped) { +class PlayerViewModel : BaseStateViewModel( + initialState = PlayerState.Stopped, + initialItem = Player()) { private val clickUseCase: ClickUseCase by inject() From b91a615e93d32dba4d555e625a29e150c42e3ca3 Mon Sep 17 00:00:00 2001 From: jozef Date: Sun, 30 May 2021 18:22:01 +0200 Subject: [PATCH 10/27] Small bugfixes + UI improvements --- .../kotlin/online/hudacek/fxradio/Config.kt | 2 +- .../hudacek/fxradio/ui/CustomErrorHandler.kt | 4 +- .../online/hudacek/fxradio/ui/Extensions.kt | 2 +- .../fxradio/ui/fragment/AboutFragment.kt | 17 +++++++- .../ui/fragment/StationInfoFragment.kt | 3 ++ .../fxradio/ui/fragment/StatsFragment.kt | 17 ++++++-- .../hudacek/fxradio/ui/menu/BaseMenu.kt | 8 ++-- .../hudacek/fxradio/ui/menu/FavouritesMenu.kt | 37 ++++++++++++---- .../view/library/LibraryCountriesFragment.kt | 2 +- .../ui/view/library/LibraryListView.kt | 2 +- .../fxradio/ui/view/library/LibraryView.kt | 42 ++++++++++++------- .../ui/view/stations/StationsEmptyView.kt | 7 ++-- .../fxradio/usecase/GetCountriesUseCase.kt | 7 ++-- .../online/hudacek/fxradio/util/Modal.kt | 4 +- src/main/resources/Messages.properties | 5 ++- 15 files changed, 112 insertions(+), 47 deletions(-) diff --git a/src/main/kotlin/online/hudacek/fxradio/Config.kt b/src/main/kotlin/online/hudacek/fxradio/Config.kt index 49fe7d9f..8669a994 100644 --- a/src/main/kotlin/online/hudacek/fxradio/Config.kt +++ b/src/main/kotlin/online/hudacek/fxradio/Config.kt @@ -52,6 +52,6 @@ object Config { object Flags { const val darkStylesEnabled = false const val useTrayIcon = false - const val enableStationDebug = true + const val enableStationDebug = false } } \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/CustomErrorHandler.kt b/src/main/kotlin/online/hudacek/fxradio/ui/CustomErrorHandler.kt index 372cf189..1bff8fb1 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/CustomErrorHandler.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/CustomErrorHandler.kt @@ -101,8 +101,8 @@ class CustomErrorHandler : Thread.UncaughtExceptionHandler { addClass(Styles.backgroundWhiteSmoke) } - val reportButton = ButtonType("Report issue", ButtonBar.ButtonData.HELP) - val copyButton = ButtonType("Copy to clipboard", ButtonBar.ButtonData.HELP_2) + val reportButton = ButtonType("Report", ButtonBar.ButtonData.HELP) + val copyButton = ButtonType("Copy", ButtonBar.ButtonData.HELP_2) buttonTypes.addAll(reportButton, copyButton) val result = showAndWait() diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/Extensions.kt b/src/main/kotlin/online/hudacek/fxradio/ui/Extensions.kt index 37e93c52..42c58e30 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/Extensions.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/Extensions.kt @@ -75,7 +75,7 @@ internal fun EventTarget.smallLabel(text: String = "", op: Label.() -> Unit = {} } internal fun FontAwesome.Glyph.make( - size: Double = 35.0, + size: Double, useStyle: Boolean = true, color: Color? = null) = toGlyph { size(size) diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/fragment/AboutFragment.kt b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/AboutFragment.kt index 6347406d..6bca0086 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/fragment/AboutFragment.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/AboutFragment.kt @@ -20,6 +20,8 @@ import javafx.geometry.Pos import online.hudacek.fxradio.Config import online.hudacek.fxradio.FxRadio import online.hudacek.fxradio.ui.BaseFragment +import online.hudacek.fxradio.ui.openUrl +import online.hudacek.fxradio.ui.requestFocusOnSceneAvailable import online.hudacek.fxradio.ui.style.Styles import online.hudacek.fxradio.util.Modal import online.hudacek.fxradio.util.open @@ -28,9 +30,10 @@ import tornadofx.* /*** * Simple Information about the app */ -class AboutFragment : BaseFragment(FxRadio.appDesc) { +class AboutFragment : BaseFragment(FxRadio.appName) { override val root = vbox { + requestFocusOnSceneAvailable() prefWidth = 300.0 vbox(alignment = Pos.CENTER) { @@ -47,7 +50,7 @@ class AboutFragment : BaseFragment(FxRadio.appDesc) { addClass(Styles.grayLabel) } - label(messages["about.datasource"]) { + label(FxRadio.appDesc) { style { paddingBottom = 8.0 } @@ -63,6 +66,16 @@ class AboutFragment : BaseFragment(FxRadio.appDesc) { paddingAll = 10.0 alignment = Pos.CENTER_RIGHT + hyperlink(messages["about.datasource"]) { + action { + app.openUrl("http://radio-browser.info") + } + } + + region { + paddingTop = 10.0 + } + hyperlink(messages["menu.app.attributions"]) { action { Modal.Attributions.open() diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/fragment/StationInfoFragment.kt b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/StationInfoFragment.kt index 7175adbf..d86f13bb 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/fragment/StationInfoFragment.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/StationInfoFragment.kt @@ -42,6 +42,7 @@ class StationInfoFragment : BaseFragment() { override val root = vbox { prefWidth = 400.0 + titleProperty.bind(viewModel.nameProperty) hbox(10) { vbox(alignment = Pos.CENTER) { @@ -131,6 +132,8 @@ class StationInfoFragment : BaseFragment() { } style { + backgroundRadius += box(6.px) + borderRadius += box(6.px) paddingAll = 8 backgroundColor += Color.WHITESMOKE } diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/fragment/StatsFragment.kt b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/StatsFragment.kt index 65568997..e0fbccd6 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/fragment/StatsFragment.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/StatsFragment.kt @@ -53,16 +53,17 @@ class StatsFragment : BaseFragment() { override val root = vbox { title = messages["stats.title"] - setPrefSize(300.0, 250.0) + prefWidth = 300.0 + minHeight = 200.0 vbox(alignment = Pos.CENTER) { - requestFocusOnSceneAvailable() //To get rid of the blue box around the hyperlink + paddingAll = 10.0 + hyperlink(serversViewModel.selectedProperty) { - paddingAll = 10.0 - addClass(Styles.header) action { app.openUrl("http://${this.text}") } + addClass(Styles.header) } vbox { @@ -70,10 +71,18 @@ class StatsFragment : BaseFragment() { label(labelTextProperty) { paddingAll = 20.0 } + + viewModel.stateObservableChanges.subscribe { + managedProperty().value = when (it) { + is StatsState.Fetched -> false + else -> true + } + } } } listview(viewModel.statsProperty) { + requestFocusOnSceneAvailable() //To get rid of the blue box around the hyperlink isMouseTransparent = true //Disable clicking into listview, as it is not needed for this listview cellFormat { paddingAll = 0.0 diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/menu/BaseMenu.kt b/src/main/kotlin/online/hudacek/fxradio/ui/menu/BaseMenu.kt index 91cd052e..31feba36 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/menu/BaseMenu.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/menu/BaseMenu.kt @@ -37,10 +37,12 @@ abstract class BaseMenu(menuTitle: String) : Controller() { * Parent menu object, extending classes defines its items via [menuItems] property */ val menu: Menu by lazy { - val actualTitle = if (messages.containsKey(menuTitle)) { - messages[menuTitle] - } else { + //Workaround to use actual key as menu text instead of + //placeholder when key does not exist in Messages + val actualTitle = if (messages[menuTitle].startsWith("[")) { menuTitle + } else { + messages[menuTitle] } menu(actualTitle) { diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/menu/FavouritesMenu.kt b/src/main/kotlin/online/hudacek/fxradio/ui/menu/FavouritesMenu.kt index ce4f938f..be49d3ed 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/menu/FavouritesMenu.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/menu/FavouritesMenu.kt @@ -17,6 +17,7 @@ package online.hudacek.fxradio.ui.menu import javafx.scene.control.MenuItem +import online.hudacek.fxradio.ui.stationImage import online.hudacek.fxradio.viewmodel.FavouritesViewModel import online.hudacek.fxradio.viewmodel.LibraryState import online.hudacek.fxradio.viewmodel.LibraryViewModel @@ -68,11 +69,34 @@ class FavouritesMenu : BaseMenu("menu.favourites") { ) override val menuItems = mutableListOf().apply { - add(item(messages["menu.favourites.show"]) { - action { - libraryViewModel.stateProperty.value = LibraryState.Favourites - } - }) + addAll(listOf( + item(messages["menu.favourites.show"]) { + action { + libraryViewModel.stateProperty.value = LibraryState.Favourites + } + }, + menu(messages["menu.favourites.all"]) { + disableWhen { + favouritesViewModel.stationsProperty.emptyProperty() + } + items.bind(favouritesViewModel.stationsProperty) { + item(it.name) { + //for some reason macos native menu does not respect + //width/height setting so it is disabled for now + if (!appMenuViewModel.usePlatformProperty.value) { + graphic = imageview { + it.stationImage(this) + fitHeight = 15.0 + fitWidth = 15.0 + isPreserveRatio = true + } + } + action { + playerViewModel.stationProperty.value = it + } + } + } + })) addAll(addRemoveFavouriteItems) addAll(listOf(separator(), item(messages["menu.station.favourite.clear"]) { @@ -85,7 +109,6 @@ class FavouritesMenu : BaseMenu("menu.favourites") { appEvent.refreshLibrary.onNext(LibraryState.Favourites) } } - }, - separator())) + })) } } diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryCountriesFragment.kt b/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryCountriesFragment.kt index e6d8f654..472ccd9e 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryCountriesFragment.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryCountriesFragment.kt @@ -50,7 +50,7 @@ class LibraryCountriesFragment(countriesProperty: ListProperty, * Set min/max size of listview based on its items size */ prefHeightProperty().bind(countriesProperty.doubleBinding { - if (it != null) it.size * 30.0 + 10 else 30.0 + if (it != null) it.size * 30.0 + 5.0 else 30.0 }) cellFormat { diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryListView.kt b/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryListView.kt index 68bb9b27..09862893 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryListView.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryListView.kt @@ -49,7 +49,7 @@ class LibraryListView : BaseView() { id = "libraryListView" prefHeightProperty().bind(viewModel.librariesProperty.doubleBinding { - if (it != null) it.size * 30.0 + 10 else 30.0 + if (it != null) it.size * 30.0 + 5 else 30.0 }) cellFormat { diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryView.kt b/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryView.kt index 7203adc3..545d6ea4 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryView.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryView.kt @@ -37,7 +37,6 @@ class LibraryView : BaseView() { .subscribe { viewModel.pinnedProperty += it } - viewModel.getCountries() } @@ -51,23 +50,29 @@ class LibraryView : BaseView() { } } - add(LibraryTitleFragment(messages["library"], viewModel.showLibraryProperty) { - viewModel.showLibraryProperty.value = !viewModel.showLibraryProperty.value - viewModel.commit() - }) - - add(libraryListView) - vbox { - add(LibraryTitleFragment(messages["pinned"], viewModel.showPinnedProperty) { - viewModel.showPinnedProperty.value = !viewModel.showPinnedProperty.value + add(LibraryTitleFragment(messages["library"], viewModel.showLibraryProperty) { + viewModel.showLibraryProperty.value = !viewModel.showLibraryProperty.value viewModel.commit() }) + paddingBottom = 5.0 + } + vbox { + add(libraryListView) + } + + vbox { vbox { - add(LibraryCountriesFragment(viewModel.pinnedProperty, viewModel.showPinnedProperty)) + add(LibraryTitleFragment(messages["pinned"], viewModel.showPinnedProperty) { + viewModel.showPinnedProperty.value = !viewModel.showPinnedProperty.value + viewModel.commit() + }) + paddingBottom = 5.0 } + add(LibraryCountriesFragment(viewModel.pinnedProperty, viewModel.showPinnedProperty)) + showWhen { viewModel.pinnedProperty.emptyProperty().not() } @@ -77,14 +82,21 @@ class LibraryView : BaseView() { center { vbox { - add(LibraryTitleFragment(messages["countries"], viewModel.showCountriesProperty) { - viewModel.showCountriesProperty.value = !viewModel.showCountriesProperty.value - viewModel.commit() - }) + vbox { + add(LibraryTitleFragment(messages["countries"], viewModel.showCountriesProperty) { + viewModel.showCountriesProperty.value = !viewModel.showCountriesProperty.value + viewModel.commit() + }) + paddingBottom = 5.0 + } vbox { add(LibraryCountriesFragment(viewModel.countriesProperty, viewModel.showCountriesProperty)) prefHeightProperty().bind(this@center.heightProperty()) + + showWhen { + viewModel.countriesProperty.emptyProperty().not() + } } //Retry link diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsEmptyView.kt b/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsEmptyView.kt index 2a0859cd..0f58eb43 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsEmptyView.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/view/stations/StationsEmptyView.kt @@ -34,9 +34,9 @@ class StationsEmptyView : BaseView() { private val viewModel: StationsViewModel by inject() - private val searchGlyph by lazy { FontAwesome.Glyph.SEARCH.make() } - private val errorGlyph by lazy { FontAwesome.Glyph.SEARCH.make() } - private val noResultsGlyph by lazy { FontAwesome.Glyph.FROWN_ALT.make() } + private val searchGlyph by lazy { FontAwesome.Glyph.SEARCH.make(size = 50.0) } + private val errorGlyph by lazy { FontAwesome.Glyph.SEARCH.make(size = 50.0) } + private val noResultsGlyph by lazy { FontAwesome.Glyph.TIMES.make(size = 50.0) } private val headerProperty = viewModel.stateProperty.stringBinding { when (it) { @@ -68,6 +68,7 @@ class StationsEmptyView : BaseView() { //Description of a message, shown only if relevant private val subHeader by lazy { label(subHeaderProperty) { + paddingTop = 5.0 id = "stationMessageSubHeader" addClass(Styles.grayLabel) } diff --git a/src/main/kotlin/online/hudacek/fxradio/usecase/GetCountriesUseCase.kt b/src/main/kotlin/online/hudacek/fxradio/usecase/GetCountriesUseCase.kt index 422be281..55e56d7a 100644 --- a/src/main/kotlin/online/hudacek/fxradio/usecase/GetCountriesUseCase.kt +++ b/src/main/kotlin/online/hudacek/fxradio/usecase/GetCountriesUseCase.kt @@ -35,8 +35,9 @@ class GetCountriesUseCase : BaseUseCase, Disposable>() { .compose(applySchedulers()) .flattenAsObservable { it } .filter { it.isValid } - .doOnError { logger.error(it) { "Exception while getting Countries!" } } - .subscribe { + .subscribe({ input.add(it) - } + }, { + logger.error(it) { "Exception while getting Countries!" } + }) } \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/util/Modal.kt b/src/main/kotlin/online/hudacek/fxradio/util/Modal.kt index 9f146e67..f9fd846d 100644 --- a/src/main/kotlin/online/hudacek/fxradio/util/Modal.kt +++ b/src/main/kotlin/online/hudacek/fxradio/util/Modal.kt @@ -28,9 +28,9 @@ import kotlin.reflect.full.createInstance */ sealed class Modal(val style: StageStyle = StageStyle.UTILITY, val resizable: Boolean = false) { - object StationInfo : Modal(style = StageStyle.UNDECORATED) + object StationInfo : Modal(style = StageStyle.DECORATED) object AddNewStation : Modal() - object About : Modal(resizable = true) + object About : Modal() object Servers : Modal(resizable = true) object Stats : Modal() object Attributions : Modal() diff --git a/src/main/resources/Messages.properties b/src/main/resources/Messages.properties index 22f99b9a..10aa9014 100644 --- a/src/main/resources/Messages.properties +++ b/src/main/resources/Messages.properties @@ -19,7 +19,7 @@ connectionError=Connection error. Please try again connectionErrorDesc=Please check if your internet connection is working. noResults=No stations were found searchingLibrary=Searching Radio Directory -searchingLibraryDesc=Type at least 3 characters to the search field +searchingLibraryDesc=Start by typing at least 3 characters to the search field... field.max.length=The field is too long field.min.length=The field is too short field.invalid.length=The field is too short or too long @@ -74,6 +74,7 @@ menu.player.animate=Show animated radio name menu.player.notifications=Show OS notifications menu.favourites=_Favourites menu.favourites.show=Show Favourites +menu.favourites.all=All favourites cache.clear.confirm=Do you want to clear image cache? cache.clear.ok=Cache cleared successfully. cache.clear.error=There was an error when clearing cache. @@ -132,7 +133,7 @@ servers.reload=Reload servers.restartNeeded=Selected server will be applied after restart of the app. server.save.ok=Server selected. Please restart the app to apply changes. server.save.error=Can't save selected server. -about.datasource=Data provided by http://radio-browser.info public API +about.datasource=Data provider pin=Pin unpin=Unpin pinned=Pinned From 7f457993d9abd9bcf6387df4c9f829b4583be934 Mon Sep 17 00:00:00 2001 From: jozef Date: Mon, 31 May 2021 23:23:50 +0200 Subject: [PATCH 11/27] accent colors for macos --- .../fxradio/ui/fragment/AboutFragment.kt | 17 +++-- .../fxradio/ui/fragment/StatsFragment.kt | 3 +- .../hudacek/fxradio/ui/menu/AboutMenu.kt | 1 - .../hudacek/fxradio/ui/menu/BaseMenu.kt | 2 + .../hudacek/fxradio/ui/menu/HelpMenu.kt | 4 +- .../online/hudacek/fxradio/ui/style/Colors.kt | 26 +++++-- .../online/hudacek/fxradio/ui/style/Styles.kt | 67 ++++++++++--------- .../hudacek/fxradio/ui/style/StylesDark.kt | 26 ------- .../view/library/LibraryCountriesFragment.kt | 2 +- .../ui/view/library/LibraryListView.kt | 4 +- .../fxradio/ui/view/player/PlayerView.kt | 7 +- .../hudacek/fxradio/util/macos/MacUtils.kt | 26 ++++++- src/main/resources/Messages.properties | 3 +- 13 files changed, 103 insertions(+), 85 deletions(-) diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/fragment/AboutFragment.kt b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/AboutFragment.kt index 6bca0086..3a70ecaf 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/fragment/AboutFragment.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/AboutFragment.kt @@ -58,28 +58,27 @@ class AboutFragment : BaseFragment(FxRadio.appName) { } label("${FxRadio.copyright} ${FxRadio.author}") { + paddingBottom = 10.0 addClass(Styles.grayLabel) } - } - - vbox { - paddingAll = 10.0 - alignment = Pos.CENTER_RIGHT hyperlink(messages["about.datasource"]) { action { app.openUrl("http://radio-browser.info") } + addClass(Styles.grayLabel) } + } - region { - paddingTop = 10.0 - } + vbox { + paddingAll = 10.0 + alignment = Pos.CENTER_RIGHT - hyperlink(messages["menu.app.attributions"]) { + button(messages["menu.app.attributions"]) { action { Modal.Attributions.open() } + addClass(Styles.primaryButton) } } diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/fragment/StatsFragment.kt b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/StatsFragment.kt index e0fbccd6..63f31e0e 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/fragment/StatsFragment.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/fragment/StatsFragment.kt @@ -53,8 +53,7 @@ class StatsFragment : BaseFragment() { override val root = vbox { title = messages["stats.title"] - prefWidth = 300.0 - minHeight = 200.0 + setPrefSize(300.0, 230.0) vbox(alignment = Pos.CENTER) { paddingAll = 10.0 diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/menu/AboutMenu.kt b/src/main/kotlin/online/hudacek/fxradio/ui/menu/AboutMenu.kt index 4b86ee19..e0cb496a 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/menu/AboutMenu.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/menu/AboutMenu.kt @@ -32,7 +32,6 @@ class AboutMenu : BaseMenu(FxRadio.appName) { Modal.About.open() } }, - separator(), item(messages["menu.app.server"]) { action { Modal.Servers.open() diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/menu/BaseMenu.kt b/src/main/kotlin/online/hudacek/fxradio/ui/menu/BaseMenu.kt index 31feba36..42742d58 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/menu/BaseMenu.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/menu/BaseMenu.kt @@ -60,5 +60,7 @@ abstract class BaseMenu(menuTitle: String) : Controller() { val stop = KeyCodeCombination(KeyCode.S, KeyCombination.CONTROL_DOWN) val info = KeyCodeCombination(KeyCode.I, KeyCombination.CONTROL_DOWN) val add = KeyCodeCombination(KeyCode.A, KeyCombination.CONTROL_DOWN) + val open = KeyCodeCombination(KeyCode.O, KeyCombination.CONTROL_DOWN) + val website = KeyCodeCombination(KeyCode.W, KeyCombination.CONTROL_DOWN) } } diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/menu/HelpMenu.kt b/src/main/kotlin/online/hudacek/fxradio/ui/menu/HelpMenu.kt index b3008c5f..f4004029 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/menu/HelpMenu.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/menu/HelpMenu.kt @@ -30,7 +30,7 @@ class HelpMenu : BaseMenu("menu.help") { private val logsFolderPath = "file://${Config.Paths.baseAppPath}" override val menuItems = listOf( - item(messages["menu.help.openhomepage"]) { + item(messages["menu.help.openhomepage"], KeyCodes.website) { action { appMenuViewModel.openWebsite() } @@ -48,7 +48,7 @@ class HelpMenu : BaseMenu("menu.help") { }, logMenu.menu, separator(), - item(messages["menu.help.logs"]) { + item(messages["menu.help.logs"], KeyCodes.open) { action { app.openUrl(logsFolderPath) } diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/style/Colors.kt b/src/main/kotlin/online/hudacek/fxradio/ui/style/Colors.kt index 00a12628..5db512da 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/style/Colors.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/style/Colors.kt @@ -17,6 +17,7 @@ package online.hudacek.fxradio.ui.style import online.hudacek.fxradio.FxRadio +import online.hudacek.fxradio.util.macos.MacUtils object Colors { val values: ColorValues by lazy { @@ -24,9 +25,27 @@ object Colors { } } +private fun getPrimaryColor(): String { + return if (MacUtils.isMac) { + when (MacUtils.accentColor) { + MacUtils.AccentColor.MULTICOLOR -> "#d65458" + MacUtils.AccentColor.GRAPHITE -> "#8C8C8C" + MacUtils.AccentColor.RED -> "#FF5258" + MacUtils.AccentColor.ORANGE -> "#F8821B" + MacUtils.AccentColor.YELLOW -> "#FFC500" + MacUtils.AccentColor.GREEN -> "#64B946" + MacUtils.AccentColor.BLUE -> "#037AFF" + MacUtils.AccentColor.PURPLE -> "#A550A6" + MacUtils.AccentColor.PINK -> "#F7509E" + null -> "#d65458" + } + } else { + "#d65458" + } +} + open class ColorValues { - open val primary = "#d65458" - open val primaryHover = "#ff696b" + open val primary = getPrimaryColor() open val background = "#E9E9E9" open val backgroundBorder = "#E8E8E8" @@ -36,13 +55,10 @@ open class ColorValues { open val grayLabel = "#8B8B8B" open val transparent = "transparent" - - open val libraryIcon = "#d65458" } class DarkColorValues : ColorValues() { override val primary = "#0097CE" - override val primaryHover = "#0097EA" override val background = "#333232" override val backgroundBorder = "#525356" diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/style/Styles.kt b/src/main/kotlin/online/hudacek/fxradio/ui/style/Styles.kt index 87f0aa3b..4c6db9e6 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/style/Styles.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/style/Styles.kt @@ -50,13 +50,10 @@ class Styles : Stylesheet() { val subheader by cssclass() val tag by cssclass() - val searchBoxLabel by cssclass() - val primaryTextColor by cssclass() val boldText by cssclass() val backgroundWhite by cssclass() val backgroundWhiteSmoke by cssclass() - val statusBar by cssclass() //For Text() elements val defaultTextColor by cssclass() @@ -67,15 +64,16 @@ class Styles : Stylesheet() { } init { + root { + focusColor = c(colors.primary) + faintFocusColor = c(colors.transparent) + } + label { textFill = c(colors.label) fontSmoothingType = FontSmoothingType.GRAY } - searchBoxLabel { - padding = box(0.px, 2.px, 0.px, 7.px) - } - playerMainBox { padding = box(10.0.px, 0.0.px) borderColor += box(c(colors.transparent), c(colors.transparent), c(colors.backgroundBorder), c(colors.transparent)) @@ -127,8 +125,8 @@ class Styles : Stylesheet() { unsafe("-fx-padding", raw("0")) unsafe("-fx-background-insets", raw("0")) unsafe("-fx-background-color", raw("-fx-background")) - unsafe("-fx-border-color", raw("transparent")) - unsafe("-fx-faint-focus-color", raw("transparent")) + unsafe("-fx-border-color", raw(colors.transparent)) + unsafe("-fx-faint-focus-color", raw(colors.transparent)) } grayLabel { @@ -137,7 +135,13 @@ class Styles : Stylesheet() { } button { - prefWidth = 75.px + + and(default) { + baseColor = c(colors.primary) + textFill = Color.WHITESMOKE + } + + minWidth = 75.px fontSize = 12.px backgroundRadius += box(6.px) borderRadius += box(6.px) @@ -146,15 +150,8 @@ class Styles : Stylesheet() { } primaryButton { - backgroundColor += c(colors.primary) + baseColor = c(colors.primary) textFill = Color.WHITESMOKE - and(hover) { - backgroundColor += c(colors.primaryHover) - } - } - - primaryTextColor { - textFill = c(colors.primary) } hyperlink { @@ -170,6 +167,7 @@ class Styles : Stylesheet() { libraryListItem { fontSize = 12.px + prefHeight = 30.px textFill = c(colors.label) backgroundColor += Color.WHITESMOKE backgroundRadius += box(6.px) @@ -178,7 +176,9 @@ class Styles : Stylesheet() { backgroundColor += c(colors.background) borderColor += box(c(colors.backgroundBorder)) } - padding = box(5.px, 10.px, 5.px, 15.px) + padding = box(6.px, 10.px, 6.px, 15.px) + //unsafe("-fx-background-insets", raw("0px 10px 0px 10px")) + //unsafe("-fx-border-insets", raw("0px 10px 0px 10px")) } libraryListItemTag { @@ -229,6 +229,23 @@ class Styles : Stylesheet() { // Restyled default compontents // =================================================================== + tableView { + tableRowCell { + and(selected) { + backgroundColor += c(colors.primary) + } + } + } + + checkBox { + baseColor = c(colors.primary) + and(selected) { + mark { + backgroundColor += Color.WHITE + } + } + } + scrollBar { backgroundColor += c(colors.transparent) borderColor += box(c(colors.transparent)) @@ -378,18 +395,6 @@ class Styles : Stylesheet() { } } - progressIndicator { - progressColor = c(colors.grayLabel) - } - - statusBar { - padding = box(5.px) - borderRadius += box(0.px) - borderWidth += box(1.px, 0.px, 0.px, 0.px) - borderColor += box(c(colors.backgroundBorder), c(colors.transparent), c(colors.transparent), c(colors.transparent)) - unsafe("-fx-control-inner-background", Color.TRANSPARENT) - } - textField { backgroundRadius += box(6.px) borderRadius += box(6.px) diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/style/StylesDark.kt b/src/main/kotlin/online/hudacek/fxradio/ui/style/StylesDark.kt index 29024cf3..5cdd2240 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/style/StylesDark.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/style/StylesDark.kt @@ -48,13 +48,10 @@ class StylesDark : Stylesheet() { val subheader by cssclass() val tag by cssclass() - val searchBoxLabel by cssclass() - val primaryTextColor by cssclass() val boldText by cssclass() val backgroundWhite by cssclass() val backgroundWhiteSmoke by cssclass() - val statusBar by cssclass() //for Text() val defaultTextColor by cssclass() @@ -72,10 +69,6 @@ class StylesDark : Stylesheet() { textFill = c(colors.label) } - searchBoxLabel { - padding = box(0.px, 2.px, 0.px, 7.px) - } - textInput { backgroundColor += c(colors.backgroundBorder) textFill = Color.WHITESMOKE @@ -156,13 +149,6 @@ class StylesDark : Stylesheet() { primaryButton { backgroundColor += c(colors.primary) textFill = Color.WHITESMOKE - and(hover) { - backgroundColor += c(colors.primaryHover) - } - } - - primaryTextColor { - textFill = c(colors.primary) } libraryListView { @@ -354,18 +340,6 @@ class StylesDark : Stylesheet() { } } - progressIndicator { - progressColor = c(colors.grayLabel) - } - - statusBar { - padding = box(5.px) - borderRadius += box(0.px) - borderWidth += box(1.px, 0.px, 0.px, 0.px) - borderColor += box(c(colors.backgroundBorder), c(colors.transparent), c(colors.transparent), c(colors.transparent)) - unsafe("-fx-control-inner-background", Color.TRANSPARENT) - } - textField { backgroundRadius += box(6.px) borderRadius += box(6.px) diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryCountriesFragment.kt b/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryCountriesFragment.kt index 472ccd9e..868dafff 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryCountriesFragment.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryCountriesFragment.kt @@ -50,7 +50,7 @@ class LibraryCountriesFragment(countriesProperty: ListProperty, * Set min/max size of listview based on its items size */ prefHeightProperty().bind(countriesProperty.doubleBinding { - if (it != null) it.size * 30.0 + 5.0 else 30.0 + if (it != null) it.size * 30.0 + 10.0 else 30.0 }) cellFormat { diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryListView.kt b/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryListView.kt index 09862893..12490074 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryListView.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryListView.kt @@ -49,11 +49,11 @@ class LibraryListView : BaseView() { id = "libraryListView" prefHeightProperty().bind(viewModel.librariesProperty.doubleBinding { - if (it != null) it.size * 30.0 + 5 else 30.0 + if (it != null) it.size * 30.0 + 10 else 30.0 }) cellFormat { - graphic = item.glyph.make(14.0, false, c(Colors.values.libraryIcon)) + graphic = item.glyph.make(14.0, false, c(Colors.values.primary)) text = messages[item.type.key] addClass(Styles.libraryListItem) } diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/view/player/PlayerView.kt b/src/main/kotlin/online/hudacek/fxradio/ui/view/player/PlayerView.kt index d0cb2a4b..f9ffecc9 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/view/player/PlayerView.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/view/player/PlayerView.kt @@ -22,6 +22,7 @@ import online.hudacek.fxradio.ui.BaseView import online.hudacek.fxradio.ui.make import online.hudacek.fxradio.ui.requestFocusOnSceneAvailable import online.hudacek.fxradio.ui.setOnSpacePressed +import online.hudacek.fxradio.ui.style.Colors import online.hudacek.fxradio.ui.style.Styles import online.hudacek.fxradio.viewmodel.HistoryViewModel import online.hudacek.fxradio.viewmodel.PlayerState @@ -44,7 +45,9 @@ class PlayerView : BaseView() { private val playGlyph by lazy { FontAwesome.Glyph.PLAY.make(size = 22.0, useStyle = false) } private val stopGlyph by lazy { FontAwesome.Glyph.STOP.make(size = 22.0, useStyle = false) } private val volumeDownGlyph by lazy { FontAwesome.Glyph.VOLUME_DOWN.make(size = 14.0, useStyle = false) } - private val randomStationGlyph by lazy { FontAwesome.Glyph.RANDOM.make(size = 14.0, useStyle = false) } + private val randomStationGlyph by lazy { + FontAwesome.Glyph.RANDOM.make(size = 14.0, useStyle = false, color = c(Colors.values.primary)) + } private val volumeUpGlyph by lazy { FontAwesome.Glyph.VOLUME_UP.make(size = 14.0, useStyle = false) } private val playerControlsBinding = viewModel.stateProperty.objectBinding { @@ -80,7 +83,7 @@ class PlayerView : BaseView() { majorTickUnit = 8.0 isSnapToTicks = true //isShowTickMarks = true - paddingTop = 3.0 + paddingTop = 2.0 //Save new value valueProperty().onChange { diff --git a/src/main/kotlin/online/hudacek/fxradio/util/macos/MacUtils.kt b/src/main/kotlin/online/hudacek/fxradio/util/macos/MacUtils.kt index a044ca45..732ced6d 100644 --- a/src/main/kotlin/online/hudacek/fxradio/util/macos/MacUtils.kt +++ b/src/main/kotlin/online/hudacek/fxradio/util/macos/MacUtils.kt @@ -19,11 +19,25 @@ package online.hudacek.fxradio.util.macos import airsquared.JMacNotification.NSUserNotification import online.hudacek.fxradio.util.Command import org.controlsfx.tools.Platform +import tornadofx.Component +import tornadofx.get -object MacUtils { +object MacUtils : Component() { val isMac = Platform.getCurrent() == Platform.OSX + enum class AccentColor(val colorCode: Int? = null) { + MULTICOLOR, + GRAPHITE(-1), + RED(0), + ORANGE(1), + YELLOW(2), + GREEN(3), + BLUE(4), + PURPLE(5), + PINK(6), + } + /** * Shows MacOS native system notification */ @@ -31,11 +45,17 @@ object MacUtils { NSUserNotification().apply { this.title = title this.informativeText = subtitle + this.actionButtonTitle = messages["show"] }.show() val isSystemDarkMode: Boolean get() = Command("defaults read -g AppleInterfaceStyle").exec() == "Dark" - val accentColor: String - get() = Command("defaults read -g AppleAccentColor").exec() + val accentColor: AccentColor? + get() = AccentColor.values().find { + it.colorCode == Command("defaults read -g AppleAccentColor").exec().toIntOrNull() + } + + val osVersion: String + get() = System.getProperty("os.version") } \ No newline at end of file diff --git a/src/main/resources/Messages.properties b/src/main/resources/Messages.properties index 10aa9014..591726b7 100644 --- a/src/main/resources/Messages.properties +++ b/src/main/resources/Messages.properties @@ -3,6 +3,7 @@ favourites=Favourites history=History topStations=Top Stations copy=Copy +show=Show copy.image.url=Copy Image URL copy.stream.url=Copy Stream URL copy.nowPlaying=Copy track name @@ -133,7 +134,7 @@ servers.reload=Reload servers.restartNeeded=Selected server will be applied after restart of the app. server.save.ok=Server selected. Please restart the app to apply changes. server.save.error=Can't save selected server. -about.datasource=Data provider +about.datasource=Data provided by radio-browser.info pin=Pin unpin=Unpin pinned=Pinned From 913734e2158a06555b8dc51f28c76923989ddb7e Mon Sep 17 00:00:00 2001 From: jozef Date: Mon, 31 May 2021 23:44:03 +0200 Subject: [PATCH 12/27] Add internal flag for AccentColor.kt setup --- .../hudacek/fxradio/ui/style/AccentColor.kt | 50 +++++++++++++++++++ .../online/hudacek/fxradio/ui/style/Colors.kt | 29 ++++++----- .../online/hudacek/fxradio/util/Properties.kt | 5 +- .../hudacek/fxradio/util/macos/MacUtils.kt | 18 +------ 4 files changed, 69 insertions(+), 33 deletions(-) create mode 100644 src/main/kotlin/online/hudacek/fxradio/ui/style/AccentColor.kt diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/style/AccentColor.kt b/src/main/kotlin/online/hudacek/fxradio/ui/style/AccentColor.kt new file mode 100644 index 00000000..e3f779f6 --- /dev/null +++ b/src/main/kotlin/online/hudacek/fxradio/ui/style/AccentColor.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2020 FXRadio by hudacek.online + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package online.hudacek.fxradio.ui.style + +/** + * App Accent Color definitions + */ +enum class AccentColor(val colorCode: Int? = null) { + MULTICOLOR, + GRAPHITE(-1), + RED(0), + ORANGE(1), + YELLOW(2), + GREEN(3), + BLUE(4), + PURPLE(5), + PINK(6), +} + +/** + * Map internal representation of accent color into hex + */ +internal fun AccentColor?.color(): String { + return when (this) { + AccentColor.MULTICOLOR -> "#d65458" + AccentColor.GRAPHITE -> "#8C8C8C" + AccentColor.RED -> "#FF5258" + AccentColor.ORANGE -> "#F8821B" + AccentColor.YELLOW -> "#FFC500" + AccentColor.GREEN -> "#64B946" + AccentColor.BLUE -> "#037AFF" + AccentColor.PURPLE -> "#A550A6" + AccentColor.PINK -> "#F7509E" + null -> "#d65458" + } +} \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/style/Colors.kt b/src/main/kotlin/online/hudacek/fxradio/ui/style/Colors.kt index 5db512da..1526e4c7 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/style/Colors.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/style/Colors.kt @@ -17,6 +17,8 @@ package online.hudacek.fxradio.ui.style import online.hudacek.fxradio.FxRadio +import online.hudacek.fxradio.util.Properties +import online.hudacek.fxradio.util.Property import online.hudacek.fxradio.util.macos.MacUtils object Colors { @@ -26,22 +28,19 @@ object Colors { } private fun getPrimaryColor(): String { - return if (MacUtils.isMac) { - when (MacUtils.accentColor) { - MacUtils.AccentColor.MULTICOLOR -> "#d65458" - MacUtils.AccentColor.GRAPHITE -> "#8C8C8C" - MacUtils.AccentColor.RED -> "#FF5258" - MacUtils.AccentColor.ORANGE -> "#F8821B" - MacUtils.AccentColor.YELLOW -> "#FFC500" - MacUtils.AccentColor.GREEN -> "#64B946" - MacUtils.AccentColor.BLUE -> "#037AFF" - MacUtils.AccentColor.PURPLE -> "#A550A6" - MacUtils.AccentColor.PINK -> "#F7509E" - null -> "#d65458" + val accentProperty = Property(Properties.AccentColor) + return AccentColor.values().find { + if (accentProperty.isPresent) { + it.colorCode == accentProperty.get() + } else { + if (MacUtils.isMac) { + it.colorCode == MacUtils.systemAccentColor + } else { + //Fallback + it == AccentColor.MULTICOLOR + } } - } else { - "#d65458" - } + }.color() } open class ColorValues { diff --git a/src/main/kotlin/online/hudacek/fxradio/util/Properties.kt b/src/main/kotlin/online/hudacek/fxradio/util/Properties.kt index bfefedba..7c92a062 100644 --- a/src/main/kotlin/online/hudacek/fxradio/util/Properties.kt +++ b/src/main/kotlin/online/hudacek/fxradio/util/Properties.kt @@ -25,11 +25,11 @@ private val logger = KotlinLogging.logger {} * Keys for values stored in app.properties */ enum class Properties(val key: String) { - UseNativeMenuBar("menu.native"), + UseNativeMenuBar("menu.native"), //Not configurable in UI Volume("player.volume"), Player("player.type"), PlayerAnimated("player.animate"), - PlayerMetaDataRefresh("player.refreshMeta"), + PlayerMetaDataRefresh("player.refreshMeta"), //Not configurable in UI ApiServer("app.server"), SearchQuery("search.query"), SendOsNotifications("notifications"), @@ -41,6 +41,7 @@ enum class Properties(val key: String) { WindowHeight("window.height"), WindowX("window.x"), WindowY("window.y"), + AccentColor("app.accentColor"), //Not configurable in UI LogLevel("log.level"); } diff --git a/src/main/kotlin/online/hudacek/fxradio/util/macos/MacUtils.kt b/src/main/kotlin/online/hudacek/fxradio/util/macos/MacUtils.kt index 732ced6d..dea590c0 100644 --- a/src/main/kotlin/online/hudacek/fxradio/util/macos/MacUtils.kt +++ b/src/main/kotlin/online/hudacek/fxradio/util/macos/MacUtils.kt @@ -26,18 +26,6 @@ object MacUtils : Component() { val isMac = Platform.getCurrent() == Platform.OSX - enum class AccentColor(val colorCode: Int? = null) { - MULTICOLOR, - GRAPHITE(-1), - RED(0), - ORANGE(1), - YELLOW(2), - GREEN(3), - BLUE(4), - PURPLE(5), - PINK(6), - } - /** * Shows MacOS native system notification */ @@ -51,10 +39,8 @@ object MacUtils : Component() { val isSystemDarkMode: Boolean get() = Command("defaults read -g AppleInterfaceStyle").exec() == "Dark" - val accentColor: AccentColor? - get() = AccentColor.values().find { - it.colorCode == Command("defaults read -g AppleAccentColor").exec().toIntOrNull() - } + val systemAccentColor: Int? + get() = Command("defaults read -g AppleAccentColor").exec().toIntOrNull() val osVersion: String get() = System.getProperty("os.version") From 9d94073b848954694ed43ae940cbe16995bedc40 Mon Sep 17 00:00:00 2001 From: jozef Date: Wed, 2 Jun 2021 00:27:40 +0200 Subject: [PATCH 13/27] UI improvements --- .../hudacek/fxradio/ui/style/AccentColor.kt | 24 +++---- .../online/hudacek/fxradio/ui/style/Colors.kt | 69 +++++++++---------- .../online/hudacek/fxradio/ui/style/Styles.kt | 4 +- .../hudacek/fxradio/ui/style/StylesDark.kt | 57 +++++++++++---- src/main/resources/Messages.properties | 2 +- 5 files changed, 87 insertions(+), 69 deletions(-) diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/style/AccentColor.kt b/src/main/kotlin/online/hudacek/fxradio/ui/style/AccentColor.kt index e3f779f6..74265e20 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/style/AccentColor.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/style/AccentColor.kt @@ -34,17 +34,15 @@ enum class AccentColor(val colorCode: Int? = null) { /** * Map internal representation of accent color into hex */ -internal fun AccentColor?.color(): String { - return when (this) { - AccentColor.MULTICOLOR -> "#d65458" - AccentColor.GRAPHITE -> "#8C8C8C" - AccentColor.RED -> "#FF5258" - AccentColor.ORANGE -> "#F8821B" - AccentColor.YELLOW -> "#FFC500" - AccentColor.GREEN -> "#64B946" - AccentColor.BLUE -> "#037AFF" - AccentColor.PURPLE -> "#A550A6" - AccentColor.PINK -> "#F7509E" - null -> "#d65458" - } +internal fun AccentColor?.color() = when (this) { + AccentColor.MULTICOLOR -> "#d65458" + AccentColor.GRAPHITE -> "#8C8C8C" + AccentColor.RED -> "#FF5258" + AccentColor.ORANGE -> "#F8821B" + AccentColor.YELLOW -> "#FFC500" + AccentColor.GREEN -> "#64B946" + AccentColor.BLUE -> "#037AFF" + AccentColor.PURPLE -> "#A550A6" + AccentColor.PINK -> "#F7509E" + null -> "#d65458" } \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/style/Colors.kt b/src/main/kotlin/online/hudacek/fxradio/ui/style/Colors.kt index 1526e4c7..78475be0 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/style/Colors.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/style/Colors.kt @@ -25,46 +25,39 @@ object Colors { val values: ColorValues by lazy { if (FxRadio.darkModeEnabled) DarkColorValues() else ColorValues() } -} -private fun getPrimaryColor(): String { - val accentProperty = Property(Properties.AccentColor) - return AccentColor.values().find { - if (accentProperty.isPresent) { - it.colorCode == accentProperty.get() - } else { - if (MacUtils.isMac) { - it.colorCode == MacUtils.systemAccentColor - } else { - //Fallback - it == AccentColor.MULTICOLOR - } - } - }.color() -} + open class ColorValues { + val primary = getPrimaryColor() + val transparent = "transparent" -open class ColorValues { - open val primary = getPrimaryColor() - - open val background = "#E9E9E9" - open val backgroundBorder = "#E8E8E8" - open val backgroundSelected = "#E9E9E9" + open val background = "#E9E9E9" + open val backgroundBorder = "#E8E8E8" + open val backgroundSelected = "#E9E9E9" + open val label = "#2b2b2b" + open val grayLabel = "#8B8B8B" + } - open val label = "#2b2b2b" - open val grayLabel = "#8B8B8B" + class DarkColorValues : ColorValues() { + override val background = "#333232" + override val backgroundBorder = "#525356" + override val backgroundSelected = "#525356" + override val label = "#ffffff" + override val grayLabel = "#a0a1a2" + } - open val transparent = "transparent" + private fun getPrimaryColor(): String { + val accentProperty = Property(Properties.AccentColor) + return AccentColor.values().find { + if (accentProperty.isPresent) { + it.colorCode == accentProperty.get() + } else { + if (MacUtils.isMac) { + it.colorCode == MacUtils.systemAccentColor + } else { + //Fallback + it == AccentColor.MULTICOLOR + } + } + }.color() + } } - -class DarkColorValues : ColorValues() { - override val primary = "#0097CE" - - override val background = "#333232" - override val backgroundBorder = "#525356" - override val backgroundSelected = "#525356" - - override val label = "#ffffff" - override val grayLabel = "#a0a1a2" - - override val transparent = "transparent" -} \ No newline at end of file diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/style/Styles.kt b/src/main/kotlin/online/hudacek/fxradio/ui/style/Styles.kt index 4c6db9e6..c7b81e69 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/style/Styles.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/style/Styles.kt @@ -66,7 +66,7 @@ class Styles : Stylesheet() { init { root { focusColor = c(colors.primary) - faintFocusColor = c(colors.transparent) + faintFocusColor = c("${colors.primary}22") } label { @@ -98,7 +98,6 @@ class Styles : Stylesheet() { borderColor += box(c(colors.backgroundBorder)) } - header { wrapText = true fontSize = 20.px @@ -135,7 +134,6 @@ class Styles : Stylesheet() { } button { - and(default) { baseColor = c(colors.primary) textFill = Color.WHITESMOKE diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/style/StylesDark.kt b/src/main/kotlin/online/hudacek/fxradio/ui/style/StylesDark.kt index 5cdd2240..09b8895b 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/style/StylesDark.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/style/StylesDark.kt @@ -60,6 +60,11 @@ class StylesDark : Stylesheet() { } init { + root { + focusColor = c(colors.primary) + faintFocusColor = c("${colors.primary}22") + } + label { textFill = c(colors.label) fontSmoothingType = FontSmoothingType.GRAY @@ -69,11 +74,6 @@ class StylesDark : Stylesheet() { textFill = c(colors.label) } - textInput { - backgroundColor += c(colors.backgroundBorder) - textFill = Color.WHITESMOKE - } - playerMainBox { padding = box(10.0.px, 0.0.px) borderColor += box(c(colors.transparent), c(colors.transparent), c(colors.backgroundBorder), c(colors.transparent)) @@ -121,8 +121,8 @@ class StylesDark : Stylesheet() { unsafe("-fx-padding", raw("0")) unsafe("-fx-background-insets", raw("0")) unsafe("-fx-background-color", raw(colors.background)) - unsafe("-fx-border-color", raw("transparent")) - unsafe("-fx-faint-focus-color", raw("transparent")) + unsafe("-fx-border-color", raw(colors.transparent)) + unsafe("-fx-faint-focus-color", raw(colors.transparent)) } grayLabel { @@ -146,28 +146,43 @@ class StylesDark : Stylesheet() { } } + button { + and(default) { + baseColor = c(colors.primary) + textFill = Color.WHITESMOKE + } + baseColor = c(colors.backgroundSelected) + minWidth = 75.px + fontSize = 12.px + backgroundRadius += box(6.px) + borderRadius += box(6.px) + padding = box(4.px, 10.px, 4.px, 10.px) + textFill = c(colors.label) + } + primaryButton { backgroundColor += c(colors.primary) textFill = Color.WHITESMOKE } libraryListView { - backgroundColor += c("#333232") + backgroundColor += c(colors.background) unsafe("-fx-control-inner-background", Color.TRANSPARENT) } libraryListItem { fontSize = 12.px + prefHeight = 30.px textFill = c(colors.label) - backgroundColor += c("#333232") + backgroundColor += c(colors.background) backgroundRadius += box(6.px) borderRadius += box(6.px) and(selected) { - backgroundColor += c(colors.backgroundBorder) + backgroundColor += c(colors.backgroundSelected) borderColor += box(c(colors.backgroundBorder)) textFill = Color.WHITESMOKE } - padding = box(5.px, 10.px, 5.px, 15.px) + padding = box(6.px, 10.px, 6.px, 15.px) } libraryListItemTag { @@ -183,7 +198,7 @@ class StylesDark : Stylesheet() { } backgroundWhiteSmoke { - backgroundColor += c("#333232") + backgroundColor += c(colors.background) } backgroundWhite { @@ -194,6 +209,15 @@ class StylesDark : Stylesheet() { // Restyled default compontents // =================================================================== + tableView { + baseColor = c(colors.background) + tableRowCell { + and(selected) { + backgroundColor += c(colors.primary) + } + } + } + scrollBar { backgroundColor += c(colors.transparent) borderColor += box(c(colors.transparent)) @@ -251,9 +275,10 @@ class StylesDark : Stylesheet() { } datagridCell { + backgroundColor += c(colors.transparent) + borderColor += box(c(colors.transparent)) + padding = box(0.px, 5.px, 5.px, 5.px) - backgroundColor += c("#6e6e6e") - borderColor += box(c("#6e6e6e")) backgroundRadius += box(6.px) borderRadius += box(6.px) @@ -270,6 +295,7 @@ class StylesDark : Stylesheet() { textArea { fontFamily = "monospace" + textFill = Color.WHITESMOKE backgroundColor += c(colors.background) content { backgroundColor += c(colors.background) @@ -341,6 +367,9 @@ class StylesDark : Stylesheet() { } textField { + baseColor = c(colors.background) + textFill = Color.WHITESMOKE + promptTextFill = Color.WHITESMOKE backgroundRadius += box(6.px) borderRadius += box(6.px) } diff --git a/src/main/resources/Messages.properties b/src/main/resources/Messages.properties index 591726b7..53a64272 100644 --- a/src/main/resources/Messages.properties +++ b/src/main/resources/Messages.properties @@ -10,7 +10,7 @@ copy.nowPlaying=Copy track name close=Close cancel=Cancel save=Save -search=Search radio directory... +search=Search radio directory loading=Loading... countries=Countries library=Library From 4e7ea6957c243223d609829614f0b3fb65fbf921 Mon Sep 17 00:00:00 2001 From: jozef Date: Thu, 3 Jun 2021 00:36:44 +0200 Subject: [PATCH 14/27] Some love for the tests --- build.gradle | 67 ++++--- .../view/library/LibraryCountriesFragment.kt | 2 + ...nalityTests.kt => AppFunctionalityTest.kt} | 168 ++++++++++-------- .../fxradio/integration/DarkModeTests.kt | 58 ------ .../hudacek/fxradio/integration/Elements.kt | 33 ++++ .../hudacek/fxradio/integration/utils.kt | 19 +- 6 files changed, 176 insertions(+), 171 deletions(-) rename src/test/kotlin/online/hudacek/fxradio/integration/{BasicFunctionalityTests.kt => AppFunctionalityTest.kt} (64%) delete mode 100644 src/test/kotlin/online/hudacek/fxradio/integration/DarkModeTests.kt create mode 100644 src/test/kotlin/online/hudacek/fxradio/integration/Elements.kt diff --git a/build.gradle b/build.gradle index 03546e7d..3d664667 100644 --- a/build.gradle +++ b/build.gradle @@ -48,7 +48,6 @@ ext.versions = [ semver : "3.1.0", testfx : "4.0.16-alpha", junit : "5.4.0", - awaitility : "4.0.3", flagIcon : "1.1.0" ] @@ -56,48 +55,48 @@ dependencies { // Align versions of all Kotlin components compile platform('org.jetbrains.kotlin:kotlin-bom') - //local jars + // Local JAR files compile fileTree(include: ['*.jar'], dir: 'libs') - // Use the Kotlin JDK 8 standard library. - compile 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' - runtime group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: versions.kotlin - compile "org.jetbrains.kotlinx:kotlinx-coroutines-core:$versions.kotlinCoroutines" + // Use the Kotlin JDK 8 standard library + compile("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + compile("org.jetbrains.kotlin:kotlin-reflect") + compile("org.jetbrains.kotlinx:kotlinx-coroutines-core:$versions.kotlinCoroutines") - compile "no.tornado:tornadofx:$versions.tornadofx" - compile "org.controlsfx:controlsfx:$versions.controlsfx" - compile "no.tornado:tornadofx-controlsfx:$versions.tornadofxControlsfx" + compile("no.tornado:tornadofx:$versions.tornadofx") + compile("org.controlsfx:controlsfx:$versions.controlsfx") + compile("no.tornado:tornadofx-controlsfx:$versions.tornadofxControlsfx") - compile "com.squareup.retrofit2:retrofit:$versions.retrofit" - compile "com.squareup.retrofit2:adapter-rxjava2:$versions.retrofitAdapters" - compile "com.squareup.retrofit2:converter-gson:$versions.retrofitAdapters" - compile "com.squareup.okhttp3:logging-interceptor:$versions.retrofitLogger" + compile("com.squareup.retrofit2:retrofit:$versions.retrofit") + compile("com.squareup.retrofit2:adapter-rxjava2:$versions.retrofitAdapters") + compile("com.squareup.retrofit2:converter-gson:$versions.retrofitAdapters") + compile("com.squareup.okhttp3:logging-interceptor:$versions.retrofitLogger") - compile "io.github.microutils:kotlin-logging:$versions.kotlinLogging" - compile "org.slf4j:slf4j-api:$versions.slf4j" - compile "org.apache.logging.log4j:log4j-slf4j-impl:$versions.log4j2" - compile "org.apache.logging.log4j:log4j-api:$versions.log4j2" - compile "org.apache.logging.log4j:log4j-core:$versions.log4j2" + compile("io.github.microutils:kotlin-logging:$versions.kotlinLogging") + compile("org.slf4j:slf4j-api:$versions.slf4j") + compile("org.apache.logging.log4j:log4j-slf4j-impl:$versions.log4j2") + compile("org.apache.logging.log4j:log4j-api:$versions.log4j2") + compile("org.apache.logging.log4j:log4j-core:$versions.log4j2") - compile "commons-io:commons-io:$versions.commonsio" + compile("commons-io:commons-io:$versions.commonsio") - compile "com.github.thomasnield:rxkotlinfx:$versions.rxkotlinfx" - compile "org.xerial:sqlite-jdbc:$versions.sqliteJdbc" - compile "org.nield:rxkotlin-jdbc:$versions.rxkotlinJdbc" - compile "de.codecentric.centerdevice:centerdevice-nsmenufx:$versions.nsmenufx" + compile("com.github.thomasnield:rxkotlinfx:$versions.rxkotlinfx") + compile("org.xerial:sqlite-jdbc:$versions.sqliteJdbc") + compile("org.nield:rxkotlin-jdbc:$versions.rxkotlinJdbc") + compile("de.codecentric.centerdevice:centerdevice-nsmenufx:$versions.nsmenufx") - compile "org.codehaus.griffon.plugins:griffon-flagicons-javafx:$versions.flagIcon" + compile("org.codehaus.griffon.plugins:griffon-flagicons-javafx:$versions.flagIcon") //Players - compile "io.humble:humble-video-all:$versions.humble" - compile "uk.co.caprica:vlcj:$versions.vlcj" - - testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$versions.junit" - testCompile "org.junit.jupiter:junit-jupiter-api:$versions.junit" - testCompile "org.testfx:testfx-core:$versions.testfx" - testCompile "org.testfx:testfx-junit5:$versions.testfx" - testCompile "org.awaitility:awaitility:$versions.awaitility" - testCompile "org.testfx:openjfx-monocle:8u76-b04" + compile("io.humble:humble-video-all:$versions.humble") + compile("uk.co.caprica:vlcj:$versions.vlcj") + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$versions.junit") + testCompile("org.junit.jupiter:junit-jupiter-api:$versions.junit") + testCompile("org.junit.jupiter:junit-jupiter-params:$versions.junit") + testCompile("org.testfx:testfx-core:$versions.testfx") + testCompile("org.testfx:testfx-junit5:$versions.testfx") + testCompile("org.testfx:openjfx-monocle:8u76-b04") } sourceSets { @@ -137,7 +136,7 @@ jfx { additionalAppResources = 'src/main/deploy/additional' String runtimePath = System.getenv('JAVA_HOME') - if(runtimePath != null && System.getenv('FX_APPEND_PATH') != null) { + if (runtimePath != null && System.getenv('FX_APPEND_PATH') != null) { runtimePath = runtimePath + System.getenv('FX_APPEND_PATH') } logger.info('Runtime path is: {}', runtimePath) diff --git a/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryCountriesFragment.kt b/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryCountriesFragment.kt index 868dafff..ab92f5c5 100644 --- a/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryCountriesFragment.kt +++ b/src/main/kotlin/online/hudacek/fxradio/ui/view/library/LibraryCountriesFragment.kt @@ -46,6 +46,8 @@ class LibraryCountriesFragment(countriesProperty: ListProperty, } override val root = listview(countriesProperty) { + id = "libraryCountriesFragment" + /** * Set min/max size of listview based on its items size */ diff --git a/src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt b/src/test/kotlin/online/hudacek/fxradio/integration/AppFunctionalityTest.kt similarity index 64% rename from src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt rename to src/test/kotlin/online/hudacek/fxradio/integration/AppFunctionalityTest.kt index 7f5db312..7fc7ce2d 100644 --- a/src/test/kotlin/online/hudacek/fxradio/integration/BasicFunctionalityTests.kt +++ b/src/test/kotlin/online/hudacek/fxradio/integration/AppFunctionalityTest.kt @@ -16,21 +16,37 @@ package online.hudacek.fxradio.integration -import javafx.scene.control.Button +import javafx.scene.control.Label import javafx.scene.control.ListView import javafx.scene.control.Slider import javafx.stage.Stage +import mu.KotlinLogging import online.hudacek.fxradio.Config import online.hudacek.fxradio.FxRadio import online.hudacek.fxradio.FxRadioLight import online.hudacek.fxradio.api.ApiServiceProvider import online.hudacek.fxradio.api.stations.StationsApi +import online.hudacek.fxradio.api.stations.model.Country import online.hudacek.fxradio.api.stations.model.SearchBody import online.hudacek.fxradio.api.stations.model.Station +import online.hudacek.fxradio.integration.Elements.libraryCountriesFragment +import online.hudacek.fxradio.integration.Elements.libraryListView +import online.hudacek.fxradio.integration.Elements.nowPlayingLabel +import online.hudacek.fxradio.integration.Elements.playerControls +import online.hudacek.fxradio.integration.Elements.search +import online.hudacek.fxradio.integration.Elements.stationMessageHeader +import online.hudacek.fxradio.integration.Elements.stationMessageSubHeader +import online.hudacek.fxradio.integration.Elements.stationsDataGrid +import online.hudacek.fxradio.integration.Elements.stationsHistory +import online.hudacek.fxradio.integration.Elements.volumeMaxIcon +import online.hudacek.fxradio.integration.Elements.volumeMinIcon +import online.hudacek.fxradio.integration.Elements.volumeSlider import online.hudacek.fxradio.storage.db.Tables import online.hudacek.fxradio.viewmodel.LibraryItem +import online.hudacek.fxradio.viewmodel.LibraryViewModel import online.hudacek.fxradio.viewmodel.PlayerState import online.hudacek.fxradio.viewmodel.PlayerViewModel +import org.controlsfx.glyphfont.Glyph import org.junit.jupiter.api.* import org.junit.jupiter.api.extension.ExtendWith import org.testfx.api.FxAssert.verifyThat @@ -50,25 +66,14 @@ import tornadofx.find */ @TestMethodOrder(MethodOrderer.OrderAnnotation::class) @ExtendWith(ApplicationExtension::class) -class BasicFunctionalityTests { +@DisplayName("Basic functionality tests for the FXRadio application") +class AppFunctionalityTest { + + private val logger = KotlinLogging.logger {} + + private val robot: FxRobot = FxRobot() private lateinit var app: FxRadio - private lateinit var stage: Stage - - companion object { - //IDs - private const val nowPlayingLabel = "#nowStreaming" - private const val stationsDataGrid = "#stations" - private const val stationsHistory = "#stationsHistoryList" - private const val libraryListView = "#libraryListView" - private const val volumeMinIcon = "#volumeMinIcon" - private const val volumeMaxIcon = "#volumeMaxIcon" - private const val volumeSlider = "#volumeSlider" - private const val playerControls = "#playerControls" - private const val search = "#search" - private const val stationMessageHeader = "#stationMessageHeader" - private const val stationMessageSubHeader = "#stationMessageSubHeader" - } //Http Client, init only once needed private val service = ApiServiceProvider("https://${Config.API.fallbackApiServerURL}").get() @@ -82,32 +87,14 @@ class BasicFunctionalityTests { fun start(stage: Stage) { app = FxRadioLight() app.start(stage) - this.stage = stage } @Stop fun stop() = app.stop() - @Test - fun apiTest(robot: FxRobot) { - val stations = service.getTopStations().blockingGet() - Assertions.assertEquals(50, stations.size) - - //Wait for stations to load - val appStations = robot.find(stationsDataGrid) as DataGrid - - stations.forEachIndexed { index, station -> - //top 50 stations should not have empty URL and have name - - Assertions.assertTrue(station.name.isNotEmpty()) - Assertions.assertTrue(station.url_resolved != null) - Assertions.assertEquals(stations[index].name, appStations.items[index].name) - } - } - @Test @Order(1) - fun basicPlayPauseTest(robot: FxRobot) { + fun `app should play and pause selected station`() { //Verify app initial state verifyThat(nowPlayingLabel, hasLabel("Streaming stopped")) @@ -116,12 +103,8 @@ class BasicFunctionalityTests { //Wait for stations to load val stations = robot.find(stationsDataGrid) as DataGrid - waitFor(5) { - stations.isVisible && stations.items.size > 1 - } - //wait until loaded - sleep(2) + waitFor(5) { stations.isVisible && stations.items.size == 50 } //Avoid station names that start with # as it is query locator for ID val stationToClick = stations.items @@ -134,33 +117,46 @@ class BasicFunctionalityTests { WaitForAsyncUtils.waitForFxEvents() //Wait for stream start - waitFor(5) { - player.stateProperty.value == PlayerState.Playing - } + waitFor(5) { player.stateProperty.value == PlayerState.Playing } + + //Check that player has text with name of the station + verifyThat(nowPlayingLabel, hasLabel(stationToClick.name)) - val stopButton = robot.find(playerControls) as Button + val stopButton = robot.find(playerControls) as Glyph robot.clickOn(stopButton) WaitForAsyncUtils.waitForFxEvents() //Wait for stream stop - waitFor(2) { - player.stateProperty.value == PlayerState.Stopped - } + waitFor(2) { player.stateProperty.value == PlayerState.Stopped } verifyThat(nowPlayingLabel, hasLabel("Streaming stopped")) } @Test - fun testHistoryTab(robot: FxRobot) { + fun `api should return same results as in app`() { + val stations = service.getTopStations().blockingGet() + Assertions.assertEquals(50, stations.size) + + //Wait for stations to load + val appStations = robot.find(stationsDataGrid) as DataGrid + + stations.forEachIndexed { index, station -> + //top 50 stations should not have empty URL and have name + Assertions.assertTrue(station.name.isNotEmpty()) + Assertions.assertTrue(station.url_resolved != null) + Assertions.assertEquals(stations[index].name, appStations.items[index].name) + } + } + + @Test + fun `history tab should show same stations as in database`() { //Verify app initial state verifyThat(nowPlayingLabel, hasLabel("Streaming stopped")) //Wait for stations to load val libraries = robot.find(libraryListView) as ListView - waitFor(10) { - libraries.items.size == 3 - } + waitFor(10) { libraries.items.size == 3 } //wait until loaded sleep(2) @@ -177,44 +173,36 @@ class BasicFunctionalityTests { val stationsHistory = robot.find(stationsHistory) as ListView //Stations datagrid is not visible in history - waitFor(2) { - !stations.isVisible - } + waitFor(2) { !stations.isVisible } //Instead, list of stations is visible - waitFor(2) { - stationsHistory.isVisible - } + waitFor(2) { stationsHistory.isVisible } //Verify DB count equals actual list items count Assertions.assertEquals(historydbCount.toInt(), stationsHistory.items.size) } @Test - fun testVolumeSliderIcons(robot: FxRobot) { + fun `volume icons should change slider value`() { //Verify app initial state verifyThat(nowPlayingLabel, hasLabel("Streaming stopped")) - //Wait for stations to load + //Verify volume icons near slider are visible verifyThat(volumeMinIcon, visible()) verifyThat(volumeMaxIcon, visible()) //Verify volume icon click changes the slider value robot.clickOn(volumeMinIcon) val slider = robot.find(volumeSlider) as Slider - waitFor(2) { - slider.value == -30.0 - } + waitFor(2) { slider.value == -30.0 } //Verify volume icon click changes the slider value robot.clickOn(volumeMaxIcon) - waitFor(2) { - slider.value == 5.0 - } + waitFor(2) { slider.value == 5.0 } } @Test - fun testSearch(robot: FxRobot) { + fun `search should show correct stations`() { //Verify app initial state verifyThat(nowPlayingLabel, hasLabel("Streaming stopped")) @@ -235,16 +223,50 @@ class BasicFunctionalityTests { robot.enterText(search, "station") verifyThat(search, hasValue("station")) - //Wait until datagrid is loaded with stations for the provided searchquery - sleep(5) + //Wait until datagrid is loaded with stations for the provided search query + sleep(8) //Get stations in DataGrid val stations = robot.find(stationsDataGrid) as DataGrid verifyThat(stationMessageHeader, visible()) //Compare results from API and APP - println("Search results displayed: " + stations.items.size) - println("Search Results from API: " + stationResults.size) - Assertions.assertEquals(stationResults.size, stations.items.size) + logger.info { "Search results displayed: " + stations.items.size } + logger.info { "Search Results from API: " + stationResults.size } + waitFor(10) { stationResults.size == stations.items.size } + } + + @Test + fun `pinned country should show in pinned list`() { + //Verify app initial state + verifyThat(nowPlayingLabel, hasLabel("Streaming stopped")) + + //Simulate add country to pinned list + robot.interact { + val testCountry = Country("TestPinnedCountryName", 250) + val library = find() + logger.info { "Pin country $testCountry" } + library.pinCountry(testCountry) + } + + val countries = robot.findAll>(libraryCountriesFragment).toList() + + //Find "TestPinnedCountryName" in the list of items + val item = robot.from(countries[0]) + .lookup("TestPinnedCountryName") + .query