Skip to content

refactor: remove Supabase integration#700

Merged
utkarshdalal merged 1 commit intoutkarshdalal:masterfrom
ObfuscatedVoid:fix/no-tea
Mar 5, 2026
Merged

refactor: remove Supabase integration#700
utkarshdalal merged 1 commit intoutkarshdalal:masterfrom
ObfuscatedVoid:fix/no-tea

Conversation

@ObfuscatedVoid
Copy link
Contributor

@ObfuscatedVoid ObfuscatedVoid commented Mar 2, 2026

remove Supabase integration and update game feedback submission to use new API


Summary by cubic

Removed Supabase integration and migrated feedback submission and supporters fetching to the GameNative worker API with Play Integrity attestation. Added a small OkHttp-based API layer and cleaned up build/workflows.

  • New Features

    • OkHttp API layer (ApiResult, GameNativeApi) with Play Integrity tokens on POST requests.
    • GameRunApi.submit for feedback and SupportersApi.fetch for supporters.
  • Refactors

    • Deleted Supabase client/init, buildConfig fields, dependencies, and CI secrets.
    • Updated GameFeedbackUtils, SupportersUtils, SupportersDialog, and PluviaMain to use the new APIs.

Written for commit 0c471c8. Summary will update on new commits.

Summary by CodeRabbit

  • Refactor
    • Feedback submission now routes through the app's worker API with unified result handling for clearer success/failure and improved reliability.
    • Supporters list retrieval switched to the worker API with robust HTTP/network error handling and safer fallbacks.
    • Streamlined app startup by removing an unnecessary background initialization path, slightly improving launch performance.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 2, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8c44cd49-6c49-4382-82d9-5c116baeb768

📥 Commits

Reviewing files that changed from the base of the PR and between 6cd5e6e and 0c471c8.

📒 Files selected for processing (13)
  • .github/workflows/app-release-signed.yml
  • .github/workflows/pluvia-pr-check.yml
  • .github/workflows/tagged-release.yml
  • app/build.gradle.kts
  • app/src/main/java/app/gamenative/PluviaApp.kt
  • app/src/main/java/app/gamenative/api/ApiResult.kt
  • app/src/main/java/app/gamenative/api/GameNativeApi.kt
  • app/src/main/java/app/gamenative/api/GameRunApi.kt
  • app/src/main/java/app/gamenative/api/SupportersApi.kt
  • app/src/main/java/app/gamenative/ui/PluviaMain.kt
  • app/src/main/java/app/gamenative/ui/component/dialog/SupportersDialog.kt
  • app/src/main/java/app/gamenative/utils/GameFeedbackUtils.kt
  • app/src/main/java/app/gamenative/utils/SupportersUtils.kt

📝 Walkthrough

Walkthrough

Removed Supabase integration and CI credential injections; added a new worker-style REST API layer (GameNativeApi, GameRunApi, SupportersApi) and refactored feedback/supporters utilities to use the API instead of Supabase. Build and app initialization related Supabase wiring and dependencies were removed.

Changes

