import {Component} from "preact"; import { createValueHideTopicPredicate, fromStorage, isHideTopicMatcher, Feature, HideTopicMatcher, type HideTopicPredicate, type HideTopicsData, } from "../../storage/exports.js"; import {log} from "../../utilities/logging.js"; import {Setting, type SettingProps} from "./index.js"; type State = { predicates: HideTopicsData; predicatesToRemove: HideTopicsData; unsavedPredicateIds: number[]; }; export class HideTopicsSetting extends Component { constructor(props: SettingProps) { super(props); this.state = { predicates: [], predicatesToRemove: [], unsavedPredicateIds: [], }; } async componentDidMount() { this.setState({ predicates: await fromStorage(Feature.HideTopics), }); } newPredicate = async () => { const {predicates, unsavedPredicateIds} = this.state; predicates.sort((a, b) => b.value.id - a.value.id); const newId = (predicates[0]?.value.id ?? 0) + 1; predicates.push( await createValueHideTopicPredicate({ id: newId, matcher: HideTopicMatcher.DomainIncludes, value: "example.org", }), ); unsavedPredicateIds.push(newId); this.setState({ predicates, unsavedPredicateIds, }); }; onInput = (event: Event, id: number, key: keyof HideTopicPredicate) => { const {predicates, unsavedPredicateIds} = this.state; const index = predicates.findIndex(({value}) => value.id === id); if (index === -1) { log(`Tried to edit unknown predicate with ID: ${id}`); return; } const newValue = (event.target as HTMLInputElement)!.value; switch (key) { case "matcher": { if (isHideTopicMatcher(newValue)) { predicates[index].value.matcher = newValue; } else { log(`Unknown HideTopicMatcher: ${newValue}`, true); return; } break; } case "value": { predicates[index].value.value = newValue; break; } default: { log(`Can't edit predicate key: ${key}`, true); return; } } unsavedPredicateIds.push(id); this.setState({predicates, unsavedPredicateIds}); }; remove = (id: number) => { const {predicates, predicatesToRemove, unsavedPredicateIds} = this.state; const index = predicates.findIndex(({value}) => value.id === id); if (index === -1) { log(`Tried to remove unknown predicate with ID: ${id}`); return; } predicatesToRemove.push(...predicates.splice(index, 1)); unsavedPredicateIds.push(id); this.setState({predicates, predicatesToRemove, unsavedPredicateIds}); }; save = async () => { const {predicates, predicatesToRemove} = this.state; for (const predicate of predicates) { await predicate.save(); } for (const predicate of predicatesToRemove) { await predicate.remove(); } this.setState({predicatesToRemove: [], unsavedPredicateIds: []}); }; render() { const {predicates, unsavedPredicateIds} = this.state; predicates.sort((a, b) => a.value.id - b.value.id); const editors = predicates.map(({value: predicate}) => { const matcherHandler = (event: Event) => { this.onInput(event, predicate.id, "matcher"); }; const valueHandler = (event: Event) => { this.onInput(event, predicate.id, "value"); }; const removeHandler = () => { this.remove(predicate.id); }; const matcherOptions = Object.values(HideTopicMatcher).map((key) => ( )); const hasUnsavedChanges = unsavedPredicateIds.includes(predicate.id) ? "unsaved-changes" : ""; const disableValueInput = [HideTopicMatcher.VotedOnTopic].includes( predicate.matcher, ); return (
); }); const hasUnsavedChanges = unsavedPredicateIds.length > 0; return (

Hide topics from the topic listing matching custom predicates.
Topics will be hidden when any of your predicates match and you can unhide them by clicking a button that will appear at the bottom of the sidebar. The topics will then show themselves and have a red border.
For hiding topics with certain tags, Tildes can do that natively via your{" "} filtered tags settings .

Matcher explanations

All matches are done without taking casing into account, so you don't have to care about upper or lowercase letters.

  • Domain Includes will match the domain of all topic links (including text topics). For example with the link{" "} https://tildes.net/~tildes, "tildes.net" is what will be matched against. If your value is included in the domain anywhere the topic will be hidden.
  • Tildes Username Equals will match the topic author's username. If your value is exactly the same as the topic author's the topic will be hidden.
  • Title Includes will match the topic title. If your value is anywhere in the title the topic will be hidden.
  • User Label Equals will match any user labels you have applied to the topic author. For example if you set a "Hide Topics" user labels matcher and then add a user label to someone with "Hide Topics" as the text, their topics will be hidden.
  • Voted On Topic will match any topic that you have voted on and hide it. You do not have to put in a value for this matcher.
{editors}
); } }