Skip to content

Commit

Permalink
❇️ Subject priority score (#287)
Browse files Browse the repository at this point in the history
* ✨ Allow sorting the queue by priority score

* ✨ Add priority score event

* ✨ Add priority score data in table

* ✨ Add priority score filter

* 💄 Add placeholder

* ⬆️ Update atproto/api version
  • Loading branch information
foysalit authored Feb 8, 2025
1 parent 481f6d3 commit 3f205b2
Show file tree
Hide file tree
Showing 15 changed files with 309 additions and 191 deletions.
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 && (
<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
}
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

0 comments on commit 3f205b2

Please sign in to comment.