mirror of
https://github.com/modrinth/code.git
synced 2026-08-25 09:04:55 +00:00
feat: Allow for adjustable alignment of main content on certain pages for content moderators (#7034)
* Add type safety and validation for moderation settings * Add ability for moderators to adjust page layout for discover, project, moderation pages Primarily for changes to have more tooling present without overlapping important info
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import type { EnumSettingDefinition, ToggleSettingDefinition } from '../types/settings.ts'
|
||||
import { setting } from '../types/settings.ts'
|
||||
|
||||
const settings = {
|
||||
General: {
|
||||
ChecklistPosition: {
|
||||
ChecklistPosition: setting.asEnum({
|
||||
type: 'enum',
|
||||
id: 'checklist-position',
|
||||
title: 'Checklist Position',
|
||||
@@ -12,21 +12,34 @@ const settings = {
|
||||
{ value: 'right', label: 'Right' },
|
||||
],
|
||||
default: 'right',
|
||||
} as EnumSettingDefinition,
|
||||
ProjectKeybinds: {
|
||||
}),
|
||||
ProjectKeybinds: setting.asToggle({
|
||||
type: 'toggle',
|
||||
id: 'project-keybinds',
|
||||
title: 'Enable Project Keybinds',
|
||||
description: 'Weather certain keybinds should work without the checklist visible.',
|
||||
default: false,
|
||||
} as ToggleSettingDefinition,
|
||||
PrivateMessageHighlight: {
|
||||
}),
|
||||
PrivateMessageHighlight: setting.asToggle({
|
||||
type: 'toggle',
|
||||
id: 'private-message-highlight',
|
||||
title: 'Highlight Private Messages',
|
||||
description: 'Whether private messages should be highlighted in the chat.',
|
||||
default: true,
|
||||
} as ToggleSettingDefinition,
|
||||
}),
|
||||
AdjustPageAlignment: setting.asEnum({
|
||||
type: 'enum',
|
||||
id: 'adjust-page-alignment',
|
||||
title: 'Adjust Page Alignment',
|
||||
description:
|
||||
'Whether the main page elements should be centered or offset opposite to the Checklist Position.',
|
||||
entries: [
|
||||
{ value: 'never', label: 'Never' },
|
||||
{ value: 'checklist-present', label: 'Checklist Needed' },
|
||||
{ value: 'always', label: 'Always' },
|
||||
],
|
||||
default: 'never',
|
||||
}),
|
||||
},
|
||||
} as const
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { SettingDefinitionBase } from '../types/settings.ts'
|
||||
import { isValidFor, type SettingDefinitionBase } from '../types/settings.ts'
|
||||
import { moderationSettings } from '../index.ts'
|
||||
|
||||
export class Settings {
|
||||
private readonly settings: { [id: string]: any }
|
||||
@@ -13,7 +14,9 @@ export class Settings {
|
||||
}
|
||||
|
||||
get<T>(definition: SettingDefinitionBase<T>): T {
|
||||
return this.settings[definition.id] ?? definition.default
|
||||
const value = this.settings[definition.id]
|
||||
|
||||
return (isValidFor(definition, value) ? value : undefined) ?? definition.default
|
||||
}
|
||||
|
||||
set<T>(definition: SettingDefinitionBase<T>, value?: T): void {
|
||||
@@ -23,3 +26,11 @@ export class Settings {
|
||||
this.onChange()
|
||||
}
|
||||
}
|
||||
|
||||
export function getMarginTarget(settings: Settings): string {
|
||||
return settings.get(moderationSettings.General.AdjustPageAlignment) == 'always'
|
||||
? settings.get(moderationSettings.General.ChecklistPosition) == 'right'
|
||||
? 'r'
|
||||
: 'l'
|
||||
: 'x'
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export interface SettingDefinitionBase<T> {
|
||||
type: string
|
||||
type: SettingDefinitionTypes
|
||||
id: string
|
||||
title: string
|
||||
description: string
|
||||
@@ -11,16 +11,53 @@ export interface ToggleSettingDefinition extends SettingDefinitionBase<boolean>
|
||||
type: 'toggle'
|
||||
}
|
||||
|
||||
export interface EnumSettingDefinition extends SettingDefinitionBase<string> {
|
||||
export interface EnumSettingDefinition<T extends string = string> extends SettingDefinitionBase<T> {
|
||||
type: 'enum'
|
||||
entries: {
|
||||
label: string
|
||||
value: string
|
||||
value: T
|
||||
}[]
|
||||
}
|
||||
|
||||
export interface StringSettingDefinition extends SettingDefinitionBase<string> {
|
||||
type: 'string'
|
||||
regex?: RegExp
|
||||
}
|
||||
|
||||
export type SettingDefinitions = ReturnType<(typeof setting)[keyof typeof setting]>
|
||||
export type SettingDefinitionTypes = SettingDefinitions['type']
|
||||
|
||||
export const setting = {
|
||||
asEnum: <const E extends ReadonlyArray<{ label: string; value: string }>>(
|
||||
data: EnumSettingDefinition<E[number]['value']> & { entries: E },
|
||||
): EnumSettingDefinition<E[number]['value']> => {
|
||||
return data as EnumSettingDefinition<E[number]['value']>
|
||||
},
|
||||
asToggle: (data: ToggleSettingDefinition) => data,
|
||||
asString: (data: StringSettingDefinition) => data,
|
||||
}
|
||||
|
||||
export function isValidFor(definitionBase: SettingDefinitionBase<any>, value: any): boolean {
|
||||
if (value != null) {
|
||||
// Tried my best with type safety but sadly having the `SettingDefinitions` as the type leads to issues handling types else where...
|
||||
const definition = definitionBase as SettingDefinitions
|
||||
if (
|
||||
definition.type == 'enum' &&
|
||||
definition.entries.map((entry) => entry.value).includes(value)
|
||||
) {
|
||||
return true
|
||||
} else if (definition.type == 'toggle' && typeof value === 'boolean') {
|
||||
return true
|
||||
} else if (
|
||||
definition.type == 'string' &&
|
||||
typeof value === 'string' &&
|
||||
(!definition.regex || value.match(definition.regex))
|
||||
) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
export type SettingDefinition =
|
||||
|
||||
Reference in New Issue
Block a user