Remove Personal Nexus API Keys and Temporarily Disable Nexus Support#1763
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
💤 Files with no reviewable changes (1)
📝 WalkthroughWalkthroughNexus integration now uses injected bearer-token authentication, removes persisted API-key handling, gates online workflows behind a disabled integration status, pauses affected imports, updates UI messaging, and adds localized temporary-unavailability text. ChangesNexus online access transition
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant NexusModsDialog
participant NexusModImportService
participant NexusApiClient
participant NexusAPI
User->>NexusModsDialog: start Nexus import
NexusModsDialog->>NexusApiClient: request authenticated data
NexusApiClient->>NexusAPI: send bearer-token request
NexusAPI-->>NexusApiClient: return response
NexusModsDialog->>NexusModImportService: enqueue import
NexusModImportService->>NexusModImportService: pause link-dependent work when access is unavailable
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
app/src/main/java/app/gamenative/service/NexusModImportService.kt (1)
182-233: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated resumable-install pause/filter logic across three methods.
The "query resumable installs → filter registered downloads → partition by
hasCompletePendingArchive→ pausedownloadsNeedingLinksviaNexusImportState.pauseWhileOnlineAccessUnavailable" sequence is repeated with small variations in the instanceresumeInterruptedImports(), the companionresumeInterruptedImports(context), andpauseInterruptedImports(context). Extracting a shared private helper (e.g. returning(completeArchives, downloadsNeedingLinks)and a separatepauseDownloads(dao, downloadsNeedingLinks, message)helper) would reduce the risk of these three copies silently diverging as this logic evolves (e.g. once OAuth rolls out).♻️ Sketch of a shared helper
private suspend fun interruptedInstalls(dao: NexusModInstallDao): List<ModInstall> = NexusModManager.resumableImportStatuses .flatMap { status -> dao.getInstallsByStatus(status) } .distinctBy { it.installId } .filter { ModDownloadRegistry.get(it.installId) == null } private suspend fun pauseDownloadsNeedingLinks( dao: NexusModInstallDao, downloads: List<ModInstall>, message: String, ) { downloads.forEach { install -> val paused = NexusImportState.pauseWhileOnlineAccessUnavailable(install, message) if (paused != install) dao.upsertInstall(paused) } }Also applies to: 406-427, 441-453
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/src/main/java/app/gamenative/service/NexusModImportService.kt` around lines 182 - 233, Extract the duplicated resumable-install query, registered-download filtering, and complete-archive partitioning used by the instance and companion resumeInterruptedImports() methods and pauseInterruptedImports() into shared private helpers in the containing class/object. Add a separate helper for applying NexusImportState.pauseWhileOnlineAccessUnavailable and persisting changed installs, then update all three callers to use these helpers while preserving their existing online-access and authorization-specific filters.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@app/src/main/java/app/gamenative/service/NexusModImportService.kt`:
- Around line 182-233: Extract the duplicated resumable-install query,
registered-download filtering, and complete-archive partitioning used by the
instance and companion resumeInterruptedImports() methods and
pauseInterruptedImports() into shared private helpers in the containing
class/object. Add a separate helper for applying
NexusImportState.pauseWhileOnlineAccessUnavailable and persisting changed
installs, then update all three callers to use these helpers while preserving
their existing online-access and authorization-specific filters.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: b92f1ecb-7493-466b-ad1b-83e41cc5409b
📒 Files selected for processing (27)
app/src/main/java/app/gamenative/MainActivity.ktapp/src/main/java/app/gamenative/PrefManager.ktapp/src/main/java/app/gamenative/mods/NexusApiClient.ktapp/src/main/java/app/gamenative/mods/NexusImportState.ktapp/src/main/java/app/gamenative/mods/NexusIntegrationStatus.ktapp/src/main/java/app/gamenative/mods/NexusUrlParser.ktapp/src/main/java/app/gamenative/service/NexusModImportService.ktapp/src/main/java/app/gamenative/ui/component/dialog/NexusModsDialog.ktapp/src/main/java/app/gamenative/ui/component/dialog/NexusModsImportSections.ktapp/src/main/res/values-da/strings.xmlapp/src/main/res/values-de/strings.xmlapp/src/main/res/values-es/strings.xmlapp/src/main/res/values-fr/strings.xmlapp/src/main/res/values-it/strings.xmlapp/src/main/res/values-ja/strings.xmlapp/src/main/res/values-ko/strings.xmlapp/src/main/res/values-pl/strings.xmlapp/src/main/res/values-pt-rBR/strings.xmlapp/src/main/res/values-ro/strings.xmlapp/src/main/res/values-ru/strings.xmlapp/src/main/res/values-uk/strings.xmlapp/src/main/res/values-zh-rCN/strings.xmlapp/src/main/res/values-zh-rTW/strings.xmlapp/src/main/res/values/strings.xmlapp/src/test/java/app/gamenative/NexusPersonalApiKeyMigrationTest.ktapp/src/test/java/app/gamenative/mods/NexusApiClientTest.ktapp/src/test/java/app/gamenative/mods/NexusImportStateTest.kt
There was a problem hiding this comment.
2 issues found across 27 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/mods/NexusImportState.kt">
<violation number="1" location="app/src/main/java/app/gamenative/mods/NexusImportState.kt:204">
P3: This newly changed user-facing wording cannot be localized while it remains an inline literal; representing the message through a string resource (or a resource ID resolved at the UI boundary) would keep Nexus errors translatable.
(Based on your team's feedback about localizing user-facing strings.) [FEEDBACK_USED]</violation>
</file>
<file name="app/src/main/java/app/gamenative/ui/component/dialog/NexusModsDialog.kt">
<violation number="1" location="app/src/main/java/app/gamenative/ui/component/dialog/NexusModsDialog.kt:1531">
P2: Re-enabling Nexus online access will leave this account lookup unauthenticated because the default `NexusApiClient()` has no OAuth token provider. Wiring the registered OAuth credentials into the client before flipping `ONLINE_ACCESS_AVAILABLE` would prevent all Nexus imports from failing at account validation.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| if (apiKey.trim() == requestedKey) nexusUserInfo = it | ||
| } | ||
| PrefManager.saveNexusApiKey(requestedKey) | ||
| val user = knownUser ?: apiClient.getCurrentUser().also { nexusUserInfo = it } |
There was a problem hiding this comment.
P2: Re-enabling Nexus online access will leave this account lookup unauthenticated because the default NexusApiClient() has no OAuth token provider. Wiring the registered OAuth credentials into the client before flipping ONLINE_ACCESS_AVAILABLE would prevent all Nexus imports from failing at account validation.
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/ui/component/dialog/NexusModsDialog.kt, line 1531:
<comment>Re-enabling Nexus online access will leave this account lookup unauthenticated because the default `NexusApiClient()` has no OAuth token provider. Wiring the registered OAuth credentials into the client before flipping `ONLINE_ACCESS_AVAILABLE` would prevent all Nexus imports from failing at account validation.</comment>
<file context>
@@ -1574,18 +1520,15 @@ fun NexusModsDialog(
- if (apiKey.trim() == requestedKey) nexusUserInfo = it
- }
- PrefManager.saveNexusApiKey(requestedKey)
+ val user = knownUser ?: apiClient.getCurrentUser().also { nexusUserInfo = it }
val authorizationUserId = reference.downloadAuthorization?.userId
if (authorizationUserId != null && authorizationUserId != user.userId) {
</file context>
| val base = when (error.reason) { | ||
| NexusApiErrorReason.AUTHENTICATION -> "Nexus rejected the API key. Reconnect the Nexus account and try again." | ||
| NexusApiErrorReason.AUTHENTICATION -> | ||
| "Nexus account authorization is unavailable or was rejected. Reconnect the Nexus account and try again." |
There was a problem hiding this comment.
P3: This newly changed user-facing wording cannot be localized while it remains an inline literal; representing the message through a string resource (or a resource ID resolved at the UI boundary) would keep Nexus errors translatable.
(Based on your team's feedback about localizing user-facing strings.)
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/mods/NexusImportState.kt, line 204:
<comment>This newly changed user-facing wording cannot be localized while it remains an inline literal; representing the message through a string resource (or a resource ID resolved at the UI boundary) would keep Nexus errors translatable.
(Based on your team's feedback about localizing user-facing strings.) </comment>
<file context>
@@ -177,7 +200,8 @@ internal object NexusImportState {
val base = when (error.reason) {
- NexusApiErrorReason.AUTHENTICATION -> "Nexus rejected the API key. Reconnect the Nexus account and try again."
+ NexusApiErrorReason.AUTHENTICATION ->
+ "Nexus account authorization is unavailable or was rejected. Reconnect the Nexus account and try again."
NexusApiErrorReason.FORBIDDEN -> "Nexus denied access to this resource for the current account."
NexusApiErrorReason.DOWNLOAD_AUTHORIZATION_REQUIRED ->
</file context>
There was a problem hiding this comment.
All reported issues were addressed across 6 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| // Keep this cleanup for several releases so restored backups are also scrubbed. | ||
| // No production code reads or sends the retired value while removal runs on IO. | ||
| if (retiredNexusPersonalApiKeyCleanupScheduled.compareAndSet(false, true)) { | ||
| val initializedDataStore = dataStore | ||
| scope.launch { | ||
| runCatching { | ||
| initializedDataStore.edit { preferences -> | ||
| if (preferences.hasRetiredNexusPersonalApiKey()) { | ||
| Timber.i("Removing retired Nexus Personal API key") | ||
| preferences.removeRetiredNexusPersonalApiKey() | ||
| } | ||
| } | ||
| }.onFailure { | ||
| retiredNexusPersonalApiKeyCleanupScheduled.set(false) | ||
| Timber.w(it, "Failed to remove retired Nexus Personal API key; will retry") | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
is this necessary? Let's not add additional permanent logic to the app, the feature was never released fully so we can assume there's nothing to clean up
| if (!NexusIntegrationStatus.ONLINE_ACCESS_AVAILABLE) { | ||
| Timber.i("[NexusDownload]: Ignoring NXM callback while online Nexus access is disabled") | ||
| SnackbarManager.show(getString(R.string.nexus_integration_temporarily_unavailable)) | ||
| return | ||
| } |
There was a problem hiding this comment.
will this show a message for everyone everytime they open the app or are offline?
There was a problem hiding this comment.
Nope, this only runs when GN receives an ACTION_VIEW intent containing an nxm:// URL, such as when someone clicks “Mod Manager Download" on the Nexus Mods website.
Description
Removes Personal API key authentication from Nexus Mods support to comply with the Nexus Mods API policy while GameNative completes registration and receives OAuth credentials.
Recording
N/A
Type of Change
Checklist
#code-changes, I have discussed this change there and it has been green-lighted. If I do not have access, I have still provided clear context in this PR. If I skip both, I accept that this change may face delays in review, may not be reviewed at all, or may be closed.CONTRIBUTING.md.Summary by CodeRabbit
New Features
Bug Fixes