Cohort / File(s) Summary
CI/CD Workflow Configuration
.github/workflows/app-release-signed.yml, .github/workflows/pluvia-pr-check.yml, .github/workflows/tagged-release.yml
Removed SUPABASE_URL and SUPABASE_KEY injections into local.properties in credential steps.
Build Configuration
app/build.gradle.kts
Removed supabaseUrl/supabaseKey declarations, BuildConfig fields for SUPABASE_*, and Supabase/Ktor dependencies.
App Initialization
app/src/main/java/app/gamenative/PluviaApp.kt
Removed Supabase client, init/is-initialized accessor, related imports, and the startup initialization call.
API Result Type
app/src/main/java/app/gamenative/api/ApiResult.kt
Added sealed ApiResult<T> with Success, HttpError, and NetworkError variants for unified API responses.
Core API Layer
app/src/main/java/app/gamenative/api/GameNativeApi.kt
Added GameNativeApi with BASE_URL switching, lazy OkHttp client, executeRequest, and request builders (POST with PlayIntegrity token, GET).
Feature APIs
app/src/main/java/app/gamenative/api/GameRunApi.kt, app/src/main/java/app/gamenative/api/SupportersApi.kt
Added GameRunApi.submit() to post game-run JSON and parse response; added SupportersApi.fetch() to GET supporters and map results.
Feedback & Supporters Utilities
app/src/main/java/app/gamenative/utils/GameFeedbackUtils.kt, app/src/main/java/app/gamenative/utils/SupportersUtils.kt
Replaced Supabase usage with API calls (GameRunApi, SupportersApi), removed Supabase parameters/serializers, adapted error handling to ApiResult.
UI Adjustments
app/src/main/java/app/gamenative/ui/PluviaMain.kt, app/src/main/java/app/gamenative/ui/component/dialog/SupportersDialog.kt
Updated feedback submission callsites to new signature; simplified supporters dialog to call fetchKofiSupporters() directly.

Sequence Diagram(s)

sequenceDiagram
    participant Client as App UI / Utils
    participant GameNativeApi as GameNativeApi
    participant PlayIntegrity as Play Integrity
    participant OkHttp as OkHttpClient
    participant Server as Worker API Server

    Client->>GameNativeApi: buildPostRequest(url, body)
    GameNativeApi->>PlayIntegrity: request token
    PlayIntegrity-->>GameNativeApi: integrity token
    GameNativeApi->>Client: Request (with headers)

    Client->>GameNativeApi: executeRequest(request, parser)
    GameNativeApi->>OkHttp: execute(request)
    OkHttp->>Server: HTTP POST/GET
    Server-->>OkHttp: Response
    OkHttp-->>GameNativeApi: Response body

    alt 2xx Success
        GameNativeApi->>GameNativeApi: parse body
        GameNativeApi-->>Client: ApiResult.Success(data)
    else HTTP Error
        GameNativeApi->>GameNativeApi: parse error details
        GameNativeApi-->>Client: ApiResult.HttpError(code,message)
    else Network/Error
        GameNativeApi-->>Client: ApiResult.NetworkError(exception)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 I hopped away from Supabase trees,

Built a small API on the breeze,
Tokens tidy, requests so neat,
OkHttp carries every beat,
Carrot-powered responses, sweet! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and accurately summarizes the main change: removing Supabase integration from the codebase.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ObfuscatedVoid
Copy link
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 2, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/src/main/java/app/gamenative/ui/component/dialog/SupportersDialog.kt (1)

50-62: ⚠️ Potential issue | 🟠 Major

Error handling is ineffective; API failures will silently show "no supporters" instead of error state.

Looking at SupportersUtils.fetchKofiSupporters() in the relevant snippet, it catches ApiResult.HttpError and ApiResult.NetworkError internally, returning emptyList() rather than throwing. The try-catch here will never catch API failures, so hasError remains false and users see the "no supporters" message instead of the error UI.

Consider returning ApiResult directly from the fetch and handling it here, or returning a result wrapper that distinguishes between "no data" and "error":

