From 175594e5e8a78b82050850587e2571a87851122d Mon Sep 17 00:00:00 2001 From: woozer Date: Mon, 26 Jan 2026 17:48:20 +0100 Subject: [PATCH] Just testing DI rework --- app/src/main/java/app/gamenative/PluviaApp.kt | 45 ----------------- .../java/app/gamenative/di/SupabaseModule.kt | 48 +++++++++++++++++++ .../repository/GameFeedbackRepository.kt | 35 ++++++++++++++ .../main/java/app/gamenative/ui/PluviaMain.kt | 4 +- .../ui/component/dialog/SupportersDialog.kt | 21 ++++---- .../app/gamenative/ui/model/MainViewModel.kt | 18 +++++++ .../ui/model/SupportersViewModel.kt | 40 ++++++++++++++++ 7 files changed, 151 insertions(+), 60 deletions(-) create mode 100644 app/src/main/java/app/gamenative/di/SupabaseModule.kt create mode 100644 app/src/main/java/app/gamenative/repository/GameFeedbackRepository.kt create mode 100644 app/src/main/java/app/gamenative/ui/model/SupportersViewModel.kt diff --git a/app/src/main/java/app/gamenative/PluviaApp.kt b/app/src/main/java/app/gamenative/PluviaApp.kt index 70c090cb1..63a4163ab 100644 --- a/app/src/main/java/app/gamenative/PluviaApp.kt +++ b/app/src/main/java/app/gamenative/PluviaApp.kt @@ -23,12 +23,6 @@ import com.posthog.android.PostHogAndroid import com.posthog.android.PostHogAndroidConfig // Supabase imports -import io.github.jan.supabase.SupabaseClient -import io.github.jan.supabase.annotations.SupabaseInternal -import io.github.jan.supabase.createSupabaseClient -import io.github.jan.supabase.postgrest.Postgrest -import io.github.jan.supabase.network.supabaseApi -import io.ktor.client.plugins.HttpTimeout import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob @@ -95,15 +89,6 @@ class PluviaApp : SplitCompatApplication() { } PostHogAndroid.setup(this, postHogConfig) - // Initialize Supabase client - try { - initSupabase() - Timber.d("Supabase client initialized with URL: ${BuildConfig.SUPABASE_URL}") - } catch (e: Exception) { - Timber.e(e, "Failed to initialize Supabase client: ${e.message}") - e.printStackTrace() - } - // Initialize GOG service appScope.launch { try { @@ -127,35 +112,5 @@ class PluviaApp : SplitCompatApplication() { var inputControlsManager: InputControlsManager? = null var touchpadView: TouchpadView? = null - // Supabase client for game feedback - lateinit var supabase: SupabaseClient - private set - - // Initialize Supabase client - @OptIn(SupabaseInternal::class) - fun initSupabase() { - Timber.d("Initializing Supabase client with URL: ${BuildConfig.SUPABASE_URL}") - if (BuildConfig.SUPABASE_URL.isBlank() || BuildConfig.SUPABASE_KEY.isBlank()) { - Timber.e("Invalid Supabase URL or key - URL: ${BuildConfig.SUPABASE_URL}, key empty: ${BuildConfig.SUPABASE_KEY.isBlank()}") - throw IllegalStateException("Supabase URL or key is empty") - } - - supabase = createSupabaseClient( - supabaseUrl = BuildConfig.SUPABASE_URL, - supabaseKey = BuildConfig.SUPABASE_KEY - ) { - Timber.d("Configuring Supabase client") - httpConfig { - Timber.d("Setting up HTTP timeouts") - install(HttpTimeout) { - requestTimeoutMillis = 30_000 // overall call - connectTimeoutMillis = 15_000 // TCP handshake / TLS - socketTimeoutMillis = 30_000 // idle socket - } - } - install(Postgrest) - Timber.d("Postgrest plugin installed") - } - } } } diff --git a/app/src/main/java/app/gamenative/di/SupabaseModule.kt b/app/src/main/java/app/gamenative/di/SupabaseModule.kt new file mode 100644 index 000000000..7ef8cdab4 --- /dev/null +++ b/app/src/main/java/app/gamenative/di/SupabaseModule.kt @@ -0,0 +1,48 @@ +package app.gamenative.di + +import app.gamenative.BuildConfig +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import io.github.jan.supabase.SupabaseClient +import io.github.jan.supabase.annotations.SupabaseInternal +import io.github.jan.supabase.createSupabaseClient +import io.github.jan.supabase.postgrest.Postgrest +import io.ktor.client.plugins.HttpTimeout +import javax.inject.Singleton +import timber.log.Timber + +@InstallIn(SingletonComponent::class) +@Module +class SupabaseModule { + @OptIn(SupabaseInternal::class) + @Provides + @Singleton + fun provideSupabaseClient(): SupabaseClient? { + Timber.d("Initializing Supabase client with URL: ${BuildConfig.SUPABASE_URL}") + if (BuildConfig.SUPABASE_URL.isBlank() || BuildConfig.SUPABASE_KEY.isBlank()) { + Timber.e( + "Invalid Supabase URL or key - URL: ${BuildConfig.SUPABASE_URL}, key empty: ${BuildConfig.SUPABASE_KEY.isBlank()}", + ) + return null + } + + return createSupabaseClient( + supabaseUrl = BuildConfig.SUPABASE_URL, + supabaseKey = BuildConfig.SUPABASE_KEY, + ) { + Timber.d("Configuring Supabase client") + httpConfig { + Timber.d("Setting up HTTP timeouts") + install(HttpTimeout) { + requestTimeoutMillis = 30_000 // overall call + connectTimeoutMillis = 15_000 // TCP handshake / TLS + socketTimeoutMillis = 30_000 // idle socket + } + } + install(Postgrest) + Timber.d("Postgrest plugin installed") + } + } +} diff --git a/app/src/main/java/app/gamenative/repository/GameFeedbackRepository.kt b/app/src/main/java/app/gamenative/repository/GameFeedbackRepository.kt new file mode 100644 index 000000000..ee7938add --- /dev/null +++ b/app/src/main/java/app/gamenative/repository/GameFeedbackRepository.kt @@ -0,0 +1,35 @@ +package app.gamenative.repository + +import android.content.Context +import app.gamenative.utils.GameFeedbackUtils +import app.gamenative.utils.KofiSupporter +import app.gamenative.utils.fetchKofiSupporters +import io.github.jan.supabase.SupabaseClient +import javax.inject.Inject + +class SupabaseRepository @Inject constructor( + private val supabaseClient: SupabaseClient?, +) { + suspend fun submitGameFeedback( + context: Context, + appId: String, + rating: Int, + tags: List, + notes: String?, + ): Boolean { + val client = supabaseClient ?: return false + return GameFeedbackUtils.submitGameFeedback( + context = context, + supabase = client, + appId = appId, + rating = rating, + tags = tags, + notes = notes, + ) + } + + suspend fun fetchSupporters(): List { + val client = supabaseClient ?: return emptyList() + return fetchKofiSupporters(client) + } +} diff --git a/app/src/main/java/app/gamenative/ui/PluviaMain.kt b/app/src/main/java/app/gamenative/ui/PluviaMain.kt index fdf63ba45..839df966f 100644 --- a/app/src/main/java/app/gamenative/ui/PluviaMain.kt +++ b/app/src/main/java/app/gamenative/ui/PluviaMain.kt @@ -69,7 +69,6 @@ import app.gamenative.ui.screen.xserver.XServerScreen import app.gamenative.ui.theme.PluviaTheme import app.gamenative.utils.ContainerUtils import app.gamenative.utils.CustomGameScanner -import app.gamenative.utils.GameFeedbackUtils import app.gamenative.utils.IntentLaunchManager import app.gamenative.utils.UpdateChecker import app.gamenative.utils.UpdateInfo @@ -849,9 +848,8 @@ fun PluviaMain( Timber.d("GameFeedback: Inside coroutine scope") try { Timber.d("GameFeedback: Calling submitGameFeedback with rating=${feedbackState.rating}") - val result = GameFeedbackUtils.submitGameFeedback( + val result = viewModel.submitGameFeedback( context = context, - supabase = PluviaApp.supabase, appId = appId, rating = feedbackState.rating, tags = feedbackState.selectedTags.toList(), diff --git a/app/src/main/java/app/gamenative/ui/component/dialog/SupportersDialog.kt b/app/src/main/java/app/gamenative/ui/component/dialog/SupportersDialog.kt index 60a5dba15..b73675945 100644 --- a/app/src/main/java/app/gamenative/ui/component/dialog/SupportersDialog.kt +++ b/app/src/main/java/app/gamenative/ui/component/dialog/SupportersDialog.kt @@ -33,30 +33,27 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import app.gamenative.PluviaApp import app.gamenative.R +import app.gamenative.ui.model.SupportersViewModel import app.gamenative.utils.KofiSupporter -import app.gamenative.utils.fetchKofiSupporters -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle @Composable fun SupportersDialog( visible: Boolean, onDismiss: () -> Unit, + viewModel: SupportersViewModel = hiltViewModel(), ) { if (!visible) return - var supporters by remember { mutableStateOf>(emptyList()) } - var isLoading by remember { mutableStateOf(true) } + val supporters by viewModel.supporters.collectAsStateWithLifecycle() + val isLoading by viewModel.isLoading.collectAsStateWithLifecycle() - LaunchedEffect(Unit) { - isLoading = true - val data = withContext(Dispatchers.IO) { - fetchKofiSupporters(PluviaApp.supabase) + LaunchedEffect(visible) { + if (visible) { + viewModel.loadSupporters() } - supporters = data - isLoading = false } val members = remember(supporters) { diff --git a/app/src/main/java/app/gamenative/ui/model/MainViewModel.kt b/app/src/main/java/app/gamenative/ui/model/MainViewModel.kt index 42fc73f23..767e64684 100644 --- a/app/src/main/java/app/gamenative/ui/model/MainViewModel.kt +++ b/app/src/main/java/app/gamenative/ui/model/MainViewModel.kt @@ -41,10 +41,12 @@ import timber.log.Timber import kotlinx.coroutines.Job import app.gamenative.utils.ContainerUtils import kotlinx.coroutines.async +import app.gamenative.repository.SupabaseRepository @HiltViewModel class MainViewModel @Inject constructor( private val appTheme: IAppTheme, + private val supabaseRepository: SupabaseRepository, ) : ViewModel() { sealed class MainUiEvent { @@ -176,6 +178,22 @@ class MainViewModel @Inject constructor( appTheme.currentPalette = value } + suspend fun submitGameFeedback( + context: Context, + appId: String, + rating: Int, + tags: List, + notes: String?, + ): Boolean { + return supabaseRepository.submitGameFeedback( + context = context, + appId = appId, + rating = rating, + tags = tags, + notes = notes, + ) + } + fun setAnnoyingDialogShown(value: Boolean) { _state.update { it.copy(annoyingDialogShown = value) } } diff --git a/app/src/main/java/app/gamenative/ui/model/SupportersViewModel.kt b/app/src/main/java/app/gamenative/ui/model/SupportersViewModel.kt new file mode 100644 index 000000000..8130ca012 --- /dev/null +++ b/app/src/main/java/app/gamenative/ui/model/SupportersViewModel.kt @@ -0,0 +1,40 @@ +package app.gamenative.ui.model + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import app.gamenative.repository.SupabaseRepository +import app.gamenative.utils.KofiSupporter +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +@HiltViewModel +class SupportersViewModel @Inject constructor( + private val supabaseRepository: SupabaseRepository, +) : ViewModel() { + private val _supporters = MutableStateFlow>(emptyList()) + val supporters: StateFlow> = _supporters.asStateFlow() + + private val _isLoading = MutableStateFlow(false) + val isLoading: StateFlow = _isLoading.asStateFlow() + + fun loadSupporters() { + if (_isLoading.value || _supporters.value.isNotEmpty()) { + return + } + + viewModelScope.launch { + _isLoading.value = true + val data = withContext(Dispatchers.IO) { + supabaseRepository.fetchSupporters() + } + _supporters.value = data + _isLoading.value = false + } + } +}