Skip to content

Commit

Permalink
WIP settings section for custom display rules
Browse files Browse the repository at this point in the history
  • Loading branch information
timo95 committed Jun 1, 2024
1 parent 9acd53b commit 13b176b
Show file tree
Hide file tree
Showing 13 changed files with 189 additions and 71 deletions.
38 changes: 26 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
"private": true,
"devDependencies": {
"@types/greasemonkey": "^4.0.7",
"@types/sortablejs": "^1.15.8",
"browserslist": "^4.23.0",
"cross-env": "^7.0.3",
"css-loader": "^7.1.1",
"css-loader": "^7.1.2",
"cssimportant-loader": "^0.4.0",
"sass": "^1.75.0",
"sass-loader": "^14.2.0",
"sass": "^1.77.4",
"sass-loader": "^14.2.1",
"sortablejs": "^1.15.2",
"style-loader": "^4.0.0",
"terser-webpack-plugin": "^5.3.10",
"tippy.js": "^6.3.7",
Expand Down
30 changes: 12 additions & 18 deletions src/check.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {prefixSymbol} from "./tooltip/tooltip";
import {stashEndpoints} from "./settings/endpoints";
import {firstText, hasKanji} from "./utils";
import {CheckOptions, CustomRule, DataField, StashEndpoint, Target, Type} from "./dataTypes";
import {CheckOptions, CustomDisplayRule, DataField, DisplayOptions, StashEndpoint, Target, Type} from "./dataTypes";
import {request} from "./request";
import {booleanOptions, OptionKey} from "./settings/general";
import {onAddition} from "./observer";
import {customDisplayRules} from "./settings/display";

const supportedDataFields = new Map<Target, DataField[]>([
[Target.Scene, [DataField.Id, DataField.Title, DataField.Organized, DataField.Studio, DataField.Code, DataField.Date, DataField.Tags, DataField.Files]],
Expand Down Expand Up @@ -105,6 +106,7 @@ async function checkElement(
target: Target,
element: Element,
customFilter: string,
display: DisplayOptions,
{
displaySelector = (e: Element) => e,
urlSelector = (e: Element) => e.closest("a")?.href,
Expand All @@ -113,7 +115,6 @@ async function checkElement(
stashIdEndpoint = `https://${window.location.host}/graphql`,
nameSelector = firstText,
titleSelector = firstText,
color = "green",
}: CheckOptions
) {
let displayElement = displaySelector(element)
Expand All @@ -125,7 +126,7 @@ async function checkElement(
let url = urlSelector(element)
if (url) {
console.debug(`URL: ${url}`);
await queryStash(url, (...args) => prefixSymbol(displayElement!, ...args, color), target, Type.Url, customFilter, stashIdEndpoint);
await queryStash(url, (...args) => prefixSymbol(displayElement!, ...args, display), target, Type.Url, customFilter, stashIdEndpoint);
} else {
console.info(`No URL for ${target} found.`);
}
Expand All @@ -134,7 +135,7 @@ async function checkElement(
let code = codeSelector(element);
if (code) {
console.debug(`Code: ${code}`);
await queryStash(code, (...args) => prefixSymbol(displayElement!, ...args, color), target, Type.Code, customFilter, stashIdEndpoint);
await queryStash(code, (...args) => prefixSymbol(displayElement!, ...args, display), target, Type.Code, customFilter, stashIdEndpoint);
} else {
console.info(`No Code for ${target} found.`);
}
Expand All @@ -143,7 +144,7 @@ async function checkElement(
let id = stashIdSelector(element);
if (id) {
console.debug(`StashId: ${id}`);
await queryStash(id, (...args) => prefixSymbol(displayElement!, ...args, color), target, Type.StashId, customFilter, stashIdEndpoint);
await queryStash(id, (...args) => prefixSymbol(displayElement!, ...args, display), target, Type.StashId, customFilter, stashIdEndpoint);
} else {
console.info(`No StashId for ${target} found.`);
}
Expand All @@ -156,7 +157,7 @@ async function checkElement(
let ignore = target === Target.Performer && nameCount === 1 && !kanji
if (name && !ignore) {
console.debug(`Name: ${name}`);
await queryStash(name, (...args) => prefixSymbol(displayElement!, ...args, color), target, Type.Name, customFilter, stashIdEndpoint);
await queryStash(name, (...args) => prefixSymbol(displayElement!, ...args, display), target, Type.Name, customFilter, stashIdEndpoint);
} else if (name && ignore) {
console.info(`Ignore single name: ${name}`)
} else {
Expand All @@ -167,22 +168,15 @@ async function checkElement(
let title = titleSelector(element);
if (title) {
console.debug(`Title: ${title}`);
await queryStash(title, (...args) => prefixSymbol(displayElement!, ...args, color), target, Type.Title, customFilter, stashIdEndpoint);
await queryStash(title, (...args) => prefixSymbol(displayElement!, ...args, display), target, Type.Title, customFilter, stashIdEndpoint);
} else {
console.info(`No Title for ${target} found.`);
}
}
}

// TODO: settings with a site pattern/regex (powerful and easy to verify - isActive indicator)
// TODO: escape inputs
const customRulesMap: Map<Target, CustomRule[]> = new Map([
[Target.Scene, [{filter: "organized:true", color: "purple"}, {filter: "file_count:{value:1,modifier:GREATER_THAN}", color: "brown"}]],
[Target.Studio, [{filter: "scene_count:{value:5,modifier:GREATER_THAN}", color: "purple"}]]
]);

function getCustomRules(target: Target): CustomRule[] {
return []//customRulesMap.get(target) ?? []
function getCustomRules(target: Target): CustomDisplayRule[] {
return customDisplayRules.get(target) ?? []
}

/**
Expand Down Expand Up @@ -216,12 +210,12 @@ function checkWithCustomRules(
for (let i = 0; i < customRules.length; i++) {
let rule = customRules[i]
let notFilters = customRules.slice(0, i).map(rule => rule.filter)
void checkElement(target, element, combineFilters([rule.filter], notFilters), {color: rule.color, ...checkConfig})
void checkElement(target, element, combineFilters([rule.filter], notFilters), rule.display, checkConfig)
}
// default excluding all rules
let notFilters = customRules.map(rule => rule.filter)
console.log("default")
void checkElement(target, element, combineFilters([], notFilters), checkConfig)
void checkElement(target, element, combineFilters([], notFilters), {color: "green"}, checkConfig)
}

/**
Expand Down
12 changes: 8 additions & 4 deletions src/dataTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export type StashEntry = {
} & {
queries: StashQuery[];
endpoint: string;
color: string;
display: DisplayOptions;
};

export type StashFile = {
Expand All @@ -41,11 +41,16 @@ export interface GraphQlQuery {
reject?: (message?: string) => void,
}

export interface CustomRule {
filter: string,
export interface DisplayOptions {
color: string,
}

export interface CustomDisplayRule {
pattern: string,
filter: string,
display: DisplayOptions,
}

/**
* Possible fields and subfields of the returned data
*/
Expand Down Expand Up @@ -130,6 +135,5 @@ export interface CheckOptions {
stashIdEndpoint?: string;
nameSelector?: Selector | null;
titleSelector?: Selector | null;
color?: string;
observe?: boolean;
}
4 changes: 3 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import {initMenu} from "./settings/menu";
import {initGeneralSettings} from "./settings/general";
import {runStashChecker} from "./stashChecker";
import {initStatistics} from "./settings/statistics";
import {initDisplaySettings} from "./settings/display";

(async function () {
initSettingsWindow();
initStatistics();
initGeneralSettings();
initDisplaySettings();
await initEndpointSettings();
await initMenu();

runStashChecker();
await runStashChecker();
})();
89 changes: 89 additions & 0 deletions src/settings/display.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import {newSettingsSection} from "./settings";
import {CustomDisplayRule, readable, Target} from "../dataTypes";
import {getValue, StorageKey} from "./storage";
import {OptionKey, stringOptions} from "./general";
import Sortable from 'sortablejs';
import {moveIndex} from "../utils";

// TODO: settings with a site pattern/regex (powerful and easy to verify - isActive indicator)
// TODO: escape input examples in description
const customRulesMap: Map<Target, CustomDisplayRule[]> = new Map([
[Target.Scene, [{pattern: "1", filter: "organized:true", display: {color: "purple"}}, {pattern: "2", filter: "organized:true", display: {color: "purple"}}, {pattern: "3", filter: "organized:true", display: {color: "purple"}}, {pattern: "4", filter: "organized:true", display: {color: "purple"}}, {pattern: "5", filter: "file_count:{value:1,modifier:GREATER_THAN}", display: {color: "brown"}}]],
[Target.Studio, [{pattern: "*", filter: "scene_count:{value:5,modifier:GREATER_THAN}", display: {color: "purple"}}]]
]);

export const customDisplayRules: Map<Target, CustomDisplayRule[]> = await getValue<Map<Target, CustomDisplayRule[]>>(StorageKey.CustomDisplayRules, customRulesMap);

export function initDisplaySettings() {
let description = "Custom display rules can change the display of check marks. " +
"A rule applies when the URL pattern matches the current website and the GraphQL filter matches the element. " +
"Rules higher in the list have higher priority. " +
"The order can be changed by dragging. " +
"If no rule applies, the default display options are use. " +
"GraphQL filters may not contain AND/OR/NOT. " +
"Multiple filters can still be concatenated by ','."
let displaySection = newSettingsSection("display", "Display", description);
populateDisplaySection(displaySection);
}

// TODO: use tabs for targets?
// TODO: separate modal/tab for display settings
function populateDisplaySection(displaySection: HTMLElement) {
let table = document.createElement("table");
let tableHead = document.createElement("thead");
tableHead.append(tableHeadRow());
table.append(tableHead);

let tableBody = document.createElement("tbody");
table.append(tableBody);
let tableHeading = document.createElement("h2");
tableHeading.innerHTML = readable(Target.Scene)
displaySection.append(tableHeading);
displaySection.append(table);
Sortable.create(tableBody, {
onEnd: event => {
if (event.oldIndex && event.newIndex) {
let old = customDisplayRules.get(Target.Scene) ?? [];
customDisplayRules.set(Target.Scene, moveIndex(old, event.oldIndex, event.newIndex));
}
}
});
populateCustomRulesTable(tableBody, Target.Scene);
}

function populateCustomRulesTable(tableBody: HTMLTableSectionElement, target: Target) {
let tableRows = Array.from(customDisplayRules.get(target) ?? [])
.map(tableRow);
tableBody.replaceChildren(...tableRows);
}

function tableHeadRow(): HTMLTableRowElement {
let row = document.createElement("tr");
row.innerHTML = "<th>URL Pattern</th><th>GraphQL Filter</th><th>Color</th><th>Preview</th>";
return row;
}

function tableRow(customRule: CustomDisplayRule): HTMLTableRowElement {
let row = document.createElement("tr");
let preview = document.createElement("span");
preview.innerHTML = stringOptions.get(OptionKey.checkMark)!;
preview.classList.add("stashCheckerSymbol");
preview.style.color = customRule.display.color;
let previewElement = document.createElement("td");
previewElement.classList.add("center");
previewElement.append(preview)

row.append(
dataCell(customRule.pattern),
dataCell(customRule.filter),
dataCell(customRule.display.color),
previewElement,
);
return row;
}

function dataCell(innerHtml: string): HTMLTableCellElement {
let cell = document.createElement("td");
cell.innerHTML = innerHtml
return cell;
}
1 change: 1 addition & 0 deletions src/settings/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const defaultData: StashEndpoint[] = [{
url: "http://localhost:9999/graphql",
key: "",
}];

export const stashEndpoints: StashEndpoint[] = await getValue<StashEndpoint[]>(StorageKey.StashEndpoints, defaultData);

export async function initEndpointSettings() {
Expand Down
2 changes: 1 addition & 1 deletion src/settings/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ function closeSettingsWindow(this: HTMLElement, event: MouseEvent) {
this.style.display = "none";
clearObservers()
clearSymbols()
runStashChecker()
void runStashChecker()
}
}

Expand Down
Loading

0 comments on commit 13b176b

Please sign in to comment.