Skip to content

Commit 8b3bbf9

Browse files
committed
feat: make issue control ids global
1 parent 38e42d4 commit 8b3bbf9

6 files changed

Lines changed: 126 additions & 47 deletions

File tree

‎apps/frontend/src/components/project-review/issue-list/issue-toggles.vue‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ function rowOptionLabel(entry: ReviewIssueControl) {
157157
}
158158
159159
function panelLabel(entry: ReviewIssueControl) {
160-
return entry.binding.panel.title ?? entry.control.issue.category
160+
return entry.binding.panel.title ?? props.issue.category
161161
}
162162
163163
function optionValue({ control }: ReviewIssueControl) {

‎apps/frontend/src/components/ui/project-issue-card/index.vue‎

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,10 @@ const canAddress = computed(() => !!currentMember.value?.accepted)
286286
const expandedAddressed = reactive(new Set<string>())
287287
const finishedExpanding = reactive(new Set<string>())
288288
const messages = defineMessages({
289-
issueOptions: { id: 'thread-issues.options', defaultMessage: 'Issue options' },
289+
issueOptions: {
290+
id: 'thread-issues.options',
291+
defaultMessage: 'Issue options',
292+
},
290293
markVerified: {
291294
id: 'thread-issues.mark-resolved',
292295
defaultMessage: 'Mark as resolved',
@@ -305,18 +308,39 @@ const messages = defineMessages({
305308
},
306309
showIssue: { id: 'thread-issues.show-issue', defaultMessage: 'Show issue' },
307310
hideIssue: { id: 'thread-issues.hide-issue', defaultMessage: 'Hide issue' },
308-
markAddressed: { id: 'thread-issues.mark-addressed', defaultMessage: 'Mark as addressed' },
311+
markAddressed: {
312+
id: 'thread-issues.mark-addressed',
313+
defaultMessage: 'Mark as addressed',
314+
},
309315
addressFailed: {
310316
id: 'thread-issues.address-failed',
311317
defaultMessage: 'Failed to mark issue as addressed',
312318
},
313-
addressed: { id: 'thread-issues.addressed', defaultMessage: 'Marked as addressed' },
314-
showAddressed: { id: 'thread-issues.show-addressed', defaultMessage: 'Show addressed' },
315-
hideAddressed: { id: 'thread-issues.hide-addressed', defaultMessage: 'Hide addressed' },
319+
addressed: {
320+
id: 'thread-issues.addressed',
321+
defaultMessage: 'Marked as addressed',
322+
},
323+
showAddressed: {
324+
id: 'thread-issues.show-addressed',
325+
defaultMessage: 'Show addressed',
326+
},
327+
hideAddressed: {
328+
id: 'thread-issues.hide-addressed',
329+
defaultMessage: 'Hide addressed',
330+
},
316331
resolved: { id: 'thread-issues.resolved', defaultMessage: 'Resolved' },
317-
showResolved: { id: 'thread-issues.show-resolved', defaultMessage: 'Show resolved' },
318-
hideResolved: { id: 'thread-issues.hide-resolved', defaultMessage: 'Hide resolved' },
319-
editName: { id: 'thread-issues.target.edit-name', defaultMessage: 'Edit name' },
332+
showResolved: {
333+
id: 'thread-issues.show-resolved',
334+
defaultMessage: 'Show resolved',
335+
},
336+
hideResolved: {
337+
id: 'thread-issues.hide-resolved',
338+
defaultMessage: 'Hide resolved',
339+
},
340+
editName: {
341+
id: 'thread-issues.target.edit-name',
342+
defaultMessage: 'Edit name',
343+
},
320344
editUrl: { id: 'thread-issues.target.edit-url', defaultMessage: 'Edit URL' },
321345
editSummary: {
322346
id: 'thread-issues.target.edit-summary',
@@ -393,7 +417,9 @@ const verifyMutation = useMutation({
393417
}) => {
394418
const results = await Promise.allSettled(
395419
facetIds.map((id) =>
396-
client.labrinth.threads_v3.editIssueFacet(id, { moderator_verified: verified }),
420+
client.labrinth.threads_v3.editIssueFacet(id, {
421+
moderator_verified: verified,
422+
}),
397423
),
398424
)
399425
const failure = results.find((result) => result.status === 'rejected')
@@ -402,11 +428,17 @@ const verifyMutation = useMutation({
402428
onSettled: async (_, __, { threadId, projectId }) => {
403429
emit('update-thread')
404430
await Promise.all([
405-
queryClient.invalidateQueries({ queryKey: threadId ? ['thread', threadId] : ['thread'] }),
431+
queryClient.invalidateQueries({
432+
queryKey: threadId ? ['thread', threadId] : ['thread'],
433+
}),
406434
...(projectId
407435
? [
408-
queryClient.invalidateQueries({ queryKey: ['project', 'v2', projectId] }),
409-
queryClient.invalidateQueries({ queryKey: ['project', 'v3', projectId] }),
436+
queryClient.invalidateQueries({
437+
queryKey: ['project', 'v2', projectId],
438+
}),
439+
queryClient.invalidateQueries({
440+
queryKey: ['project', 'v3', projectId],
441+
}),
410442
projectContext?.projectV2.value.id === projectId
411443
? projectContext.refreshProjectValidation()
412444
: Promise.resolve(),
@@ -508,7 +540,7 @@ function matchesTarget(what: Target): boolean {
508540
509541
function panelIssues(nodes: readonly PanelNode[]): Issue[] {
510542
return nodes.flatMap((node) =>
511-
node.type === 'section' ? panelIssues(node.children) : [node.issue],
543+
node.type === 'section' ? panelIssues(node.children) : node.issue ? [node.issue] : [],
512544
)
513545
}
514546

‎apps/frontend/src/providers/project-review/review-panels.ts‎

Lines changed: 71 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import type { createReviewSession } from './review-session'
2323
interface ResolvedIssueControlOptions {
2424
disabled: boolean
2525
required: boolean
26-
issue: Issue
26+
issue?: Issue
2727
issueId: string
2828
childToggleIds?: string[]
2929
label: string
@@ -32,7 +32,12 @@ interface ResolvedIssueControlOptions {
3232

3333
export type ResolvedIssueControl = ResolvedIssueControlOptions &
3434
(
35-
| { type: 'toggle'; id?: string; issueListLabel?: string; issueListGroup?: string }
35+
| {
36+
type: 'toggle'
37+
id?: string
38+
issueListLabel?: string
39+
issueListGroup?: string
40+
}
3641
| {
3742
type: 'markdown' | 'text' | 'textarea'
3843
key: string
@@ -168,7 +173,10 @@ export function createReviewPanels(
168173
| 'organizationMembers'
169174
| 'wasReviewed'
170175
| 'permissions'
171-
> & { previousLinks: ReadonlyMap<string, string>; previousIssueIds?: readonly string[] }
176+
> & {
177+
previousLinks: ReadonlyMap<string, string>
178+
previousIssueIds?: readonly string[]
179+
}
172180
>,
173181
definitions: Record<string, PanelRegistration> = reviewPanels,
174182
) {
@@ -277,6 +285,15 @@ export function createReviewPanels(
277285
return result
278286
}
279287

288+
function contextSelectValues(projectId: string, issueId: string, key: string): string[] {
289+
const values = session.read(projectId, 'issue-select')[issueId]
290+
const scope =
291+
values && typeof values === 'object' && !(values instanceof Set) && key in values
292+
? issueId
293+
: ''
294+
return readSelectValues(projectId, scope, key)
295+
}
296+
280297
const selectedIssueIds = computed(() => {
281298
const projectId = project.value?.id
282299
if (!projectId) return []
@@ -299,13 +316,12 @@ export function createReviewPanels(
299316
const context: ReviewContext = {
300317
projectV3,
301318
...reviewData.value,
302-
getTextValue: (id, issueId) => (issueId ? (textValues(projectV3.id, issueId)[id] ?? '') : ''),
303-
getSelectValue: (id, issueId) =>
304-
issueId ? (readSelectValues(projectV3.id, issueId, id)[0] ?? '') : '',
305-
getSelectValues: (id, issueId) =>
306-
issueId ? readSelectValues(projectV3.id, issueId, id) : [],
307-
getMarkdownValue: (id, issueId) =>
308-
issueId ? (textValues(projectV3.id, issueId)[id] ?? '') : '',
319+
getTextValue: (id, issueId = '') =>
320+
textValues(projectV3.id, issueId)[id] ?? textValues(projectV3.id, '')[id] ?? '',
321+
getSelectValue: (id, issueId = '') => contextSelectValues(projectV3.id, issueId, id)[0] ?? '',
322+
getSelectValues: (id, issueId = '') => contextSelectValues(projectV3.id, issueId, id),
323+
getMarkdownValue: (id, issueId = '') =>
324+
textValues(projectV3.id, issueId)[id] ?? textValues(projectV3.id, '')[id] ?? '',
309325
selected: {
310326
items: {},
311327
issueIds: selectedIssueIds.value,
@@ -347,19 +363,23 @@ export function createReviewPanels(
347363
sections.push(...resolveNodes(node.children, resolveWithContext(node.label, context)))
348364
continue
349365
}
350-
const issue = itemIssues.get(node.issue.id) ?? node.issue
351-
const issueId = issue.id
366+
const issue = node.issue ? (itemIssues.get(node.issue.id) ?? node.issue) : undefined
367+
const issueId = issue?.id ?? ''
352368
const issueContext: ReviewContext = {
353369
...context,
354-
getMarkdownValue: (id, scope = issueId) => textValues(projectV3.id, scope)[id] ?? '',
355-
getTextValue: (id, scope = issueId) => textValues(projectV3.id, scope)[id] ?? '',
370+
getMarkdownValue: (id, scope = issueId) =>
371+
textValues(projectV3.id, scope)[id] ?? textValues(projectV3.id, '')[id] ?? '',
372+
getTextValue: (id, scope = issueId) =>
373+
textValues(projectV3.id, scope)[id] ?? textValues(projectV3.id, '')[id] ?? '',
356374
getSelectValue: (id, scope = issueId) =>
357-
readSelectValues(projectV3.id, scope, id)[0] ?? '',
358-
getSelectValues: (id, scope = issueId) => readSelectValues(projectV3.id, scope, id),
375+
contextSelectValues(projectV3.id, scope, id)[0] ?? '',
376+
getSelectValues: (id, scope = issueId) => contextSelectValues(projectV3.id, scope, id),
359377
selected: {
360378
items: {},
361379
issueIds: selectedIssueIds.value,
362-
toggleIds: [...selectedToggleIds(projectV3.id, issueId)],
380+
toggleIds: issue
381+
? [...selectedToggleIds(projectV3.id, issueId)]
382+
: context.selected.toggleIds,
363383
},
364384
}
365385
if (resolveWithContext(node.shown, issueContext) === false) continue
@@ -609,7 +629,7 @@ export function createReviewPanels(
609629
for (const binding of panels.value.values()) {
610630
for (const section of binding.panel.sections) {
611631
for (const control of section.controls) {
612-
if (control.childToggleIds) continue
632+
if (control.childToggleIds || !control.issue) continue
613633
const issue: ReviewIssue = issues.get(control.issueId) ?? {
614634
id: control.issueId,
615635
title: control.issue.title,
@@ -716,6 +736,10 @@ export function createReviewPanels(
716736
const projectV3 = project.value
717737
if (!projectV3) return []
718738
const issues = new Map<string, IssueSelection>()
739+
const sharedInputs = {
740+
textValues: {} as Record<string, string>,
741+
selectValues: {} as Record<string, string[]>,
742+
}
719743
for (const entry of availableIssues.value) {
720744
if (session.read(projectV3.id, 'issue-active')[entry.id] !== true) continue
721745
issues.set(entry.id, {
@@ -733,7 +757,7 @@ export function createReviewPanels(
733757
: [],
734758
),
735759
}
736-
: entry.controls[0].control.issue,
760+
: entry.controls[0].control.issue!,
737761
active: true,
738762
keys: new Set(),
739763
textValues: {},
@@ -744,6 +768,14 @@ export function createReviewPanels(
744768
for (const binding of panels.value.values()) {
745769
for (const section of binding.panel.sections) {
746770
for (const control of section.controls) {
771+
if (!control.issue) {
772+
if (control.disabled) continue
773+
if (control.type === 'select')
774+
sharedInputs.selectValues[control.key] = selectValues(binding, control)
775+
else if (control.type !== 'toggle')
776+
sharedInputs.textValues[control.key] = textValue(binding, control)
777+
continue
778+
}
747779
if (
748780
control.childToggleIds ||
749781
control.disabled ||
@@ -791,10 +823,14 @@ export function createReviewPanels(
791823
issueIds: selectedIssueIds.value,
792824
toggleIds: [...keys],
793825
},
794-
getMarkdownValue: (key, scope = id) => issues.get(scope)?.textValues[key] ?? '',
795-
getTextValue: (key, scope = id) => issues.get(scope)?.textValues[key] ?? '',
796-
getSelectValue: (key, scope = id) => issues.get(scope)?.selectValues[key]?.[0] ?? '',
797-
getSelectValues: (key, scope = id) => issues.get(scope)?.selectValues[key] ?? [],
826+
getMarkdownValue: (key, scope = id) =>
827+
issues.get(scope)?.textValues[key] ?? sharedInputs.textValues[key] ?? '',
828+
getTextValue: (key, scope = id) =>
829+
issues.get(scope)?.textValues[key] ?? sharedInputs.textValues[key] ?? '',
830+
getSelectValue: (key, scope = id) =>
831+
(issues.get(scope)?.selectValues[key] ?? sharedInputs.selectValues[key])?.[0] ?? '',
832+
getSelectValues: (key, scope = id) =>
833+
issues.get(scope)?.selectValues[key] ?? sharedInputs.selectValues[key] ?? [],
798834
}
799835
const controls = availableIssues.value.find((entry) => entry.id === id)?.controls ?? []
800836
if (
@@ -829,11 +865,20 @@ export function createReviewPanels(
829865
})
830866
})
831867

832-
const validationErrors = computed(() =>
833-
activeIssues.value
868+
const validationErrors = computed(() => [
869+
...activeIssues.value
834870
.filter(({ id }) => !isRestoredIssue(id))
835871
.flatMap(({ id, missing }) => missing.map((key) => ({ issueId: id, key }))),
836-
)
872+
...[...panels.value.values()].flatMap((binding) =>
873+
binding.panel.sections.flatMap((section) =>
874+
section.controls.flatMap((control) =>
875+
!control.issue && control.type !== 'toggle' && missing(binding, control)
876+
? [{ issueId: '', key: control.key }]
877+
: [],
878+
),
879+
),
880+
),
881+
])
837882

838883
return {
839884
previousLinks: computed(() => reviewData.value.previousLinks),

‎packages/moderation/src/data/issues/component-builders/item-panels.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ export function expandItemReviewPanels(
8989
collectItemControls(node.children, item)
9090
continue
9191
}
92-
definitions.set(node.issue.id, node.issue)
92+
if (node.issue) definitions.set(node.issue.id, node.issue)
9393
if (node.type === 'toggle') {
9494
const ids = toggles.get(node.issue.id) ?? []
9595
ids.push(node.id ?? node.issue.id)
@@ -164,11 +164,11 @@ export function expandItemReviewPanels(
164164
},
165165
]
166166
}
167-
const issue = issues.get(node.issue.id) ?? node.issue
167+
const issue = node.issue ? (issues.get(node.issue.id) ?? node.issue) : undefined
168168
if (!item) return [node]
169169
const scoped = {
170170
...node,
171-
issue,
171+
...(issue ? { issue } : {}),
172172
id: `${item.key}:${node.type === 'toggle' ? (node.id ?? node.issue.id) : node.id}`,
173173
label: (ctx: ReviewContext) => resolve(node.label, scopedContext(ctx, item)),
174174
shown: (ctx: ReviewContext) => resolve(node.shown, scopedContext(ctx, item)) !== false,

‎packages/moderation/src/data/issues/component-builders/types.ts‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ export interface ReviewContext {
2828
items: Readonly<Record<string, readonly { key: string; context: ReviewContext }[]>>
2929
}
3030
/**
31-
* Returns a markdown value for the current issue, or an empty string if unavailable.
32-
* Panel and section callbacks must supply an issue ID to read stored input values.
31+
* Returns an input value for the current issue, falling back to shared inputs, or empty if unavailable.
32+
* Panel and section callbacks read shared inputs unless an issue ID is supplied.
3333
* Only visible, enabled controls contribute when resolving messages.
3434
* UI callbacks read stored values; message callbacks also resolve defaults.
3535
*/
@@ -60,7 +60,8 @@ export interface Issue {
6060
export type IssueConfig = Issue
6161

6262
interface IssueControlOptions {
63-
issue: Issue
63+
/** Omit to share this input across issues by its control ID. */
64+
issue?: Issue
6465
label: WithContext<string>
6566
shown?: WithContext<boolean>
6667
tooltip?: WithContext<string>
@@ -69,6 +70,7 @@ interface IssueControlOptions {
6970

7071
export interface IssueToggle extends IssueControlOptions {
7172
type: 'toggle'
73+
issue: Issue
7274
/** Optional label for this toggle in the issue list. */
7375
issueListLabel?: WithContext<string>
7476
/** Optional issue-list row for this toggle. Defaults to the toggle label. */

‎packages/moderation/src/data/issues/summary.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ export const summaryReviewPanel = panel({
107107
}).content(
108108
textarea({
109109
issue: summaryFormattingIssue,
110-
id: 'suggestion',
110+
id: 'summary-suggestion',
111111
label: 'Suggestion',
112112
maxlength: 256,
113113
rows: 3,

0 commit comments

Comments
 (0)