Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

❇️ Subject priority score #287

Merged
merged 7 commits into from
Feb 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion app/actions/ModActionPanel/QuickAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
toLabelVal,
isSelfLabel,
ModerationLabel,
LabelChip,
} from '@/common/labels'
import { FullScreenActionPanel } from '@/common/FullScreenActionPanel'
import { PreviewCard } from '@/common/PreviewCard'
Expand Down Expand Up @@ -60,6 +61,8 @@ import { SubjectTag } from 'components/tags/SubjectTag'
import { HighProfileWarning } from '@/repositories/HighProfileWarning'
import { EmailComposer } from 'components/email/Composer'
import { ActionPolicySelector } from '@/reports/ModerationForm/ActionPolicySelector'
import { HandRaisedIcon } from '@heroicons/react/24/solid'
import { PriorityScore } from '@/subject/PriorityScore'

const FORM_ID = 'mod-action-panel'
const useBreakpoint = createBreakpoint({ xs: 340, sm: 640 })
Expand Down Expand Up @@ -195,6 +198,7 @@ function Form(
const isDivertEvent = modEventType === MOD_EVENTS.DIVERT
const isMuteEvent = modEventType === MOD_EVENTS.MUTE
const isMuteReporterEvent = modEventType === MOD_EVENTS.MUTE_REPORTER
const isPriorityScoreEvent = modEventType === MOD_EVENTS.SET_PRIORITY
const isCommentEvent = modEventType === MOD_EVENTS.COMMENT
const isTakedownEvent = modEventType === MOD_EVENTS.TAKEDOWN
const isAckEvent = modEventType === MOD_EVENTS.ACKNOWLEDGE
Expand Down Expand Up @@ -288,6 +292,10 @@ function Form(
coreEvent.sticky = true
}

if (isPriorityScoreEvent) {
coreEvent.score = Number(formData.get('priorityScore'))
}

if (formData.get('tags')) {
const tags = String(formData.get('tags'))
.split(',')
Expand Down Expand Up @@ -613,7 +621,12 @@ function Form(

{!!subjectStatus && (
<div className="pb-4">
<p>
<p className="flex flex-row items-center">
{!!subjectStatus?.priorityScore && (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does 0 means low priority ? If so, it might make sense to distinguish null here:

Suggested change
{!!subjectStatus?.priorityScore && (
{subjectStatus?.priorityScore != null && (

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 means either we haven't set a priority or we have intentionally set it to 0. it always defaults to 0 either way and never null and we won't care why it's 0.

<PriorityScore
priorityScore={subjectStatus.priorityScore}
/>
)}
<SubjectReviewStateBadge subjectStatus={subjectStatus} />
<LastReviewedTimestamp subjectStatus={subjectStatus} />
</p>
Expand Down Expand Up @@ -698,6 +711,26 @@ function Form(
</div>
{shouldShowDurationInHoursField && (
<div className="flex flex-row gap-2">
{isPriorityScoreEvent && (
<FormLabel
label=""
className="mt-2 w-1/2"
htmlFor="priorityScore"
>
<Input
type="number"
id="priorityScore"
name="priorityScore"
className="block w-full"
placeholder="Score between 0-100"
autoFocus
min={0}
max={100}
step={1}
required
/>
</FormLabel>
)}
<FormLabel
label=""
htmlFor="durationInHours"
Expand All @@ -723,6 +756,8 @@ function Form(
? 'Mute duration'
: isLabelEvent
? 'Label duration'
: isPriorityScoreEvent
? 'Score duration'
: ''
}
/>
Expand Down
7 changes: 7 additions & 0 deletions app/reports/page-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ const getSortParams = (params: ReadonlyURLSearchParams) => {
'lastReviewedAt',
'reportedRecordsCount',
'takendownRecordsCount',
'priorityScore',
].includes(sortField ?? '')
) {
sortField = 'lastReportedAt'
Expand Down Expand Up @@ -316,6 +317,7 @@ function useModerationQueueQuery() {
const minAccountSuspendCount = params.get('minAccountSuspendCount')
const minReportedRecordsCount = params.get('minReportedRecordsCount')
const minTakendownRecordsCount = params.get('minTakendownRecordsCount')
const minPriorityScore = params.get('minPriorityScore')
const { sortField, sortDirection } = getSortParams(params)
const { lastReviewedBy, subject, reporters, includeAllUserRecords } =
useFluentReportSearchParams()
Expand Down Expand Up @@ -343,6 +345,7 @@ function useModerationQueueQuery() {
minAccountSuspendCount,
minReportedRecordsCount,
minTakendownRecordsCount,
minPriorityScore,
},
],
queryFn: async ({ pageParam }) => {
Expand Down Expand Up @@ -407,6 +410,10 @@ function useModerationQueueQuery() {
queryParams.minTakendownRecordsCount = Number(minTakendownRecordsCount)
}

if (minPriorityScore) {
queryParams.minPriorityScore = Number(minPriorityScore)
}

// For these fields, we only want to add them to the filter if the values are set, otherwise, defaults will kick in
Object.entries({
sortField,
Expand Down
2 changes: 1 addition & 1 deletion components/common/labels/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function LabelChip(props: ComponentProps<'span'>) {
const { className = '', ...others } = props
return (
<span
className={`${className} inline-flex mx-1 items-center rounded-md px-2 py-0.5 text-xs font-medium bg-gray-100 text-gray-600 font-semibold`}
className={`inline-flex mx-1 items-center rounded-md px-2 py-0.5 text-xs font-medium bg-gray-100 text-gray-600 font-semibold ${className}`}
{...others}
/>
)
Expand Down
27 changes: 27 additions & 0 deletions components/mod-event/EventItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,28 @@ const Email = ({
)
}

const PriorityScore = ({
modEvent,
}: {
modEvent: ToolsOzoneModerationDefs.ModEventView & {
event: ToolsOzoneModerationDefs.ModEventPriorityScore
}
}) => {
return (
<>
<p>
Set to <b>{modEvent.event.score}</b> By{' '}
<LinkToAuthor
createdBy={modEvent.createdBy}
creatorHandle={modEvent.creatorHandle}
/>
</p>

{modEvent.event.comment && <p>{modEvent.event.comment}</p>}
</>
)
}

function isMessageSubject(
subject: ToolsOzoneModerationDefs.ModEventView['subject'],
): subject is ChatBskyConvoDefs.MessageRef {
Expand Down Expand Up @@ -423,6 +445,11 @@ export const ModEventItem = ({
if (isModEventType(modEvent, ToolsOzoneModerationDefs.isModEventEmail)) {
eventItem = <Email modEvent={modEvent} />
}
if (
isModEventType(modEvent, ToolsOzoneModerationDefs.isModEventPriorityScore)
) {
eventItem = <PriorityScore modEvent={modEvent} />
}
const previewSubject = modEvent.subject.uri || modEvent.subject.did
return (
<div className="mt-4">
Expand Down
4 changes: 4 additions & 0 deletions components/mod-event/ItemTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ export const ItemTitle = ({
eventColor = 'text-blue-400'
eventTitle = 'Email sent'
}
if (ToolsOzoneModerationDefs.isModEventPriorityScore(modEvent.event)) {
eventColor = 'text-blue-400'
eventTitle = 'Updated priority score'
}
const subjectStatus = modEvent.repo
? modEvent.repo.moderation.subjectStatus
: modEvent.record
Expand Down
4 changes: 4 additions & 0 deletions components/mod-event/SelectorButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ const actions = [
text: 'Enable Video Upload',
key: MOD_EVENTS.ENABLE_VIDEO_UPLOAD,
},
{
text: 'Set Priority Score',
key: MOD_EVENTS.SET_PRIORITY,
},
]
const actionsByKey = actions.reduce((acc, action) => {
acc[action.key] = action.text
Expand Down
2 changes: 2 additions & 0 deletions components/mod-event/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const MOD_EVENTS = {
EMAIL: 'tools.ozone.moderation.defs#modEventEmail',
TAG: 'tools.ozone.moderation.defs#modEventTag',
DIVERT: 'tools.ozone.moderation.defs#modEventDivert',
SET_PRIORITY: 'tools.ozone.moderation.defs#modEventPriorityScore',
APPEAL: 'appeal',
DISABLE_DMS: 'disableDms',
ENABLE_DMS: 'enableDms',
Expand All @@ -36,6 +37,7 @@ export const MOD_EVENT_TITLES = {
[MOD_EVENTS.EMAIL]: 'Email Sent',
[MOD_EVENTS.RESOLVE_APPEAL]: 'Appeal Resolved',
[MOD_EVENTS.TAG]: 'Tag',
[MOD_EVENTS.SET_PRIORITY]: 'Set Priority',
[MOD_EVENTS.APPEAL]: 'Appeal',
[MOD_EVENTS.DIVERT]: 'Divert',
[MOD_EVENTS.DISABLE_DMS]: 'Disable DMs',
Expand Down
37 changes: 27 additions & 10 deletions components/reports/QueueFilter/Stats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const QueueFilterStats = () => {
setMinAccountSuspendCount,
setMinReportedRecordsCount,
setMinTakendownRecordsCount,
setMinPriorityScore,
} = useQueueFilter()

const handleInputChange =
Expand Down Expand Up @@ -45,16 +46,32 @@ export const QueueFilterStats = () => {
/>
</FormLabel>
</div>
<FormLabel label="Min. Takendown records">
<Input
type="number"
className="block w-full"
id="minTakendownRecordsCount"
name="minTakendownRecordsCount"
value={queueFilters.minTakendownRecordsCount || ''}
onChange={handleInputChange(setMinTakendownRecordsCount)}
/>
</FormLabel>
<div className="flex flex-row gap-2">
<FormLabel label="Min. Takendown records">
<Input
type="number"
className="block w-full"
id="minTakendownRecordsCount"
name="minTakendownRecordsCount"
value={queueFilters.minTakendownRecordsCount || ''}
onChange={handleInputChange(setMinTakendownRecordsCount)}
/>
</FormLabel>
<FormLabel label="Min. Priority score">
<Input
type="number"
id="minPriorityScore"
className="block w-full"
name="minPriorityScore"
min={0}
max={100}
step={1}
placeholder="0-100"
value={queueFilters.minPriorityScore || ''}
onChange={handleInputChange(setMinPriorityScore)}
/>
</FormLabel>
</div>
</div>
</div>
)
Expand Down
5 changes: 5 additions & 0 deletions components/reports/useQueueFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ export const useQueueFilter = () => {
updateFilters({ minTakendownRecordsCount })
}

const setMinPriorityScore = (minPriorityScore?: number) => {
updateFilters({ minPriorityScore })
}

return {
queueFilters,
updateTagExclusions,
Expand All @@ -146,5 +150,6 @@ export const useQueueFilter = () => {
setMinAccountSuspendCount,
setMinReportedRecordsCount,
setMinTakendownRecordsCount,
setMinPriorityScore,
}
}
27 changes: 27 additions & 0 deletions components/subject/PriorityScore.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { LabelChip } from '@/common/labels'
import { classNames } from '@/lib/util'
import { HandRaisedIcon } from '@heroicons/react/24/solid'

export const PriorityScore = ({
priorityScore,
size,
}: {
priorityScore: number
size?: 'sm'
}) => {
if (!priorityScore) {
return null
}
Comment on lines +12 to +14
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since priorityScore is not optional, does it make sense to not render when priorityScore is 0 (low priority) ?

Suggested change
if (!priorityScore) {
return null
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't have a use case where 0 priority score needs to be taken into account by mods so a non-positive integer does not need to be rendered.

return (
<LabelChip
className={classNames(
'flex flex-row gap-1 items-center bg-orange-300 text-orange-800',
size === 'sm' ? 'text-xs px-1 py-0' : '',
)}
title={`This subject's priority score is set to ${priorityScore} out of 100. Subjects with higher score should be reviewed more urgently.`}
>
<HandRaisedIcon className="h-3 w-3" />
{priorityScore}
</LabelChip>
)
}
Loading
Loading