From bbe4744f961599dc037c7324704fe675fc24e13b Mon Sep 17 00:00:00 2001 From: double beep <38133098+double-beep@users.noreply.github.com> Date: Tue, 11 Jun 2024 13:02:51 +0000 Subject: [PATCH] feat: ability to add new flag types from modal --- build.js | 2 +- src/AdvancedFlagging.ts | 2 +- src/Configuration.ts | 5 +- src/FlagTypes.ts | 19 ++++++++ src/modals/comments/main.ts | 96 +++++++++++++++++++++++++++++++++---- 5 files changed, 110 insertions(+), 14 deletions(-) diff --git a/build.js b/build.js index a99521f..dd8edb5 100644 --- a/build.js +++ b/build.js @@ -1,7 +1,7 @@ import { build } from 'esbuild'; import info from './package.json' assert { type: 'json' }; -const svgsNeeded = ['Checkmark', 'Clear', 'EyeOff', 'Flag', 'Pencil', 'Trash']; +const svgsNeeded = ['Checkmark', 'Clear', 'EyeOff', 'Flag', 'Pencil', 'Trash', 'Plus']; const svgsUrls = svgsNeeded.map(svgName => { return `// @resource icon${svgName} https://cdn.sstatic.net/Img/stacks-icons/${svgName}.svg`; }); diff --git a/src/AdvancedFlagging.ts b/src/AdvancedFlagging.ts index 7ecaf05..6747014 100644 --- a/src/AdvancedFlagging.ts +++ b/src/AdvancedFlagging.ts @@ -23,7 +23,7 @@ import { Store, Cached } from './UserscriptTools/Store'; // - choose the bots to which to send feedback // - choose the feedback to send // - also the recommend deletion/Delete popup -// - Add new category/flagtype +// - Add option to flag post from recommend deletion popup // function setupStyles(): void { diff --git a/src/Configuration.ts b/src/Configuration.ts index 6f218f0..35e9809 100644 --- a/src/Configuration.ts +++ b/src/Configuration.ts @@ -58,7 +58,8 @@ function cacheCategories(): void { { isDangerous: category.isDangerous, name: category.name, - appliesTo: category.appliesTo + appliesTo: category.appliesTo, + id: category.id } as CachedCategory )); @@ -77,7 +78,7 @@ function setupDefaults(): void { } if (!Store.categories.length - || !('appliesTo' in Store.categories[0])) { + || !('id' in Store.categories[0])) { cacheCategories(); } diff --git a/src/FlagTypes.ts b/src/FlagTypes.ts index affc06c..0a15529 100644 --- a/src/FlagTypes.ts +++ b/src/FlagTypes.ts @@ -1,4 +1,5 @@ import { FlagTypeFeedbacks, PostType, FlagNames } from './shared'; +import { CachedFlag } from './UserscriptTools/Store'; export type Flags = 'AnswerNotAnAnswer' | 'PostOffensive' @@ -33,6 +34,7 @@ export interface FlagCategory { isDangerous: boolean; // whether each FlagType of the category should have a red colour name: string; // will appear on the edit comments & flags modal appliesTo: PostType[]; // where it'll appear (question, answer or both) + id: number; FlagTypes: FlagType[]; } @@ -41,6 +43,7 @@ export const flagCategories: FlagCategory[] = [ isDangerous: true, name: 'Red flags', appliesTo: ['Answer', 'Question'], + id: 1, FlagTypes: [ { id: 1, @@ -62,6 +65,7 @@ export const flagCategories: FlagCategory[] = [ isDangerous: true, name: 'Guttenberg mod flags', appliesTo: [ 'Answer' ], + id: 2, FlagTypes: [ { id: 3, @@ -102,6 +106,7 @@ export const flagCategories: FlagCategory[] = [ isDangerous: false, name: 'Answer-related', appliesTo: [ 'Answer' ], + id: 3, FlagTypes: [ { id: 6, @@ -232,6 +237,7 @@ export const flagCategories: FlagCategory[] = [ isDangerous: false, name: 'General', appliesTo: ['Answer', 'Question'], + id: 4, FlagTypes: [ { id: 15, @@ -257,3 +263,16 @@ export const flagCategories: FlagCategory[] = [ ] } ]; + +export function getEmptyFlagType(id: number, belongsTo: string): CachedFlag { + return { + id, + displayName: 'Name', + reportType: FlagNames.NoFlag, + feedbacks: { Smokey: '', Natty: '', Guttenberg: '', 'Generic Bot': '' }, + sendWhenFlagRaised: false, + downvote: false, + enabled: true, + belongsTo + }; +} diff --git a/src/modals/comments/main.ts b/src/modals/comments/main.ts index 0ad815e..5cbb51d 100644 --- a/src/modals/comments/main.ts +++ b/src/modals/comments/main.ts @@ -1,4 +1,4 @@ -import { Cached, CachedFlag, Store } from '../../UserscriptTools/Store'; +import { Cached, CachedCategory, CachedFlag, Store } from '../../UserscriptTools/Store'; import { getIconPath, displayStacksToast, @@ -21,6 +21,7 @@ import { Toggle, Input, } from '@userscripters/stacks-helpers'; +import { getEmptyFlagType } from '../../FlagTypes'; /* In this case, we are caching a FlagType, but removing unnecessary properties. Only the Id, FlagText, and Comments (both LowRep and HighRep) and the flag's name @@ -286,9 +287,7 @@ function getH3(displayName: string): HTMLHeadElement { return h3; } -function createFlagTypeDiv( - flagType: CachedFlag -): HTMLDivElement { +function createFlagTypeDiv(flagType: CachedFlag): HTMLDivElement { const { id, displayName, @@ -347,15 +346,91 @@ function createFlagTypeDiv( return card; } -function createCategoryDiv(displayName: string): HTMLDivElement { +function createCategoryDiv(category: Partial): HTMLDivElement { const container = document.createElement('div'); container.classList.add('flex--item'); + const wrapper = document.createElement('div'); + wrapper.classList.add('d-flex', 'ai-center', 'mb8'); + const header = document.createElement('h2'); - header.classList.add('ta-center', 'mb8', 'fs-title'); - header.innerHTML = displayName; + header.classList.add('flex--item', 'fs-title', 'mb0', 'mr-auto', 'fw-normal'); + header.textContent = category.name ?? ''; + + const buttonContainer = document.createElement('div'); + buttonContainer.classList.add('d-flex', 'g8', 'ai-center'); + + const addNew = Buttons.makeStacksButton( + `advanced-flagging-add-new-${category.id}`, + 'New', + { + type: [ 'outlined' ], + iconConfig: { + name: 'iconPlus', + path: getIconPath('iconPlus'), + height: 18, + width: 18 + }, + } + ); + + addNew.addEventListener('click', () => { + const id = Math.max(...Store.flagTypes.map(({ id }) => id)); + + const flagType = getEmptyFlagType(id + 1, category.name ?? ''); + Store.flagTypes.push(flagType); + Store.updateFlagTypes(); + + const div = createFlagTypeDiv(flagType); + + // fade in + div.style.display = 'none'; + container.append(div); + $(div).fadeIn(); + + // click edit & focus on name input + div.querySelector('[id^="advanced-flagging-edit-flagtype-"]')?.click(); + div.querySelector('[id^="advanced-flagging-flag-name-"]')?.focus(); + }); + + const flagTypes = Store.flagTypes.filter(({ belongsTo }) => belongsTo === category.name); + const enabled = flagTypes.some(({ enabled }) => enabled); + + const toggle = Toggle.makeStacksToggle( + `advanced-flagging-toggle-category-${category.id}`, + { text: '' }, + enabled + ).querySelector('.s-toggle-switch') as HTMLInputElement; + + toggle.addEventListener('change', () => { + container + .querySelectorAll('input[id^="advanced-flagging-toggle-flagtype-"]') + .forEach(box => { + (box as HTMLInputElement).checked = toggle.checked; + }); + + Store.flagTypes + .filter(({ belongsTo }) => belongsTo === category.name) + .forEach(flag => { + flag.enabled = toggle.checked; + + const card = document.querySelector(`[data-flag-id="${flag.id}"]`); + if (!card) return; + + card.classList[toggle.checked ? 'remove' : 'add']('s-card__muted'); + }); + Store.updateFlagTypes(); + + displayStacksToast( + `Successfully ${toggle.checked ? 'en' : 'dis'}abled all flag types from this category`, + 'success', + true + ); + }); - container.append(header); + buttonContainer.append(addNew, toggle); + wrapper.append(header, buttonContainer); + container.append(wrapper); return container; } @@ -366,8 +441,9 @@ function getCommentsModalBody(): HTMLElement { const categories = Store.categories .filter(({ name }) => name) - .map(({ name }) => { - const div = createCategoryDiv(name ?? ''); + .map(category => { + const { name } = category; + const div = createCategoryDiv(category); const flagTypes = Store.flagTypes // only those belonging to "Name" category