Skip to content

Commit

Permalink
feat: ability to add new flag types from modal
Browse files Browse the repository at this point in the history
  • Loading branch information
double-beep authored Jun 11, 2024
1 parent 8c2b7be commit bbe4744
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 14 deletions.
2 changes: 1 addition & 1 deletion build.js
Original file line number Diff line number Diff line change
@@ -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`;
});
Expand Down
2 changes: 1 addition & 1 deletion src/AdvancedFlagging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
// </TODO>

function setupStyles(): void {
Expand Down
5 changes: 3 additions & 2 deletions src/Configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ function cacheCategories(): void {
{
isDangerous: category.isDangerous,
name: category.name,
appliesTo: category.appliesTo
appliesTo: category.appliesTo,
id: category.id
} as CachedCategory
));

Expand All @@ -77,7 +78,7 @@ function setupDefaults(): void {
}

if (!Store.categories.length
|| !('appliesTo' in Store.categories[0])) {
|| !('id' in Store.categories[0])) {
cacheCategories();
}

Expand Down
19 changes: 19 additions & 0 deletions src/FlagTypes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { FlagTypeFeedbacks, PostType, FlagNames } from './shared';
import { CachedFlag } from './UserscriptTools/Store';

export type Flags = 'AnswerNotAnAnswer'
| 'PostOffensive'
Expand Down Expand Up @@ -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[];
}

Expand All @@ -41,6 +43,7 @@ export const flagCategories: FlagCategory[] = [
isDangerous: true,
name: 'Red flags',
appliesTo: ['Answer', 'Question'],
id: 1,
FlagTypes: [
{
id: 1,
Expand All @@ -62,6 +65,7 @@ export const flagCategories: FlagCategory[] = [
isDangerous: true,
name: 'Guttenberg mod flags',
appliesTo: [ 'Answer' ],
id: 2,
FlagTypes: [
{
id: 3,
Expand Down Expand Up @@ -102,6 +106,7 @@ export const flagCategories: FlagCategory[] = [
isDangerous: false,
name: 'Answer-related',
appliesTo: [ 'Answer' ],
id: 3,
FlagTypes: [
{
id: 6,
Expand Down Expand Up @@ -232,6 +237,7 @@ export const flagCategories: FlagCategory[] = [
isDangerous: false,
name: 'General',
appliesTo: ['Answer', 'Question'],
id: 4,
FlagTypes: [
{
id: 15,
Expand All @@ -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
};
}
96 changes: 86 additions & 10 deletions src/modals/comments/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Cached, CachedFlag, Store } from '../../UserscriptTools/Store';
import { Cached, CachedCategory, CachedFlag, Store } from '../../UserscriptTools/Store';
import {
getIconPath,
displayStacksToast,
Expand All @@ -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
Expand Down Expand Up @@ -286,9 +287,7 @@ function getH3(displayName: string): HTMLHeadElement {
return h3;
}

function createFlagTypeDiv(
flagType: CachedFlag
): HTMLDivElement {
function createFlagTypeDiv(flagType: CachedFlag): HTMLDivElement {
const {
id,
displayName,
Expand Down Expand Up @@ -347,15 +346,91 @@ function createFlagTypeDiv(
return card;
}

function createCategoryDiv(displayName: string): HTMLDivElement {
function createCategoryDiv(category: Partial<CachedCategory>): 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<HTMLButtonElement>('[id^="advanced-flagging-edit-flagtype-"]')?.click();
div.querySelector<HTMLInputElement>('[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;
}
Expand All @@ -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
Expand Down

0 comments on commit bbe4744

Please sign in to comment.