Skip to content

Commit 8f392b7

Browse files
The412Bannerclaude
andcommitted
feat: NEW chip on source cards with unseen releases
Adds a per-source new-item badge to the source list in the Download Components tab. Each source card with at least one new fingerprint (latest item changed since last visit) shows a small "NEW" chip in primaryContainer colour to the right of the source name. RemoteSourceRepository.getNewSourceNames() derives the set of source names from the fingerprint diff; DownloadScreen captures it once with remember{} when the tab is opened — so the chips are visible while the user browses and gone on the next visit (markAllAsSeen already ran on tab entry via LaunchedEffect in MainActivity). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent e9a0881 commit 8f392b7

2 files changed

Lines changed: 30 additions & 0 deletions

File tree

app/src/main/java/com/banner/inject/data/RemoteSourceRepository.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,6 +1164,19 @@ class RemoteSourceRepository(private val context: Context) {
11641164
return result
11651165
}
11661166

1167+
/**
1168+
* Returns the set of source names that have at least one new fingerprint
1169+
* compared to the saved snapshot. Empty set if no snapshot exists yet.
1170+
*/
1171+
fun getNewSourceNames(): Set<String> {
1172+
val seen = loadSeenFingerprints()
1173+
if (seen.isEmpty()) return emptySet()
1174+
return currentFingerprints()
1175+
.filter { it !in seen }
1176+
.mapNotNull { fp -> fp.split("::").firstOrNull() }
1177+
.toSet()
1178+
}
1179+
11671180
/**
11681181
* Returns true when the current cache contains items not present in the last
11691182
* saved snapshot. On first run (no saved snapshot) establishes a baseline

app/src/main/java/com/banner/inject/ui/screens/DownloadScreen.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ fun DownloadScreen(
8989
var detailTarget by remember { mutableStateOf<Pair<RemoteSourceRepository.RemoteItem, String>?>(null) }
9090
var searchQuery by remember { mutableStateOf("") }
9191
var isSearchCaching by remember { mutableStateOf(false) }
92+
// Captured once on composition — which source names had new items when the tab was opened
93+
val newSourceNames = remember { repo.getNewSourceNames() }
9294

9395
val componentTypes = listOf("dxvk", "vkd3d", "box64", "fexcore", "wined3d", RemoteSourceRepository.GPU_DRIVER_TYPE, "drivers", "wine", "proton")
9496

@@ -461,6 +463,21 @@ fun DownloadScreen(
461463
color = MaterialTheme.colorScheme.onSurface,
462464
modifier = Modifier.weight(1f)
463465
)
466+
if (source.name in newSourceNames) {
467+
Spacer(Modifier.width(6.dp))
468+
Surface(
469+
color = MaterialTheme.colorScheme.primaryContainer,
470+
shape = MaterialTheme.shapes.extraSmall
471+
) {
472+
Text(
473+
"NEW",
474+
fontSize = 9.sp,
475+
fontWeight = FontWeight.Bold,
476+
color = MaterialTheme.colorScheme.onPrimaryContainer,
477+
modifier = Modifier.padding(horizontal = 4.dp, vertical = 2.dp)
478+
)
479+
}
480+
}
464481
Box {
465482
IconButton(
466483
onClick = { sourceMenuExpanded = source },

0 commit comments

Comments
 (0)