add toggle to hide already-installed modpacks (#6917)

* add toggle to hide already-installed modpacks, on by default

* make false by default
This commit is contained in:
Prospector
2026-07-29 17:54:21 +00:00
committed by GitHub
parent 39745086fa
commit 9ccaab5fad
4 changed files with 98 additions and 28 deletions
@@ -260,6 +260,9 @@
"app.browse.hide-added-servers": {
"message": "Hide servers already added"
},
"app.browse.hide-installed-modpacks": {
"message": "Hide already installed"
},
"app.browse.project-type.modpacks": {
"message": "Modpacks"
},
+93 -28
View File
@@ -50,6 +50,7 @@ import { instance_listener } from '@/helpers/events.js'
import {
get as getInstance,
get_installed_project_ids as getInstalledProjectIds,
list as listInstances,
} from '@/helpers/instance'
import { get_loader_versions as getLoaderManifest } from '@/helpers/metadata'
import { get as getSettings, set as setSettings } from '@/helpers/settings.ts'
@@ -171,7 +172,7 @@ const instance: Ref<Instance | null> = ref(
queryClient.getQueryData<Instance>(['instances', 'summary', initialInstanceId]) ?? null,
)
const installedProjectIds: Ref<string[] | null> = ref(null)
const instanceHideInstalled = ref(false)
const instanceHideInstalled = ref(route.query.ai === 'true')
const newlyInstalled = ref<string[]>([])
const hiddenInstanceProjectIds = ref<Set<string>>(new Set())
const hiddenInstanceProjectIdsInitialized = ref(false)
@@ -288,7 +289,17 @@ watchServerContextChanges()
await initInstanceContext()
async function refreshInstalledProjectIds() {
if (!route.query.i) return
if (!route.query.i) {
const instances = await listInstances().catch(handleError)
if (!instances) return
const ids = instances
.map((gameInstance) => gameInstance.link?.project_id)
.filter((id): id is string => !!id)
debugLog('installedInstanceProjectIds loaded', { count: ids.length })
installedProjectIds.value = ids
return
}
if (route.query.from === 'worlds') {
const worlds = await get_instance_worlds(route.query.i as string).catch(handleError)
@@ -318,6 +329,7 @@ async function initInstanceContext() {
queryFrom: route.query.from,
})
await initServerContext()
await refreshInstalledProjectIds()
if (route.query.i) {
instance.value = (await getInstance(route.query.i as string).catch(handleError)) ?? null
@@ -327,8 +339,6 @@ async function initInstanceContext() {
gameVersion: instance.value?.game_version,
})
await refreshInstalledProjectIds()
if (instance.value?.link?.project_id) {
debugLog('checking linked project for server status', instance.value.link.project_id)
const projectV3 = await get_project_v3(
@@ -341,13 +351,23 @@ async function initInstanceContext() {
}
}
}
if (route.query.ai && !(route.params.projectType === 'modpack')) {
debugLog('setting instanceHideInstalled from query', route.query.ai)
instanceHideInstalled.value = route.query.ai === 'true'
}
}
function setBrowseHideInstalledFlag(flag: 'hide_installed_modpacks', value: boolean) {
themeStore.featureFlags[flag] = value
getSettings()
.then((settings) => {
settings.feature_flags[flag] = value
return setSettings(settings)
})
.catch(handleError)
}
const hideInstalledModpacks = computed({
get: () => themeStore.getFeatureFlag('hide_installed_modpacks'),
set: (value: boolean) => setBrowseHideInstalledFlag('hide_installed_modpacks', value),
})
const instanceFilters = computed(() => {
const filters = []
@@ -367,11 +387,15 @@ const instanceFilters = computed(() => {
if (isServerInstance.value) {
filters.push({ type: 'environment', option: 'client' })
}
}
if (instanceHideInstalled.value && hiddenInstanceProjectIds.value.size > 0) {
for (const id of hiddenInstanceProjectIds.value) {
filters.push({ type: 'project_id', option: `project_id:${id}`, negative: true })
}
if (
(instance.value || projectType.value === 'modpack') &&
(projectType.value === 'modpack' ? hideInstalledModpacks.value : instanceHideInstalled.value) &&
hiddenInstanceProjectIds.value.size > 0
) {
for (const id of hiddenInstanceProjectIds.value) {
filters.push({ type: 'project_id', option: `project_id:${id}`, negative: true })
}
}
@@ -430,6 +454,12 @@ const serverContextFilters = computed(() => {
{ type: 'environment', option: 'client' },
{ type: 'environment', option: 'server' },
)
if (hideInstalledModpacks.value && hiddenInstanceProjectIds.value.size > 0) {
for (const id of hiddenInstanceProjectIds.value) {
filters.push({ type: 'project_id', option: `project_id:${id}`, negative: true })
}
}
}
if (serverHideInstalled.value && hiddenServerContentProjectIds.value.size > 0) {
@@ -500,6 +530,10 @@ const messages = defineMessages({
id: 'app.browse.hide-added-servers',
defaultMessage: 'Hide servers already added',
},
hideInstalledModpacks: {
id: 'app.browse.hide-installed-modpacks',
defaultMessage: 'Hide already installed',
},
installingToServer: {
id: 'app.browse.server.installing',
defaultMessage: 'Installing',
@@ -553,6 +587,7 @@ function resetInstanceContext() {
hiddenInstanceProjectIdsInitialized.value = false
isServerInstance.value = false
browseBreadcrumb.reset()
void refreshInstalledProjectIds()
}
watch(
@@ -781,6 +816,7 @@ function getCardActions(
serverContentProjectIds.value.has(projectResult.project_id || '') ||
serverContextServerData.value?.upstream?.project_id === projectResult.project_id
const isInstalling = installingProjectIds.value.has(projectResult.project_id)
const showAsInstalled = isInstalled && currentProjectType !== 'modpack'
if (
isServerContext.value &&
@@ -790,7 +826,7 @@ function getCardActions(
const isInstallingSelection = isInstallingQueuedServerInstalls.value
const validatingInstall =
isInstalling && currentProjectType !== 'modpack' && !isInstallingSelection
const installLabel = isInstalled
const installLabel = showAsInstalled
? commonMessages.installedLabel
: isQueued
? isInstalling || isInstallingSelection
@@ -810,11 +846,11 @@ function getCardActions(
icon:
isInstalling || isInstallingSelection
? SpinnerIcon
: isQueued || isInstalled
: isQueued || showAsInstalled
? CheckIcon
: PlusIcon,
iconClass: isInstalling || isInstallingSelection ? 'animate-spin' : undefined,
disabled: isInstalled || isInstalling || isInstallingSelection,
disabled: showAsInstalled || isInstalling || isInstallingSelection,
color: isQueued && !isInstalling && !isInstallingSelection ? 'green' : 'brand',
type: 'outlined',
onClick: async () => {
@@ -875,15 +911,15 @@ function getCardActions(
label: formatMessage(
isInstalling
? messages.installingToServer
: isInstalled
: showAsInstalled
? commonMessages.installedLabel
: shouldUseInstallIcon
? commonMessages.installButton
: messages.addToAnInstance,
),
icon: isInstalling ? SpinnerIcon : isInstalled ? CheckIcon : PlusIcon,
icon: isInstalling ? SpinnerIcon : showAsInstalled ? CheckIcon : PlusIcon,
iconClass: isInstalling ? 'animate-spin' : undefined,
disabled: isInstalled || isInstalling,
disabled: showAsInstalled || isInstalling,
color: 'brand',
type: 'outlined',
onClick: async () => {
@@ -994,10 +1030,11 @@ async function search(requestParams: string) {
...hit,
}
if (instance.value || isServerContext.value) {
const installedIds = instance.value
? new Set([...newlyInstalled.value, ...(installedProjectIds.value ?? [])])
: serverContentProjectIds.value
if (instance.value || isServerContext.value || projectType.value === 'modpack') {
const installedIds =
isServerContext.value && projectType.value !== 'modpack'
? serverContentProjectIds.value
: new Set([...newlyInstalled.value, ...(installedProjectIds.value ?? [])])
mapped.installed = installedIds.has(hit.project_id)
}
@@ -1057,9 +1094,9 @@ watch(
() => projectType.value,
],
() => {
if (isServerContext.value) {
if (isServerContext.value && projectType.value !== 'modpack') {
syncHiddenServerContentProjectIds()
} else if (instance.value) {
} else if (instance.value || projectType.value === 'modpack') {
syncHiddenInstanceProjectIds()
}
},
@@ -1091,6 +1128,18 @@ let unlistenInstances: UnlistenFn | null = null
onMounted(() => {
instance_listener(async (event: { event: string; instance_id: string }) => {
if (event.event === 'added' || event.event === 'created' || event.event === 'removed') {
if (!route.query.i) {
await refreshInstalledProjectIds()
if (projectType.value === 'modpack') {
if (event.event === 'removed') {
syncHiddenInstanceProjectIds()
}
await searchState.refreshSearch()
}
}
}
if (instance.value && event.instance_id === instance.value.id && event.event === 'synced') {
await refreshInstalledProjectIds()
await searchState.refreshSearch()
@@ -1156,8 +1205,17 @@ provideBrowseManager({
installContext,
providedFilters: combinedProvidedFilters,
hideInstalled: computed({
get: () => (isServerContext.value ? serverHideInstalled.value : instanceHideInstalled.value),
get: () => {
if (projectType.value === 'modpack') return hideInstalledModpacks.value
if (isServerContext.value) return serverHideInstalled.value
return instanceHideInstalled.value
},
set: (val: boolean) => {
if (projectType.value === 'modpack') {
hideInstalledModpacks.value = val
if (val) syncHiddenInstanceProjectIds()
return
}
if (isServerContext.value) {
serverHideInstalled.value = val
if (val) syncHiddenServerContentProjectIds()
@@ -1168,11 +1226,18 @@ provideBrowseManager({
},
}),
showHideInstalled: computed(
() => (isServerContext.value && projectType.value !== 'modpack') || !!instance.value,
() =>
projectType.value === 'modpack' ||
(isServerContext.value && projectType.value !== 'modpack') ||
!!instance.value,
),
hideInstalledLabel: computed(() =>
formatMessage(
isFromWorlds.value ? messages.hideAddedServers : commonMessages.hideInstalledContentLabel,
isFromWorlds.value
? messages.hideAddedServers
: projectType.value === 'modpack'
? messages.hideInstalledModpacks
: commonMessages.hideInstalledContentLabel,
),
),
hideSelected: hideSelectedServerInstalls,
+1
View File
@@ -17,6 +17,7 @@ export const DEFAULT_FEATURE_FLAGS = {
show_instance_play_time: true,
advanced_filters_collapsed: true,
always_show_copy_details: false,
hide_installed_modpacks: false,
}
export const THEME_OPTIONS = ['dark', 'light', 'oled', 'retro', 'system'] as const
+1
View File
@@ -65,6 +65,7 @@ pub enum FeatureFlag {
SkipNonEssentialWarnings,
AdvancedFiltersCollapsed,
AlwaysShowCopyDetails,
HideInstalledModpacks,
}
impl Settings {