Initial work on 'blur' filter for posts

pull/1120/head
Lim Chee Aun 2025-04-09 08:26:09 +08:00
rodzic 75a35b7e27
commit ed41d4c22e
6 zmienionych plików z 89 dodań i 21 usunięć

Wyświetl plik

@ -653,6 +653,7 @@
display: flex; display: flex;
gap: 4px; gap: 4px;
align-items: center; align-items: center;
text-align: start;
} }
.status .content-container.has-spoiler:not(.show-spoiler) .spoiler-button { .status .content-container.has-spoiler:not(.show-spoiler) .spoiler-button {
~ *:not( ~ *:not(
@ -1374,7 +1375,8 @@ body:has(#modal-container .carousel) .status .media img:hover {
width: 100%; width: 100%;
height: 100%; height: 100%;
min-height: var(--min-dimension); min-height: var(--min-dimension);
background-image: radial-gradient( background-image:
radial-gradient(
circle at center center, circle at center center,
transparent, transparent,
var(--bg-faded-color) var(--bg-faded-color)

Wyświetl plik

@ -54,6 +54,7 @@ import htmlContentLength from '../utils/html-content-length';
import isRTL from '../utils/is-rtl'; import isRTL from '../utils/is-rtl';
import isMastodonLinkMaybe from '../utils/isMastodonLinkMaybe'; import isMastodonLinkMaybe from '../utils/isMastodonLinkMaybe';
import localeMatch from '../utils/locale-match'; import localeMatch from '../utils/locale-match';
import mem from '../utils/mem';
import niceDateTime from '../utils/nice-date-time'; import niceDateTime from '../utils/nice-date-time';
import openCompose from '../utils/open-compose'; import openCompose from '../utils/open-compose';
import pmem from '../utils/pmem'; import pmem from '../utils/pmem';
@ -317,6 +318,24 @@ const checkDifferentLanguage = (
return different; return different;
}; };
const getCurrentAccID = mem(
() => {
return getCurrentAccountID();
},
{
maxAge: 60 * 1000, // 1 minute
},
);
const getPrefs = mem(
() => {
return store.account.get('preferences') || {};
},
{
maxAge: 60 * 1000, // 1 minute
},
);
function Status({ function Status({
statusID, statusID,
status, status,
@ -330,7 +349,7 @@ function Status({
enableTranslate, enableTranslate,
forceTranslate: _forceTranslate, forceTranslate: _forceTranslate,
previewMode, previewMode,
// allowFilters, allowFilters,
onMediaClick, onMediaClick,
quoted, quoted,
onStatusLinkClick = () => {}, onStatusLinkClick = () => {},
@ -447,16 +466,16 @@ function Status({
const hasMediaAttachments = !!mediaAttachments?.length; const hasMediaAttachments = !!mediaAttachments?.length;
if (mediaFirst && hasMediaAttachments) size = 's'; if (mediaFirst && hasMediaAttachments) size = 's';
const currentAccount = useMemo(() => { const currentAccount = getCurrentAccID();
return getCurrentAccountID();
}, []);
const isSelf = useMemo(() => { const isSelf = useMemo(() => {
return currentAccount && currentAccount === accountId; return currentAccount && currentAccount === accountId;
}, [accountId, currentAccount]); }, [accountId, currentAccount]);
const filterContext = useContext(FilterContext); const filterContext = useContext(FilterContext);
const filterInfo = const filterInfo =
!isSelf && !readOnly && !previewMode && isFiltered(filtered, filterContext); !isSelf &&
((!readOnly && !previewMode) || allowFilters) &&
isFiltered(filtered, filterContext);
if (filterInfo?.action === 'hide') { if (filterInfo?.action === 'hide') {
return null; return null;
@ -472,7 +491,11 @@ function Status({
} }
}; };
if (/*allowFilters && */ size !== 'l' && filterInfo) { if (
(allowFilters || size !== 'l') &&
filterInfo &&
filterInfo.action !== 'blur'
) {
return ( return (
<FilteredStatus <FilteredStatus
status={status} status={status}
@ -515,13 +538,13 @@ function Status({
mentions?.find((mention) => mention.id === currentAccount); mentions?.find((mention) => mention.id === currentAccount);
const readingExpandSpoilers = useMemo(() => { const readingExpandSpoilers = useMemo(() => {
const prefs = store.account.get('preferences') || {}; const prefs = getPrefs();
return !!prefs['reading:expand:spoilers']; return !!prefs['reading:expand:spoilers'];
}, []); }, []);
const readingExpandMedia = useMemo(() => { const readingExpandMedia = useMemo(() => {
// default | show_all | hide_all // default | show_all | hide_all
// Ignore hide_all because it means hide *ALL* media including non-sensitive ones // Ignore hide_all because it means hide *ALL* media including non-sensitive ones
const prefs = store.account.get('preferences') || {}; const prefs = getPrefs();
return prefs['reading:expand:media']?.toLowerCase() || 'default'; return prefs['reading:expand:media']?.toLowerCase() || 'default';
}, []); }, []);
// FOR TESTING: // FOR TESTING:
@ -2011,7 +2034,9 @@ function Status({
)} )}
<div <div
class={`content-container ${ class={`content-container ${
spoilerText || sensitive ? 'has-spoiler' : '' spoilerText || sensitive || filterInfo?.action === 'blur'
? 'has-spoiler'
: ''
} ${showSpoiler ? 'show-spoiler' : ''} ${ } ${showSpoiler ? 'show-spoiler' : ''} ${
showSpoilerMedia ? 'show-media' : '' showSpoilerMedia ? 'show-media' : ''
}`} }`}
@ -2184,7 +2209,7 @@ function Status({
/> />
)} )}
{!previewMode && {!previewMode &&
sensitive && (sensitive || filterInfo?.action === 'blur') &&
!!mediaAttachments.length && !!mediaAttachments.length &&
readingExpandMedia !== 'show_all' && ( readingExpandMedia !== 'show_all' && (
<button <button
@ -2206,7 +2231,15 @@ function Status({
<Icon <Icon
icon={showSpoilerMedia ? 'eye-open' : 'eye-close'} icon={showSpoilerMedia ? 'eye-open' : 'eye-close'}
/>{' '} />{' '}
<span>
{filterInfo?.action === 'blur' && (
<small>
<Trans>Filtered: {filterInfo?.titlesStr}</Trans>
<br />
</small>
)}
{showSpoilerMedia ? t`Show less` : t`Show media`} {showSpoilerMedia ? t`Show less` : t`Show media`}
</span>
</button> </button>
)} )}
{!!mediaAttachments.length && {!!mediaAttachments.length &&

Wyświetl plik

@ -608,8 +608,12 @@ const TimelineItem = memo(
// } // }
const aFiltered = isFiltered(a.filtered, filterContext); const aFiltered = isFiltered(a.filtered, filterContext);
const bFiltered = isFiltered(b.filtered, filterContext); const bFiltered = isFiltered(b.filtered, filterContext);
if (aFiltered) filteredItemsIDs.add(a.id); if (aFiltered && aFiltered?.action !== 'blur') {
if (bFiltered) filteredItemsIDs.add(b.id); filteredItemsIDs.add(a.id);
}
if (bFiltered && bFiltered?.action !== 'blur') {
filteredItemsIDs.add(b.id);
}
if (aFiltered && !bFiltered) { if (aFiltered && !bFiltered) {
return 1; return 1;
} }

Wyświetl plik

@ -309,7 +309,7 @@ function Catchup() {
original = 0; original = 0;
const links = {}; const links = {};
for (const post of posts) { for (const post of posts) {
if (post._filtered) { if (post._filtered && post._filtered?.action !== 'blur') {
filtered++; filtered++;
post.__FILTER = 'filtered'; post.__FILTER = 'filtered';
} else if (post.group) { } else if (post.group) {
@ -1711,7 +1711,7 @@ const PostLine = memo(
__BOOSTERS, __BOOSTERS,
} = post; } = post;
const isReplyTo = inReplyToId && inReplyToAccountId !== account.id; const isReplyTo = inReplyToId && inReplyToAccountId !== account.id;
const isFiltered = !!filterInfo; const isFiltered = !!filterInfo && filterInfo?.action !== 'blur';
const debugHover = (e) => { const debugHover = (e) => {
if (e.shiftKey) { if (e.shiftKey) {
@ -1853,7 +1853,9 @@ function PostPeek({ post, filterInfo }) {
return !!prefs['reading:expand:spoilers']; return !!prefs['reading:expand:spoilers'];
}, []); }, []);
// const readingExpandSpoilers = true; // const readingExpandSpoilers = true;
const showMedia = readingExpandSpoilers || (!spoilerText && !sensitive); const showMedia =
readingExpandSpoilers ||
(!spoilerText && !sensitive && filterInfo?.action !== 'blur');
const postText = content ? statusPeek(post) : ''; const postText = content ? statusPeek(post) : '';
const showPostContent = !spoilerText || readingExpandSpoilers; const showPostContent = !spoilerText || readingExpandSpoilers;
@ -1866,7 +1868,7 @@ function PostPeek({ post, filterInfo }) {
<span class="post-peek-tag post-peek-thread">Thread</span>{' '} <span class="post-peek-tag post-peek-thread">Thread</span>{' '}
</> </>
)} )}
{!!filterInfo ? ( {!!filterInfo && filterInfo?.action !== 'blur' ? (
<span class="post-peek-filtered"> <span class="post-peek-filtered">
{/* Filtered{filterInfo?.titlesStr ? `: ${filterInfo.titlesStr}` : ''} */} {/* Filtered{filterInfo?.titlesStr ? `: ${filterInfo.titlesStr}` : ''} */}
{filterInfo?.titlesStr {filterInfo?.titlesStr
@ -1918,7 +1920,7 @@ function PostPeek({ post, filterInfo }) {
</> </>
)} )}
</span> </span>
{!filterInfo && ( {(!filterInfo || filterInfo?.action === 'blur') && (
<span class="post-peek-post-content"> <span class="post-peek-post-content">
{!!poll && ( {!!poll && (
<span class="post-peek-tag post-peek-poll"> <span class="post-peek-tag post-peek-poll">

Wyświetl plik

@ -13,6 +13,7 @@ import NavMenu from '../components/nav-menu';
import RelativeTime from '../components/relative-time'; import RelativeTime from '../components/relative-time';
import { api } from '../utils/api'; import { api } from '../utils/api';
import i18nDuration from '../utils/i18n-duration'; import i18nDuration from '../utils/i18n-duration';
import { getAPIVersions } from '../utils/store-utils';
import useInterval from '../utils/useInterval'; import useInterval from '../utils/useInterval';
import useTitle from '../utils/useTitle'; import useTitle from '../utils/useTitle';
@ -525,12 +526,27 @@ function FiltersAddEdit({ filter, onClose }) {
<p> <p>
<Trans>Filtered post will be</Trans> <Trans>Filtered post will be</Trans>
<br /> <br />
{getAPIVersions()?.mastodon >= 5 && (
<label class="ib">
<input
type="radio"
name="filter_action"
value="blur"
defaultChecked={filterAction === 'blur'}
disabled={uiState === 'loading'}
/>{' '}
<Trans>obscured (media only)</Trans>
</label>
)}{' '}
<label class="ib"> <label class="ib">
<input <input
type="radio" type="radio"
name="filter_action" name="filter_action"
value="warn" value="warn"
defaultChecked={filterAction === 'warn' || !editMode} defaultChecked={
(filterAction !== 'hide' && filterAction !== 'blur') ||
!editMode
}
disabled={uiState === 'loading'} disabled={uiState === 'loading'}
/>{' '} />{' '}
<Trans>minimized</Trans> <Trans>minimized</Trans>

Wyświetl plik

@ -16,7 +16,18 @@ function _isFiltered(filtered, filterContext) {
return { return {
action: 'hide', action: 'hide',
}; };
const isWarn = appliedFilters.some((f) => f.filter.filterAction === 'warn'); const isBlur = appliedFilters.every((f) => f.filter.filterAction === 'blur');
if (isBlur) {
const filterTitles = appliedFilters.map((f) => f.filter.title);
return {
action: 'blur',
titles: filterTitles,
titlesStr: filterTitles.join(' • '),
};
}
// const isWarn = appliedFilters.some((f) => f.filter.filterAction === 'warn');
const isWarn = appliedFilters.some((f) => !!f.filter.filterAction);
// Re: spec; unknown values for filter_action should be treated as warn.
if (isWarn) { if (isWarn) {
const filterTitles = appliedFilters.map((f) => f.filter.title); const filterTitles = appliedFilters.map((f) => f.filter.title);
return { return {