mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
feat(catalog filters): add filter by user address, display name or anonymous, mod role
This commit is contained in:
@@ -55,6 +55,18 @@ const FiltersProtip = () => {
|
|||||||
<code>idolm*ster</code> — this can match <em>"idolmaster"</em> or <em>"idolm@ster"</em>, etc…
|
<code>idolm*ster</code> — this can match <em>"idolmaster"</em> or <em>"idolm@ster"</em>, etc…
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<ul>
|
||||||
|
<strong>Filtering by display name or user ID</strong>
|
||||||
|
<li>
|
||||||
|
Prefix the pattern with <code>#</code> to search by <em>user ID</em>: <code>#GPPr4CQSRCt8</code>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Prefix the pattern with <code>##</code> to search by <em>name</em>: <code>##Anonymous</code>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
To filter by <em>role</em>: <code>#!#owner</code>, <code>#!#admin</code>, <code>#!#moderator</code> (or <code>#!#mod</code>)
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
<ul>
|
<ul>
|
||||||
<strong>It is also possible to filter by regular expression:</strong>
|
<strong>It is also possible to filter by regular expression:</strong>
|
||||||
<li>
|
<li>
|
||||||
@@ -75,6 +87,9 @@ const FiltersProtip = () => {
|
|||||||
<li>
|
<li>
|
||||||
<strong>On</strong> — enables or disables the filter.
|
<strong>On</strong> — enables or disables the filter.
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Color</strong> — highlights matched threads with the specified color.
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<strong>Hide</strong> — hides matched threads.
|
<strong>Hide</strong> — hides matched threads.
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Comment } from '@plebbit/plebbit-react-hooks';
|
import { Comment } from '@plebbit/plebbit-react-hooks';
|
||||||
|
import useSubplebbitsStore from '@plebbit/plebbit-react-hooks/dist/stores/subplebbits';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a text matches a pattern according to various pattern matching rules:
|
* Checks if a text matches a pattern according to various pattern matching rules:
|
||||||
@@ -93,7 +94,110 @@ export const matchesPattern = (text: string, pattern: string): boolean => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a comment matches a pattern in its title or content
|
* Checks if a user ID matches a pattern
|
||||||
|
*
|
||||||
|
* @param comment The comment to check
|
||||||
|
* @param pattern The pattern to match (without the # prefix)
|
||||||
|
* @returns True if the user ID matches the pattern, false otherwise
|
||||||
|
*/
|
||||||
|
export const userIdMatchesPattern = (comment: Comment, pattern: string): boolean => {
|
||||||
|
if (!pattern || !comment?.author) return false;
|
||||||
|
|
||||||
|
// Check both address and shortAddress if available
|
||||||
|
const address = comment.author.address || '';
|
||||||
|
const shortAddress = comment.author.shortAddress || '';
|
||||||
|
|
||||||
|
return address.includes(pattern) || shortAddress.includes(pattern);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a display name matches a pattern
|
||||||
|
*
|
||||||
|
* @param comment The comment to check
|
||||||
|
* @param pattern The pattern to match (without the ## prefix)
|
||||||
|
* @returns True if the display name matches the pattern, false otherwise
|
||||||
|
*/
|
||||||
|
export const displayNameMatchesPattern = (comment: Comment, pattern: string): boolean => {
|
||||||
|
if (!pattern) return false;
|
||||||
|
|
||||||
|
// Special case for "Anonymous" to match undefined display names
|
||||||
|
if (pattern.toLowerCase() === 'anonymous') {
|
||||||
|
return comment?.author?.displayName === undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!comment?.author?.displayName) return false;
|
||||||
|
|
||||||
|
return comment.author.displayName.toLowerCase().includes(pattern.toLowerCase());
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a user has a specific role
|
||||||
|
*
|
||||||
|
* @param comment The comment to check
|
||||||
|
* @param role The role to check for (without the #!# prefix)
|
||||||
|
* @returns True if the user has the specified role, false otherwise
|
||||||
|
*/
|
||||||
|
export const userHasRole = (comment: Comment, role: string): boolean => {
|
||||||
|
if (!role || !comment?.author?.address || !comment?.subplebbitAddress) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const subplebbits = useSubplebbitsStore.getState().subplebbits;
|
||||||
|
const subplebbit = subplebbits[comment.subplebbitAddress];
|
||||||
|
|
||||||
|
if (!subplebbit?.roles) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userRole = subplebbit.roles[comment.author.address]?.role;
|
||||||
|
|
||||||
|
// Handle different role names (moderator/mod)
|
||||||
|
if ((role.toLowerCase() === 'moderator' || role.toLowerCase() === 'mod') && userRole === 'moderator') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return userRole?.toLowerCase() === role.toLowerCase();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a pattern that may contain special filters and content filters
|
||||||
|
*
|
||||||
|
* @param pattern The pattern to parse
|
||||||
|
* @returns An object containing the parsed special filters and content filters
|
||||||
|
*/
|
||||||
|
export const parsePattern = (
|
||||||
|
pattern: string,
|
||||||
|
): {
|
||||||
|
specialFilters: { type: 'userId' | 'displayName' | 'role'; value: string }[];
|
||||||
|
contentFilter: string;
|
||||||
|
} => {
|
||||||
|
if (!pattern) return { specialFilters: [], contentFilter: '' };
|
||||||
|
|
||||||
|
const parts = pattern.split(' ');
|
||||||
|
const specialFilters: { type: 'userId' | 'displayName' | 'role'; value: string }[] = [];
|
||||||
|
const contentParts: string[] = [];
|
||||||
|
|
||||||
|
parts.forEach((part) => {
|
||||||
|
if (part.startsWith('#') && !part.startsWith('##') && !part.startsWith('#!#')) {
|
||||||
|
specialFilters.push({ type: 'userId', value: part.substring(1) });
|
||||||
|
} else if (part.startsWith('##')) {
|
||||||
|
specialFilters.push({ type: 'displayName', value: part.substring(2) });
|
||||||
|
} else if (part.startsWith('#!#')) {
|
||||||
|
specialFilters.push({ type: 'role', value: part.substring(3) });
|
||||||
|
} else {
|
||||||
|
contentParts.push(part);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
specialFilters,
|
||||||
|
contentFilter: contentParts.join(' '),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a comment matches a pattern in its title or content,
|
||||||
|
* or if it matches special filters for user ID, display name, or role
|
||||||
*
|
*
|
||||||
* @param comment The comment to check
|
* @param comment The comment to check
|
||||||
* @param pattern The pattern to match
|
* @param pattern The pattern to match
|
||||||
@@ -102,6 +206,53 @@ export const matchesPattern = (text: string, pattern: string): boolean => {
|
|||||||
export const commentMatchesPattern = (comment: Comment, pattern: string): boolean => {
|
export const commentMatchesPattern = (comment: Comment, pattern: string): boolean => {
|
||||||
if (!pattern || !comment) return false;
|
if (!pattern || !comment) return false;
|
||||||
|
|
||||||
|
// Check if the pattern contains spaces, which might indicate combined filters
|
||||||
|
if (pattern.includes(' ') && (pattern.includes('#') || pattern.includes('##') || pattern.includes('#!#'))) {
|
||||||
|
// Parse the pattern into special filters and content filter
|
||||||
|
const { specialFilters, contentFilter } = parsePattern(pattern);
|
||||||
|
|
||||||
|
// If there are special filters, check if the comment matches all of them
|
||||||
|
if (specialFilters.length > 0) {
|
||||||
|
const allSpecialFiltersMatch = specialFilters.every((filter) => {
|
||||||
|
if (filter.type === 'userId') {
|
||||||
|
return userIdMatchesPattern(comment, filter.value);
|
||||||
|
} else if (filter.type === 'displayName') {
|
||||||
|
return displayNameMatchesPattern(comment, filter.value);
|
||||||
|
} else if (filter.type === 'role') {
|
||||||
|
return userHasRole(comment, filter.value);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// If there's also a content filter, check if the comment matches it as well
|
||||||
|
if (contentFilter) {
|
||||||
|
return allSpecialFiltersMatch && matchesPattern((comment?.title || '') + ' ' + (comment?.content || ''), contentFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
return allSpecialFiltersMatch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple cases for single filters
|
||||||
|
// Check for user ID filter (starts with #)
|
||||||
|
if (pattern.startsWith('#') && !pattern.startsWith('##') && !pattern.startsWith('#!#')) {
|
||||||
|
const userIdPattern = pattern.substring(1);
|
||||||
|
return userIdMatchesPattern(comment, userIdPattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for display name filter (starts with ##)
|
||||||
|
if (pattern.startsWith('##')) {
|
||||||
|
const displayNamePattern = pattern.substring(2);
|
||||||
|
return displayNameMatchesPattern(comment, displayNamePattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for role filter (starts with #!#)
|
||||||
|
if (pattern.startsWith('#!#')) {
|
||||||
|
const rolePattern = pattern.substring(3);
|
||||||
|
return userHasRole(comment, rolePattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regular content matching
|
||||||
const titleLower = comment?.title?.toLowerCase() || '';
|
const titleLower = comment?.title?.toLowerCase() || '';
|
||||||
const contentLower = comment?.content?.toLowerCase() || '';
|
const contentLower = comment?.content?.toLowerCase() || '';
|
||||||
const textToMatch = titleLower + ' ' + contentLower;
|
const textToMatch = titleLower + ' ' + contentLower;
|
||||||
|
|||||||
Reference in New Issue
Block a user