@@ -21,7 +21,8 @@ import io.github.composegears.valkyrie.ui.foundation.picker.PickerEvent
2121import io.github.composegears.valkyrie.ui.screen.mode.iconpack.conversion.ConversionEvent.OpenPreview
2222import io.github.composegears.valkyrie.ui.screen.mode.iconpack.conversion.IconPackConversionState.BatchProcessing
2323import io.github.composegears.valkyrie.ui.screen.mode.iconpack.conversion.IconPackConversionState.IconsPickering
24- import io.github.composegears.valkyrie.ui.screen.mode.iconpack.conversion.ui.util.checkImportIssues
24+ import io.github.composegears.valkyrie.ui.screen.mode.iconpack.conversion.ui.util.BatchIconsIssuesResolver.resolve
25+ import io.github.composegears.valkyrie.ui.screen.mode.iconpack.conversion.ui.util.BatchIconsValidator.validate
2526import java.nio.file.Path
2627import kotlin.io.path.isDirectory
2728import kotlin.io.path.isRegularFile
@@ -66,7 +67,7 @@ class IconPackConversionViewModel(
6667 _state .updateState {
6768 BatchProcessing .IconPackCreationState (
6869 icons = restoredState,
69- importIssues = restoredState.checkImportIssues( useFlatPackage = isFlatPackage),
70+ importIssues = validate(batchIcons = restoredState, useFlatPackage = isFlatPackage),
7071 )
7172 }
7273 }
@@ -93,7 +94,7 @@ class IconPackConversionViewModel(
9394 _state .updateState {
9495 when (this ) {
9596 is BatchProcessing .IconPackCreationState -> {
96- copy(importIssues = icons.checkImportIssues( useFlatPackage = settings.flatPackage))
97+ copy(importIssues = validate(batchIcons = icons, useFlatPackage = settings.flatPackage))
9798 }
9899 else -> this
99100 }
@@ -121,7 +122,7 @@ class IconPackConversionViewModel(
121122 } else {
122123 copy(
123124 icons = iconsToProcess,
124- importIssues = iconsToProcess.checkImportIssues( useFlatPackage = isFlatPackage),
125+ importIssues = validate(batchIcons = iconsToProcess, useFlatPackage = isFlatPackage),
125126 )
126127 }
127128 }
@@ -148,7 +149,7 @@ class IconPackConversionViewModel(
148149 }
149150 copy(
150151 icons = updatedIcons,
151- importIssues = updatedIcons.checkImportIssues( useFlatPackage = isFlatPackage),
152+ importIssues = validate(batchIcons = updatedIcons, useFlatPackage = isFlatPackage),
152153 )
153154 }
154155 else -> this
@@ -244,7 +245,7 @@ class IconPackConversionViewModel(
244245 }
245246 copy(
246247 icons = icons,
247- importIssues = icons.checkImportIssues( useFlatPackage = isFlatPackage),
248+ importIssues = validate(icons, useFlatPackage = isFlatPackage),
248249 )
249250 }
250251 else -> this
@@ -259,99 +260,7 @@ class IconPackConversionViewModel(
259260 fun resolveImportIssues () = viewModelScope.launch {
260261 val creationState = _state .value.safeAs<BatchProcessing .IconPackCreationState >() ? : return @launch
261262
262- val processedIcons = creationState.icons
263- .filterIsInstance<BatchIcon .Valid >()
264- .map { icon ->
265- val name = icon.iconName.name
266-
267- when {
268- name.isEmpty() -> icon.copy(iconName = IconName (" IconName" ))
269- name.contains(" " ) -> icon.copy(iconName = IconName (name.replace(" " , " " )))
270- else -> icon
271- }
272- }
273-
274- // Group icons by their output location (considering useFlatPackage)
275- val iconsByLocation = processedIcons.groupBy { icon ->
276- when (val pack = icon.iconPack) {
277- is IconPack .Single -> pack.iconPackName
278- is IconPack .Nested -> when {
279- isFlatPackage -> pack.iconPackName // All in same location when flat
280- else -> " ${pack.iconPackName} .${pack.currentNestedPack} " // Separate locations
281- }
282- }
283- }
284-
285- // Process duplicates within each location group
286- val resolvedIcons = iconsByLocation.flatMap { (_, iconsInLocation) ->
287- // Track all committed names to ensure uniqueness across both passes
288- val committedNames = mutableSetOf<String >()
289-
290- // First, resolve exact duplicates
291- val nameGroups = iconsInLocation.groupBy { it.iconName.name }
292- val nameCounters = mutableMapOf<String , Int >()
293-
294- val iconsWithResolvedExactDuplicates = iconsInLocation.map { icon ->
295- val originalName = icon.iconName.name
296- val group = nameGroups[originalName]
297-
298- if (group != null && group.size > 1 ) {
299- val counter = nameCounters.getOrDefault(originalName, 0 ) + 1
300- nameCounters[originalName] = counter
301-
302- if (counter > 1 ) {
303- // Generate unique name by incrementing suffix until not in committedNames
304- var suffix = counter - 1
305- var candidateName = " $originalName$suffix "
306- while (committedNames.any { it.equals(candidateName, ignoreCase = true ) }) {
307- suffix++
308- candidateName = " $originalName$suffix "
309- }
310- committedNames.add(candidateName)
311- icon.copy(iconName = IconName (candidateName))
312- } else {
313- committedNames.add(originalName)
314- icon
315- }
316- } else {
317- committedNames.add(originalName)
318- icon
319- }
320- }
321-
322- // Then, resolve case-insensitive duplicates
323- val lowercaseGroups = iconsWithResolvedExactDuplicates.groupBy { it.iconName.name.lowercase() }
324- val lowercaseCounters = mutableMapOf<String , Int >()
325-
326- iconsWithResolvedExactDuplicates.map { icon ->
327- val currentName = icon.iconName.name
328- val lowercaseKey = currentName.lowercase()
329- val group = lowercaseGroups[lowercaseKey]
330-
331- // Only process if there are multiple icons with same lowercase name but different actual names
332- if (group != null && group.size > 1 && group.map { it.iconName.name }.distinct().size > 1 ) {
333- val counter = lowercaseCounters.getOrDefault(lowercaseKey, 0 ) + 1
334- lowercaseCounters[lowercaseKey] = counter
335-
336- if (counter > 1 ) {
337- // Generate unique name by incrementing suffix until not in committedNames
338- var suffix = counter - 1
339- var candidateName = " $currentName$suffix "
340- while (committedNames.any { it.equals(candidateName, ignoreCase = true ) }) {
341- suffix++
342- candidateName = " $currentName$suffix "
343- }
344- committedNames.add(candidateName)
345- icon.copy(iconName = IconName (candidateName))
346- } else {
347- // First in group - name is already in committedNames from exact duplicate pass
348- icon
349- }
350- } else {
351- icon
352- }
353- }
354- }
263+ val resolvedIcons = resolve(batchIcons = creationState.icons, useFlatPackage = isFlatPackage)
355264
356265 if (resolvedIcons.isEmpty()) {
357266 _events .send(ConversionEvent .NothingToImport )
@@ -360,7 +269,7 @@ class IconPackConversionViewModel(
360269 _state .updateState {
361270 creationState.copy(
362271 icons = resolvedIcons,
363- importIssues = resolvedIcons.checkImportIssues( useFlatPackage = isFlatPackage),
272+ importIssues = validate(batchIcons = resolvedIcons, useFlatPackage = isFlatPackage),
364273 )
365274 }
366275 }
@@ -388,7 +297,7 @@ class IconPackConversionViewModel(
388297
389298 BatchProcessing .IconPackCreationState (
390299 icons = icons,
391- importIssues = icons.checkImportIssues( useFlatPackage = isFlatPackage),
300+ importIssues = validate(batchIcons = icons, useFlatPackage = isFlatPackage),
392301 )
393302 }
394303 }
@@ -427,7 +336,7 @@ class IconPackConversionViewModel(
427336
428337 BatchProcessing .IconPackCreationState (
429338 icons = icons,
430- importIssues = icons.checkImportIssues( useFlatPackage = isFlatPackage),
339+ importIssues = validate(batchIcons = icons, useFlatPackage = isFlatPackage),
431340 )
432341 }
433342 }
0 commit comments