diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7ffcfba82..f91be2ec1 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -48,7 +48,7 @@ fun getAppVersion(): String { android { namespace = "com.github.damontecres.stashapp" - compileSdk = 35 + compileSdk = 36 sourceSets { getByName("main") { @@ -66,7 +66,7 @@ android { defaultConfig { applicationId = "com.github.damontecres.stashapp" minSdk = 23 - targetSdk = 35 + targetSdk = 36 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" versionCode = getVersionCode() versionName = getAppVersion() diff --git a/app/src/main/graphql/FindGroups.graphql b/app/src/main/graphql/FindGroups.graphql index 1c9739268..112e988d0 100644 --- a/app/src/main/graphql/FindGroups.graphql +++ b/app/src/main/graphql/FindGroups.graphql @@ -84,6 +84,7 @@ fragment GroupData on Group { description } scene_count + performer_count front_image_path back_image_path created_at @catch(to: NULL) diff --git a/app/src/main/java/com/github/damontecres/stashapp/data/SortOption.kt b/app/src/main/java/com/github/damontecres/stashapp/data/SortOption.kt index 8f139357e..67109e1e0 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/data/SortOption.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/data/SortOption.kt @@ -342,6 +342,7 @@ sealed interface SortOption { Name, Rating, ScenesCount, + TagCount, ) val TAG_SORT_OPTIONS = diff --git a/app/src/main/java/com/github/damontecres/stashapp/filter/CreateFilterStep.kt b/app/src/main/java/com/github/damontecres/stashapp/filter/CreateFilterStep.kt index 87711d556..a0604d2a4 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/filter/CreateFilterStep.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/filter/CreateFilterStep.kt @@ -163,7 +163,7 @@ class CreateFilterStep : CreateFilterGuidedStepFragment() { ConfirmationDialogFragment.show( childFragmentManager, getString( - R.string.stashapp_dialogs_overwrite_filter_confirm, + R.string.stashapp_dialogs_overwrite_filter_warning, filterArgs.name, ), ) { diff --git a/app/src/main/java/com/github/damontecres/stashapp/image/ImageViewFragment.kt b/app/src/main/java/com/github/damontecres/stashapp/image/ImageViewFragment.kt index 07b7a76c6..334147d31 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/image/ImageViewFragment.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/image/ImageViewFragment.kt @@ -136,10 +136,10 @@ class ImageViewFragment : } override fun onResourceReady( - resource: Drawable, - model: Any, + resource: Drawable?, + model: Any?, target: Target?, - dataSource: DataSource, + dataSource: DataSource?, isFirstResource: Boolean, ): Boolean { viewModel.pulseSlideshow() diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/cards/GroupCard.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/cards/GroupCard.kt index ba9535e28..48fcf86dc 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/cards/GroupCard.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/cards/GroupCard.kt @@ -38,6 +38,7 @@ fun GroupCard( val dataTypeMap = EnumMap(DataType::class.java) item?.let { dataTypeMap[DataType.SCENE] = item.scene_count + dataTypeMap[DataType.PERFORMER] = item.performer_count dataTypeMap[DataType.TAG] = item.tags.size } diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/components/prefs/StashPreference.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/components/prefs/StashPreference.kt index d7632753d..9e7904591 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/components/prefs/StashPreference.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/components/prefs/StashPreference.kt @@ -543,6 +543,7 @@ sealed interface StashPreference { listOf( TabType.DETAILS, TabType.SCENES, + TabType.PERFORMERS, TabType.MARKERS, TabType.TAGS, TabType.CONTAINING_GROUPS, diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/pages/GroupPage.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/pages/GroupPage.kt index d5ba3f1ba..0b3bf065b 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/pages/GroupPage.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/pages/GroupPage.kt @@ -37,6 +37,7 @@ import com.github.damontecres.stashapp.api.fragment.TagData import com.github.damontecres.stashapp.api.type.CriterionModifier import com.github.damontecres.stashapp.api.type.GroupFilterType import com.github.damontecres.stashapp.api.type.HierarchicalMultiCriterionInput +import com.github.damontecres.stashapp.api.type.PerformerFilterType import com.github.damontecres.stashapp.api.type.SceneFilterType import com.github.damontecres.stashapp.api.type.SceneMarkerFilterType import com.github.damontecres.stashapp.api.type.SortDirectionEnum @@ -116,7 +117,7 @@ fun GroupPage( Optional.present( HierarchicalMultiCriterionInput( value = Optional.present(listOf(group.id)), - modifier = CriterionModifier.INCLUDES_ALL, + modifier = CriterionModifier.INCLUDES, depth = Optional.present(if (includeSubGroups) -1 else 0), ), ) @@ -196,6 +197,41 @@ fun GroupPage( ) } } + + // Performers + var performersSubTags by rememberSaveable { mutableStateOf(false) } + var performerFilter by rememberSaveable(performersSubTags, saver = filterArgsSaver) { + mutableStateOf( + FilterArgs( + DataType.PERFORMER, + findFilter = tabFindFilter(server, PageFilterKey.GROUP_PERFORMERS), + objectFilter = PerformerFilterType(groups = groupsFunc(performersSubTags)), + ), + ) + } + val performerTab = + remember(performerFilter, performersSubTags) { + TabProvider( + context.getString(R.string.stashapp_performers), + TabType.PERFORMERS, + ) { positionCallback -> + StashGridTab( + name = context.getString(R.string.stashapp_performers), + server = server, + initialFilter = performerFilter, + itemOnClick = itemOnClick, + longClicker = longClicker, + modifier = Modifier, + positionCallback = positionCallback, + subToggleLabel = subToggleLabel, + onSubToggleCheck = { performersSubTags = it }, + subToggleChecked = performersSubTags, + composeUiConfig = uiConfig, + onFilterChange = { performerFilter = it }, + ) + } + } + // markers var markersSubTags by rememberSaveable { mutableStateOf(false) } var markersFilter by rememberSaveable(markersSubTags, saver = filterArgsSaver) { @@ -309,8 +345,14 @@ fun GroupPage( val uiTabs = getUiTabs(uiConfig.preferences.interfacePreferences.tabPreferences, DataType.GROUP) val tabs = - listOf(detailsTab, scenesTab, markersTab, containingGroupsTab, subGroupsTab) - .filter { it.type in uiTabs } + listOf( + detailsTab, + scenesTab, + performerTab, + markersTab, + containingGroupsTab, + subGroupsTab, + ).filter { it.type in uiTabs } val title = AnnotatedString(group.name) LaunchedEffect(title) { onUpdateTitle?.invoke(title) } TabPage( diff --git a/app/src/main/java/com/github/damontecres/stashapp/util/AppUpgradeHandler.kt b/app/src/main/java/com/github/damontecres/stashapp/util/AppUpgradeHandler.kt index c55e75d49..71ff3ad7d 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/util/AppUpgradeHandler.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/util/AppUpgradeHandler.kt @@ -10,6 +10,7 @@ import androidx.core.content.edit import androidx.preference.PreferenceManager import com.github.damontecres.stashapp.R import com.github.damontecres.stashapp.proto.StashPreferences +import com.github.damontecres.stashapp.proto.TabType import com.github.damontecres.stashapp.ui.components.prefs.StashPreference import com.github.damontecres.stashapp.ui.components.prefs.advancedPreferences import com.github.damontecres.stashapp.ui.components.prefs.basicPreferences @@ -126,6 +127,7 @@ class AppUpgradeHandler( putBoolean(key, true) } } + if (previousVersion.isLessThan(Version.fromString("0.6.10"))) { try { preferences.getString(context.getString(R.string.pref_key_card_size), "5") @@ -182,6 +184,24 @@ class AppUpgradeHandler( } } } + + if (previousVersion.isLessThan(Version.fromString("v0.7.0"))) { + preferences.ensureSetHas( + context, + R.string.pref_key_ui_group_tabs, + R.array.group_tabs, + listOf(context.getString(R.string.stashapp_performers)), + ) + } + CoroutineScope(Dispatchers.IO + StashCoroutineExceptionHandler()).launch { + context.preferences.updateData { + it.updateTabPreferences { + if (TabType.PERFORMERS !in groupList) { + addGroup(TabType.PERFORMERS) + } + } + } + } } private fun SharedPreferences.ensureSetHas( diff --git a/app/src/main/java/com/github/damontecres/stashapp/util/PageFilterKey.kt b/app/src/main/java/com/github/damontecres/stashapp/util/PageFilterKey.kt index dee226ec7..e299d791c 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/util/PageFilterKey.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/util/PageFilterKey.kt @@ -4,6 +4,8 @@ import com.github.damontecres.stashapp.data.DataType /** * List of keys to look up how to sort items in a tab on a page + * + * https://github.com/stashapp/stash/blob/develop/ui/v2.5/src/components/List/views.ts */ enum class PageFilterKey( val dataType: DataType, @@ -33,4 +35,5 @@ enum class PageFilterKey( GROUP_SCENES(DataType.SCENE, "group_scenes"), GROUP_SUB_GROUPS(DataType.GROUP, "group_sub_groups"), + GROUP_PERFORMERS(DataType.PERFORMER, "group_performers"), } diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 93506f866..fab26cc7d 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -158,6 +158,7 @@ @string/stashapp_details @string/stashapp_scenes + @string/stashapp_performers @string/stashapp_markers @string/stashapp_tags @string/stashapp_containing_groups diff --git a/buildSrc/src/main/java/com/github/damontecres/buildsrc/ParseStashStrings.kt b/buildSrc/src/main/java/com/github/damontecres/buildsrc/ParseStashStrings.kt index ef9b4c950..b8b6e6b00 100644 --- a/buildSrc/src/main/java/com/github/damontecres/buildsrc/ParseStashStrings.kt +++ b/buildSrc/src/main/java/com/github/damontecres/buildsrc/ParseStashStrings.kt @@ -109,6 +109,7 @@ abstract class ParseStashStrings : DefaultTask() { escaped = escaped.replace(">", ">") escaped = escaped.replace("<", "<") escaped = escaped.replace("'", "\\'") + escaped = escaped.replace("\uFEFF", "") if (!escaped.contains("{count, plural,")) { // If the message is not a ICU plural, then it may have parameters to replace // Now replace the parameters in the message values with the equivalent Android format diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 76c411caa..46c3380db 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,9 +1,9 @@ [versions] -acra = "5.12.0" -activity-compose = "1.10.1" +acra = "5.13.1" +activity-compose = "1.11.0" androidsvg-aar = "1.4" -android-application = "8.12.0" -apollo = "4.3.2" +android-application = "8.13.0" +apollo = "4.3.3" androidx-media3 = "1.8.0" androidx-test = "1.7.0" androidx-test-runner = "1.7.0" @@ -11,41 +11,41 @@ androidx-test-ext-junit = "1.3.0" androidx-test-ext-truth = "1.7.0" auto-service = "1.1.1" coil-compose = "3.3.0" -compose-bom = "2025.08.00" -compose-runtime = "1.9.0" +compose-bom = "2025.11.00" +compose-runtime = "1.9.4" compose-wheel-picker = "1.0.0-rc02" constraintlayout = "2.2.1" datastore = "1.1.7" desugar_jdk_libs = "2.1.5" markwon-core = "4.6.2" -core-ktx = "1.16.0" -glide = "4.16.0" +core-ktx = "1.17.0" +glide = "5.0.5" junit = "4.13.2" -kotlin = "2.2.10" +kotlin = "2.2.21" kotlinx-coroutines-android = "1.10.2" kotlinx-serialization = "1.9.0" -ksp = "2.2.10-2.0.2" +ksp = "2.3.0" leanback = "1.2.0" leanback-preference = "1.2.0" leanback-tab = "1.1.0" leanback-grid = "1.0.0" -lifecycle-process = "2.9.2" -lifecycle-viewmodel-compose = "2.9.2" -material = "1.12.0" +lifecycle-process = "2.9.4" +lifecycle-viewmodel-compose = "2.9.4" +material = "1.13.0" material3-adaptive = "1.0.0-alpha06" -material3-android = "1.3.2" -mockito-core = "5.18.0" -mockito-kotlin = "6.0.0" +material3-android = "1.4.0" +mockito-core = "5.20.0" +mockito-kotlin = "6.1.0" navigation-reimagined = "1.5.0" parcelable-core = "0.9.0" preference-ktx-version = "1.2.1" previewseekbar = "3.1.1" previewseekbar-media3 = "1.1.1.0" protobuf = "0.9.5" -protobuf-javalite = "4.31.1" +protobuf-javalite = "4.33.0" restring = "6.0.0" reword = "4.0.4" -room = "2.7.2" +room = "2.8.3" swiperefreshlayout = "1.1.0" tv-foundation = "1.0.0-alpha12" tv-material = "1.0.1" diff --git a/stash-server b/stash-server index cc6917f29..7716c4dd8 160000 --- a/stash-server +++ b/stash-server @@ -1 +1 @@ -Subproject commit cc6917f29d8a51390d8d86ea1d576075f4a3484f +Subproject commit 7716c4dd874512e7937c59ee52d034e452ba2026