Suggested approach: Handle ApiResult directly
+import app.gamenative.api.ApiResult
+import app.gamenative.api.SupportersApi

 LaunchedEffect(Unit) {
     isLoading = true
     hasError = false
-    try {
-        val data = withContext(Dispatchers.IO) {
-            fetchKofiSupporters()
-        }
-        supporters = data
-    } catch (e: Exception) {
-        hasError = true
+    val result = withContext(Dispatchers.IO) {
+        SupportersApi.fetch()
+    }
+    when (result) {
+        is ApiResult.Success -> {
+            supporters = result.data.map {
+                KofiSupporter(name = it.name, oneOff = it.oneOff, total = it.total)
+            }
+        }
+        is ApiResult.HttpError, is ApiResult.NetworkError -> {
+            hasError = true
+        }
     }
     isLoading = false
 }

Alternatively, modify fetchKofiSupporters() to return a wrapper or nullable result that the caller can distinguish.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/app/gamenative/ui/component/dialog/SupportersDialog.kt`
around lines 50 - 62, The current LaunchedEffect in SupportersDialog.kt calls
SupportersUtils.fetchKofiSupporters() which swallows ApiResult errors and
returns emptyList(), so the try/catch never triggers and hasError stays false;
change the contract so fetchKofiSupporters() returns an ApiResult (or a sealed
Result wrapper) instead of an empty list, then update the LaunchedEffect to call
the new fetch (or fetchKofiSupportersResult()) and branch on the result setting
supporters on success, hasError = true on ApiResult.HttpError/NetworkError, and
isLoading = false in finally; update any callers or rename symbols accordingly
so SupportersDialog.kt can distinguish "no supporters" vs "error".
🧹 Nitpick comments (2)
app/src/main/java/app/gamenative/api/GameNativeApi.kt (2)

19-24: Consider adding a write timeout.

The client configures connect and read timeouts but no write timeout. For POST requests with larger payloads, this could cause indefinite blocking during upload.

Suggested improvement
     val httpClient: OkHttpClient by lazy {
         OkHttpClient.Builder()
             .connectTimeout(10, TimeUnit.SECONDS)
             .readTimeout(10, TimeUnit.SECONDS)
+            .writeTimeout(10, TimeUnit.SECONDS)
             .build()
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/app/gamenative/api/GameNativeApi.kt` around lines 19 - 24,
Add a write timeout to the OkHttp client configuration to avoid uploads blocking
indefinitely: in the httpClient lazy block where OkHttpClient.Builder() is used
(currently calling connectTimeout and readTimeout), add a writeTimeout call
(e.g., .writeTimeout(10, TimeUnit.SECONDS) or an appropriate value) before
.build() so POSTs and large payload uploads have an enforced timeout.

52-69: Redundant Content-Type header.

Line 62 manually sets Content-Type: application/json, but this is already handled by toRequestBody(mediaType) on line 55. The header is redundant but harmless.

Suggested cleanup
         val builder = Request.Builder()
             .url(url)
             .post(requestBody)
-            .header("Content-Type", "application/json")

         if (integrityToken != null) {
             builder.header("X-Integrity-Token", integrityToken)
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/app/gamenative/api/GameNativeApi.kt` around lines 52 - 69,
In buildPostRequest suspend function remove the manual .header("Content-Type",
"application/json") call on the Request.Builder because requestBody created via
bodyString.toRequestBody(mediaType) already sets the Content-Type; update the
Request.Builder chain to rely on the requestBody and keep the integrity token
header logic as-is (i.e., remove the redundant header call referencing
"Content-Type" in the builder).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src/main/java/app/gamenative/api/GameNativeApi.kt`:
- Around line 42-49: The parser lambda's exceptions are being lumped into
ApiResult.NetworkError; add a dedicated parse error case and catch
parser-related exceptions separately: introduce an
ApiResult.ParseError(Throwable) variant (or similar) and, in GameNativeApi's
try/catch around ApiResult.Success(parser(body)), add a catch specifically for
parsing exceptions (e.g., com.squareup.moshi.JsonDataException /
JsonEncodingException / com.google.gson.JsonParseException /
org.json.JSONException or a general JsonParseException) that returns
ApiResult.ParseError(e), keep the existing catch(IOException) =>
ApiResult.NetworkError(e) and a final generic catch for other unexpected
exceptions.

---

Outside diff comments:
In `@app/src/main/java/app/gamenative/ui/component/dialog/SupportersDialog.kt`:
- Around line 50-62: The current LaunchedEffect in SupportersDialog.kt calls
SupportersUtils.fetchKofiSupporters() which swallows ApiResult errors and
returns emptyList(), so the try/catch never triggers and hasError stays false;
change the contract so fetchKofiSupporters() returns an ApiResult (or a sealed
Result wrapper) instead of an empty list, then update the LaunchedEffect to call
the new fetch (or fetchKofiSupportersResult()) and branch on the result setting
supporters on success, hasError = true on ApiResult.HttpError/NetworkError, and
isLoading = false in finally; update any callers or rename symbols accordingly
so SupportersDialog.kt can distinguish "no supporters" vs "error".

---

Nitpick comments:
In `@app/src/main/java/app/gamenative/api/GameNativeApi.kt`:
- Around line 19-24: Add a write timeout to the OkHttp client configuration to
avoid uploads blocking indefinitely: in the httpClient lazy block where
OkHttpClient.Builder() is used (currently calling connectTimeout and
readTimeout), add a writeTimeout call (e.g., .writeTimeout(10, TimeUnit.SECONDS)
or an appropriate value) before .build() so POSTs and large payload uploads have
an enforced timeout.
- Around line 52-69: In buildPostRequest suspend function remove the manual
.header("Content-Type", "application/json") call on the Request.Builder because
requestBody created via bodyString.toRequestBody(mediaType) already sets the
Content-Type; update the Request.Builder chain to rely on the requestBody and
keep the integrity token header logic as-is (i.e., remove the redundant header
call referencing "Content-Type" in the builder).

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 08a27a8 and 6cd5e6e.

📒 Files selected for processing (13)
  • .github/workflows/app-release-signed.yml
  • .github/workflows/pluvia-pr-check.yml
  • .github/workflows/tagged-release.yml
  • app/build.gradle.kts
  • app/src/main/java/app/gamenative/PluviaApp.kt
  • app/src/main/java/app/gamenative/api/ApiResult.kt
  • app/src/main/java/app/gamenative/api/GameNativeApi.kt
  • app/src/main/java/app/gamenative/api/GameRunApi.kt
  • app/src/main/java/app/gamenative/api/SupportersApi.kt
  • app/src/main/java/app/gamenative/ui/PluviaMain.kt
  • app/src/main/java/app/gamenative/ui/component/dialog/SupportersDialog.kt
  • app/src/main/java/app/gamenative/utils/GameFeedbackUtils.kt
  • app/src/main/java/app/gamenative/utils/SupportersUtils.kt
💤 Files with no reviewable changes (5)
  • .github/workflows/app-release-signed.yml
  • app/src/main/java/app/gamenative/PluviaApp.kt
  • .github/workflows/tagged-release.yml
  • app/build.gradle.kts
  • .github/workflows/pluvia-pr-check.yml

Comment on lines +42 to +49
ApiResult.Success(parser(body))
} catch (e: IOException) {
Timber.tag("GameNativeApi").e(e, "Network error: ${e.message}")
ApiResult.NetworkError(e)
} catch (e: Exception) {
Timber.tag("GameNativeApi").e(e, "Unexpected error: ${e.message}")
ApiResult.NetworkError(e)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Parser exceptions are incorrectly classified as NetworkError.

If the parser lambda throws an exception (e.g., JSON parsing failure on a successful response), it gets caught and wrapped as NetworkError, which is semantically misleading. Consider adding a dedicated error type for parsing failures or catching parser exceptions separately.

Suggested fix
     inline fun <T> executeRequest(request: Request, parser: (String) -> T): ApiResult<T> {
         return try {
             val response = httpClient.newCall(request).execute()
             val body = response.body?.string() ?: ""

             if (!response.isSuccessful) {
                 val message = try {
                     val json = JSONObject(body)
                     json.optJSONObject("error")?.optString("message") ?: body
                 } catch (_: Exception) {
                     body
                 }
                 Timber.tag("GameNativeApi").w("HTTP ${response.code}: $message")
                 return ApiResult.HttpError(response.code, message)
             }

-            ApiResult.Success(parser(body))
+            try {
+                ApiResult.Success(parser(body))
+            } catch (e: Exception) {
+                Timber.tag("GameNativeApi").e(e, "Failed to parse response: ${e.message}")
+                ApiResult.NetworkError(e) // Or introduce ApiResult.ParseError
+            }
         } catch (e: IOException) {
             Timber.tag("GameNativeApi").e(e, "Network error: ${e.message}")
             ApiResult.NetworkError(e)
         } catch (e: Exception) {
             Timber.tag("GameNativeApi").e(e, "Unexpected error: ${e.message}")
             ApiResult.NetworkError(e)
         }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ApiResult.Success(parser(body))
} catch (e: IOException) {
Timber.tag("GameNativeApi").e(e, "Network error: ${e.message}")
ApiResult.NetworkError(e)
} catch (e: Exception) {
Timber.tag("GameNativeApi").e(e, "Unexpected error: ${e.message}")
ApiResult.NetworkError(e)
}
try {
ApiResult.Success(parser(body))
} catch (e: Exception) {
Timber.tag("GameNativeApi").e(e, "Failed to parse response: ${e.message}")
ApiResult.NetworkError(e)
}
} catch (e: IOException) {
Timber.tag("GameNativeApi").e(e, "Network error: ${e.message}")
ApiResult.NetworkError(e)
} catch (e: Exception) {
Timber.tag("GameNativeApi").e(e, "Unexpected error: ${e.message}")
ApiResult.NetworkError(e)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/app/gamenative/api/GameNativeApi.kt` around lines 42 - 49,
The parser lambda's exceptions are being lumped into ApiResult.NetworkError; add
a dedicated parse error case and catch parser-related exceptions separately:
introduce an ApiResult.ParseError(Throwable) variant (or similar) and, in
GameNativeApi's try/catch around ApiResult.Success(parser(body)), add a catch
specifically for parsing exceptions (e.g., com.squareup.moshi.JsonDataException
/ JsonEncodingException / com.google.gson.JsonParseException /
org.json.JSONException or a general JsonParseException) that returns
ApiResult.ParseError(e), keep the existing catch(IOException) =>
ApiResult.NetworkError(e) and a final generic catch for other unexpected
exceptions.

@utkarshdalal
Copy link
Owner

Looks reasonable.

@utkarshdalal utkarshdalal marked this pull request as ready for review March 5, 2026 00:28
@utkarshdalal utkarshdalal merged commit 6f999e3 into utkarshdalal:master Mar 5, 2026
2 of 3 checks passed
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 13 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="app/src/main/java/app/gamenative/api/SupportersApi.kt">

<violation number="1" location="app/src/main/java/app/gamenative/api/SupportersApi.kt:18">
P2: Strict JSON parsing can make supporters fetch brittle to backend response shape changes. Consider using `optJSONArray("supporters")` with null checks to gracefully degrade to an empty list instead of throwing `JSONException` on missing/malformed fields.</violation>
</file>

Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Add one-off context when rerunning by tagging @cubic-dev-ai with guidance or docs links (including llms.txt)
  • Ask questions if you need clarification on any suggestion

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.


GameNativeApi.executeRequest(request) { responseBody ->
val json = JSONObject(responseBody)
val array = json.getJSONArray("supporters")
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Strict JSON parsing can make supporters fetch brittle to backend response shape changes. Consider using optJSONArray("supporters") with null checks to gracefully degrade to an empty list instead of throwing JSONException on missing/malformed fields.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/src/main/java/app/gamenative/api/SupportersApi.kt, line 18:

<comment>Strict JSON parsing can make supporters fetch brittle to backend response shape changes. Consider using `optJSONArray("supporters")` with null checks to gracefully degrade to an empty list instead of throwing `JSONException` on missing/malformed fields.</comment>

<file context>
@@ -0,0 +1,29 @@
+
+        GameNativeApi.executeRequest(request) { responseBody ->
+            val json = JSONObject(responseBody)
+            val array = json.getJSONArray("supporters")
+            (0 until array.length()).map { i ->
+                val item = array.getJSONObject(i)
</file context>
Fix with Cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants