* feat(#433): achievements 1:1 parity with dashboard - Expanded model: AchievementsResponse now carries header stats (unlockedCount, discoveredCount, secretCount, totalCount, isStale, scanMeta/ScanStatus) and per-achievement fields (unlockedAt, secret, criteria) - Added API endpoints: scan-status, rescan, reset-state, recent-unlocks - Created AchievementIcon emoji mapper for all Lucide icon names - Rewrote ViewModel with full state: scan tracking, recent unlocks, category/state filtering, rescan/reset actions with loading states - Rewrote Screen: header stats row, scan status chip, stale indicator, category + state filter chips, recent unlocks section, expandable criteria, icon emoji, unlocked glow border, overflow menu with rescan/reset, reset confirmation dialog - Added 30 string resources for all new UI elements * fix: resolve CI compilation errors in achievements module - Replace progress_pct references with correct Kotlin field progressPct (Achievement model uses @SerializedName for snake_case, Kotlin field is camelCase) - Add missing onStart/onError parameters to safeLaunchLoad calls in loadScanStatus() and loadRecentUnlocks() * fix: remove top padding on achievements tab content * fix: remove redundant paddingValues on achievement LazyColumn --------- Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
1 parent
2bceac7ab3
commit
f887146056
6 files changed
+941
-108
No files matched your search
@@ -1,7 +1,50 @@
|
||||
package com.m57.hermescontrol.data.model
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class AchievementsResponse(
|
||||
val achievements: List<Achievement>,
|
||||
val achievements: List<Achievement>? = emptyList(),
|
||||
@SerializedName("unlocked_count") val unlockedCount: Int = 0,
|
||||
@SerializedName("discovered_count") val discoveredCount: Int = 0,
|
||||
@SerializedName("secret_count") val secretCount: Int = 0,
|
||||
@SerializedName("total_count") val totalCount: Int = 0,
|
||||
@SerializedName("is_stale") val isStale: Boolean = false,
|
||||
@SerializedName("generated_at") val generatedAt: Long? = null,
|
||||
@SerializedName("scan_meta") val scanMeta: ScanMeta? = null,
|
||||
val error: String? = null,
|
||||
)
|
||||
|
||||
data class ScanMeta(
|
||||
val status: ScanStatus? = null,
|
||||
)
|
||||
|
||||
data class ScanStatus(
|
||||
val state: String = "idle",
|
||||
@SerializedName("started_at") val startedAt: Long? = null,
|
||||
@SerializedName("finished_at") val finishedAt: Long? = null,
|
||||
@SerializedName("last_error") val lastError: String? = null,
|
||||
@SerializedName("last_duration_ms") val lastDurationMs: Long? = null,
|
||||
@SerializedName("run_count") val runCount: Int = 0,
|
||||
)
|
||||
|
||||
data class RecentUnlock(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val description: String?,
|
||||
val category: String?,
|
||||
val kind: String?,
|
||||
val icon: String?,
|
||||
val unlocked: Boolean,
|
||||
val discovered: Boolean,
|
||||
val state: String?,
|
||||
val tier: String?,
|
||||
val progress: Double?,
|
||||
@SerializedName("next_tier") val nextTier: String?,
|
||||
@SerializedName("next_threshold") val nextThreshold: Double?,
|
||||
@SerializedName("progress_pct") val progressPct: Double?,
|
||||
@SerializedName("unlocked_at") val unlockedAt: Long?,
|
||||
val secret: Boolean?,
|
||||
val criteria: String?,
|
||||
)
|
||||
|
||||
data class Achievement(
|
||||
@@ -16,7 +59,10 @@ data class Achievement(
|
||||
val state: String?,
|
||||
val tier: String?,
|
||||
val progress: Double?,
|
||||
val next_tier: String?,
|
||||
val next_threshold: Double?,
|
||||
val progress_pct: Double?,
|
||||
@SerializedName("next_tier") val nextTier: String?,
|
||||
@SerializedName("next_threshold") val nextThreshold: Double?,
|
||||
@SerializedName("progress_pct") val progressPct: Double?,
|
||||
@SerializedName("unlocked_at") val unlockedAt: Long?,
|
||||
val secret: Boolean?,
|
||||
val criteria: String?,
|
||||
)
|
||||
@@ -28,7 +28,9 @@ import com.m57.hermescontrol.data.model.PluginsHubResponse
|
||||
import com.m57.hermescontrol.data.model.ProfileSoulResponse
|
||||
import com.m57.hermescontrol.data.model.ProfilesResponse
|
||||
import com.m57.hermescontrol.data.model.RawConfigResponse
|
||||
import com.m57.hermescontrol.data.model.RecentUnlock
|
||||
import com.m57.hermescontrol.data.model.SaveSkillContentRequest
|
||||
import com.m57.hermescontrol.data.model.ScanStatus
|
||||
import com.m57.hermescontrol.data.model.SessionListResponse
|
||||
import com.m57.hermescontrol.data.model.SessionMessagesResponse
|
||||
import com.m57.hermescontrol.data.model.SetActiveProfileRequest
|
||||
@@ -180,6 +182,18 @@ interface HermesApiService {
|
||||
@GET("api/plugins/hermes-achievements/achievements")
|
||||
suspend fun getAchievements(): Response<AchievementsResponse>
|
||||
|
||||
@GET("api/plugins/hermes-achievements/scan-status")
|
||||
suspend fun getAchievementScanStatus(): Response<ScanStatus>
|
||||
|
||||
@POST("api/plugins/hermes-achievements/rescan")
|
||||
suspend fun rescanAchievements(): Response<AchievementsResponse>
|
||||
|
||||
@POST("api/plugins/hermes-achievements/reset-state")
|
||||
suspend fun resetAchievementState(): Response<Unit>
|
||||
|
||||
@GET("api/plugins/hermes-achievements/recent-unlocks")
|
||||
suspend fun getRecentUnlocks(): Response<List<RecentUnlock>>
|
||||
|
||||
@GET("api/pairing")
|
||||
suspend fun getPairing(): Response<PairingResponse>
|
||||
|
||||
|
||||
Reference in new issue
Block a user