Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 0 additions & 45 deletions app/src/main/java/app/gamenative/PluviaApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -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")
}
}
}
}
48 changes: 48 additions & 0 deletions app/src/main/java/app/gamenative/di/SupabaseModule.kt
Original file line number Diff line number Diff line change
@@ -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")
}
}
}
Original file line number Diff line number Diff line change
@@ -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<String>,
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<KofiSupporter> {
val client = supabaseClient ?: return emptyList()
return fetchKofiSupporters(client)
}
}
4 changes: 1 addition & 3 deletions app/src/main/java/app/gamenative/ui/PluviaMain.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<List<KofiSupporter>>(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) {
Expand Down
18 changes: 18 additions & 0 deletions app/src/main/java/app/gamenative/ui/model/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -176,6 +178,22 @@ class MainViewModel @Inject constructor(
appTheme.currentPalette = value
}

suspend fun submitGameFeedback(
context: Context,
appId: String,
rating: Int,
tags: List<String>,
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) }
}
Expand Down
40 changes: 40 additions & 0 deletions app/src/main/java/app/gamenative/ui/model/SupportersViewModel.kt
Original file line number Diff line number Diff line change
@@ -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<List<KofiSupporter>>(emptyList())
val supporters: StateFlow<List<KofiSupporter>> = _supporters.asStateFlow()

private val _isLoading = MutableStateFlow(false)
val isLoading: StateFlow<Boolean> = _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
}
}
}