Add the Anonymize Usernames feature (#5).
This commit is contained in:
parent
52696fe50e
commit
75006ff7eb
|
@ -8,6 +8,7 @@ import {
|
|||
BackToTopFeature,
|
||||
JumpToNewCommentFeature,
|
||||
UserLabelsFeature,
|
||||
runAnonymizeUsernamesFeature,
|
||||
runHideVotesFeature,
|
||||
runMarkdownToolbarFeature,
|
||||
} from './scripts/exports.js';
|
||||
|
@ -36,6 +37,10 @@ async function initialize() {
|
|||
// Object to hold the active components we are going to render.
|
||||
const components: Record<string, TRXComponent | undefined> = {};
|
||||
|
||||
if (settings.features.anonymizeUsernames) {
|
||||
runAnonymizeUsernamesFeature();
|
||||
}
|
||||
|
||||
if (settings.features.autocomplete) {
|
||||
components.autocomplete = html`
|
||||
<${AutocompleteFeature} settings=${settings} />
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
import {html} from 'htm/preact';
|
||||
|
||||
import {Setting, SettingProps} from './index.js';
|
||||
|
||||
export function AnonymizeUsernamesSetting(props: SettingProps): TRXComponent {
|
||||
return html`
|
||||
<${Setting} ...${props}>
|
||||
<p class="info">
|
||||
Anonymizes usernames by replacing them with "Anonymous #".
|
||||
<br />
|
||||
Note that User Labels will still be applied to any usernames as normal.
|
||||
</p>
|
||||
<//>
|
||||
`;
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
export {AboutSetting} from './about.js';
|
||||
export {AnonymizeUsernamesSetting} from './anonymize-usernames.js';
|
||||
export {AutocompleteSetting} from './autocomplete.js';
|
||||
export {BackToTopSetting} from './back-to-top.js';
|
||||
export {HideVotesSetting} from './hide-votes.js';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import {
|
||||
AboutSetting,
|
||||
AnonymizeUsernamesSetting,
|
||||
AutocompleteSetting,
|
||||
BackToTopSetting,
|
||||
HideVotesSetting,
|
||||
|
@ -16,6 +17,12 @@ import {
|
|||
* * The component function should return the corresponding settings components.
|
||||
*/
|
||||
export const features = [
|
||||
{
|
||||
index: 0,
|
||||
key: 'anonymizeUsernames',
|
||||
value: 'Anonymize Usernames',
|
||||
component: () => AnonymizeUsernamesSetting,
|
||||
},
|
||||
{
|
||||
index: 0,
|
||||
key: 'autocomplete',
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
import {log, querySelectorAll} from '../utilities/exports.js';
|
||||
|
||||
export function runAnonymizeUsernamesFeature() {
|
||||
const observer = new window.MutationObserver(() => {
|
||||
observer.disconnect();
|
||||
anonymizeUsernames();
|
||||
startObserver();
|
||||
});
|
||||
|
||||
function startObserver() {
|
||||
observer.observe(document, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
}
|
||||
|
||||
const count = anonymizeUsernames();
|
||||
startObserver();
|
||||
|
||||
log(`Anonymize Usernames: Initialized for ${count} user links.`);
|
||||
}
|
||||
|
||||
function anonymizeUsernames(): number {
|
||||
const usernameElements = querySelectorAll<HTMLElement>(
|
||||
'.link-user:not(.trx-anonymized)',
|
||||
);
|
||||
const replacements = generateReplacements(usernameElements);
|
||||
|
||||
for (const element of usernameElements) {
|
||||
let username = usernameFromElement(element);
|
||||
const isMention = username.startsWith('@');
|
||||
if (isMention) {
|
||||
username = username.slice(1);
|
||||
}
|
||||
|
||||
const replacement = replacements[username];
|
||||
element.textContent = isMention ? `@${replacement}` : `${replacement}`;
|
||||
|
||||
element.classList.add('trx-anonymized');
|
||||
element.dataset.trxUsername = username;
|
||||
}
|
||||
|
||||
return usernameElements.length;
|
||||
}
|
||||
|
||||
function generateReplacements(elements: HTMLElement[]): Record<string, string> {
|
||||
const usernames = new Set(
|
||||
elements.map((element) => usernameFromElement(element).replace(/@/g, '')),
|
||||
);
|
||||
|
||||
const replacements: Record<string, string> = {};
|
||||
for (const [index, username] of Array.from(usernames).entries()) {
|
||||
replacements[username] = `Anonymous ${index}`;
|
||||
}
|
||||
|
||||
return replacements;
|
||||
}
|
||||
|
||||
function usernameFromElement(element: HTMLElement): string {
|
||||
return (element.textContent ?? '<unknown>').trim();
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
export * from './anonymize-usernames.js';
|
||||
export * from './autocomplete.js';
|
||||
export * from './back-to-top.js';
|
||||
export * from './hide-votes.js';
|
||||
|
|
|
@ -64,7 +64,7 @@ export class UserLabelsFeature extends Component<Props, State> {
|
|||
this.setState({hidden: true});
|
||||
};
|
||||
|
||||
addLabelsToUsernames = (elements: Element[], onlyID?: number): number => {
|
||||
addLabelsToUsernames = (elements: HTMLElement[], onlyID?: number): number => {
|
||||
const settings = this.props.settings;
|
||||
const inTopicListing = document.querySelector('.topic-listing') !== null;
|
||||
|
||||
|
@ -87,10 +87,14 @@ export class UserLabelsFeature extends Component<Props, State> {
|
|||
});
|
||||
|
||||
for (const element of elements) {
|
||||
const username: string = element
|
||||
let username: string = element
|
||||
.textContent!.replace(/@/g, '')
|
||||
.toLowerCase();
|
||||
|
||||
if (settings.features.anonymizeUsernames) {
|
||||
username = element.dataset.trxUsername ?? username;
|
||||
}
|
||||
|
||||
const userLabels = sortedLabels.filter(
|
||||
(value) =>
|
||||
value.username === username &&
|
||||
|
|
|
@ -60,6 +60,7 @@ export default class Settings {
|
|||
|
||||
public features: {
|
||||
[index: string]: boolean;
|
||||
anonymizeUsernames: boolean;
|
||||
autocomplete: boolean;
|
||||
backToTop: boolean;
|
||||
debug: boolean;
|
||||
|
@ -120,6 +121,7 @@ export default class Settings {
|
|||
};
|
||||
|
||||
this.features = {
|
||||
anonymizeUsernames: false,
|
||||
autocomplete: true,
|
||||
backToTop: true,
|
||||
debug: false,
|
||||
|
|
Loading…
Reference in New Issue