Moderation Changes Phase 2.-1 (#6833)

* better default state handling

* make next stage button work for going to next project

* fix skill issue

* actually fix initial values this time,
make single line inputs with varying lengths (text input and dropdown) use their widest possible length instead of whatever the hell they used to be

* loaders in progress

* loaders and versions test (probably dont use them as the messages suck and idrk what they should do quick fix/message wise)

* WIP the wip

* good enough for now slug taken improvements

* trim all those extra newlines

* viewedness

* batch prefetch queue + technically slightly faster checklist init

* show all valid alternative project types regardless of current project type

* Hide moderation permissions stage when it should be, and I'm sure this won't cause any issues

* Hide moderation permissions stage when it should be, and I'm sure this won't cause any issues

* gimme them versions

* better moderation permission stage shown check?

* betterer moderation permission stage shown check?

* bettererer moderation permission stage shown check? + also began deleting legacy checklist types

* fix certain stages being able to be considered viewed before being viewed

* more removals + i forgot to remove an import last commit

* changed nothing™️

* remove severities + bandaid post-approval message priorities + dependencies

* fix message preview tooltips

* next stage = next project when done

* insufficient description custom fix

* fix that thing coolbot told me was broken I forgot what it was oops

* oh there was a second bug there

* make alternate versions and incorrect project type required cuz they are

* re-navigate button also damn that's a lot of other stuff that pnpm fix... fixed?

* you cant just quick fix a slug into a taken one

* maybe lets not just try and set a slug to something entirely invalid?

* what if we just didn't drop ur queue :smart:

* more queue upgrades

* tweaks

* I love unbreaking things

* un-nuked renavigate button

* prepr

* prepr worked this time i think

* undo more mistakes

* i love intellij refactoring

* ok i think we're good
This commit is contained in:
chyz
2026-08-07 23:40:11 +00:00
committed by GitHub
parent e4cbec6b2c
commit 5072c1d298
77 changed files with 3596 additions and 3502 deletions
-205
View File
@@ -1,181 +1,5 @@
import type { Labrinth } from '@modrinth/api-client'
import type {
Action,
AdditionalTextInput,
ButtonAction,
ConditionalMessage,
ToggleAction,
} from './types/actions'
export interface ActionState {
selected: boolean
value?: Set<string | number> | number | string | unknown
}
export interface MessagePart {
weight: number
content: string
actionId: string
stageIndex: number
}
export function getActionIdForStage(
action: Action,
stageIndex: number,
actionIndex?: number,
enabledIndex?: number,
): string {
if (action.id) {
return `stage-${stageIndex}-${action.id}`
}
const suffix = enabledIndex !== undefined ? `-enabled-${enabledIndex}` : ''
return `stage-${stageIndex}-action-${actionIndex}${suffix}`
}
export function getActionId(action: Action, currentStage: number, index?: number): string {
return getActionIdForStage(action, currentStage, index)
}
export function getActionKey(
action: Action,
currentStage: number,
visibleActions: Action[],
): string {
const index = visibleActions.indexOf(action)
return `${currentStage}-${index}-${getActionId(action, currentStage)}`
}
export function initializeActionState(action: Action): ActionState {
if (action.type === 'toggle') {
return {
selected: action.defaultChecked || false,
}
} else if (action.type === 'dropdown') {
return {
selected: true,
value: action.defaultOption || 0,
}
} else if (action.type === 'multi-select-chips') {
return {
selected: false,
value: new Set<string | number>(),
}
} else {
return {
selected: false,
}
}
}
export function processMessage(
message: string,
action: Action,
stageIndex: number,
textInputValues: Record<string, string>,
): string {
let processedMessage = message
if (action.relevantExtraInput) {
action.relevantExtraInput.forEach((input, index) => {
if (input.variable) {
const inputKey = `stage-${stageIndex}-${action.id || `action-${index}`}-${index}`
const value = textInputValues[inputKey] || ''
const regex = new RegExp(`%${input.variable}%`, 'g')
processedMessage = processedMessage.replace(regex, value)
}
})
}
return processedMessage
}
export function findMatchingVariant(
variants: ConditionalMessage[],
selectedActionIds: string[],
allValidActionIds?: string[],
currentStageIndex?: number,
): ConditionalMessage | null {
for (const variant of variants) {
const conditions = variant.conditions
const meetsRequired =
!conditions.requiredActions ||
conditions.requiredActions.every((id) => {
let fullId = id
if (currentStageIndex !== undefined && !id.startsWith('stage-')) {
fullId = `stage-${currentStageIndex}-${id}`
}
if (allValidActionIds && !allValidActionIds.includes(fullId)) {
return false
}
return selectedActionIds.includes(fullId)
})
const meetsExcluded =
!conditions.excludedActions ||
!conditions.excludedActions.some((id) => {
let fullId = id
if (currentStageIndex !== undefined && !id.startsWith('stage-')) {
fullId = `stage-${currentStageIndex}-${id}`
}
return selectedActionIds.includes(fullId)
})
if (meetsRequired && meetsExcluded) {
return variant
}
}
return null
}
export async function getActionMessage(
action: ButtonAction | ToggleAction,
selectedActionIds: string[],
allValidActionIds?: string[],
): Promise<string> {
if (action.conditionalMessages && action.conditionalMessages.length > 0) {
const matchingConditional = findMatchingVariant(
action.conditionalMessages,
selectedActionIds,
allValidActionIds,
)
if (matchingConditional) {
return (await matchingConditional.message()) as string
}
}
return (await action.message()) as string
}
export function getVisibleInputs(
action: Action,
actionStates: Record<string, ActionState>,
): AdditionalTextInput[] {
if (!action.relevantExtraInput) return []
const selectedActionIds = Object.entries(actionStates)
.filter(([, state]) => state.selected)
.map(([id]) => id)
return action.relevantExtraInput.filter((input) => {
if (!input.showWhen) return true
const meetsRequired =
!input.showWhen.requiredActions ||
input.showWhen.requiredActions.every((id) => selectedActionIds.includes(id))
const meetsExcluded =
!input.showWhen.excludedActions ||
!input.showWhen.excludedActions.some((id) => selectedActionIds.includes(id))
return meetsRequired && meetsExcluded
})
}
export function expandVariables(
template: string,
project: Labrinth.Projects.v2.Project,
@@ -283,35 +107,6 @@ export function formatProjectTypes(type: string, lower: boolean = false) {
return value
}
export function formatEnvironments(environment: string, lower: boolean = false) {
let value = environment
try {
value = value
.replaceAll('client_only_server_optional', 'Client and server: Optional on server')
.replaceAll('client_and_server', 'Required on both')
.replaceAll('client_only', 'Client-side only')
.replaceAll('server_only_client_optional', 'Client optional')
.replaceAll('dedicated_server_only', 'Dedicated server only')
.replaceAll('server_only', 'Servers and Singleplayer')
.replaceAll('singleplayer_only', 'Singleplayer only')
.replaceAll(
'client_or_server_prefers_both',
'Optional on both, works best when installed on both sides',
)
.replaceAll(
'client_or_server',
'Optional on both, works the same if installed on either side',
)
// This shouldn't come up for this use but yk
.replaceAll('unknown', 'Unknown')
} catch {
return 'No project environment'
}
if (lower === true) value = value.toLowerCase()
return value
}
export function requiresEnvironmentInfo(projectTypes): boolean {
return projectTypes.includes('mod') || projectTypes.includes('modpack')
}