mirror of
https://github.com/cheeaun/phanpy.git
synced 2025-02-16 15:21:48 +03:00
Breaking: rewrote filters implementation
This commit is contained in:
parent
1cdc4ebbe8
commit
0bc1b598c3
13 changed files with 287 additions and 207 deletions
|
@ -1,9 +1,13 @@
|
|||
import './media-post.css';
|
||||
|
||||
import { memo } from 'preact/compat';
|
||||
import { useContext, useMemo } from 'preact/hooks';
|
||||
import { useSnapshot } from 'valtio';
|
||||
|
||||
import FilterContext from '../utils/filter-context';
|
||||
import { isFiltered } from '../utils/filters';
|
||||
import states, { statusKey } from '../utils/states';
|
||||
import store from '../utils/store';
|
||||
|
||||
import Media from './media';
|
||||
|
||||
|
@ -13,7 +17,7 @@ function MediaPost({
|
|||
status,
|
||||
instance,
|
||||
parent,
|
||||
allowFilters,
|
||||
// allowFilters,
|
||||
onMediaClick,
|
||||
}) {
|
||||
let sKey = statusKey(statusID, instance);
|
||||
|
@ -68,7 +72,7 @@ function MediaPost({
|
|||
// Non-API props
|
||||
_deleted,
|
||||
_pinned,
|
||||
_filtered,
|
||||
// _filtered,
|
||||
} = status;
|
||||
|
||||
if (!mediaAttachments?.length) {
|
||||
|
@ -83,6 +87,20 @@ function MediaPost({
|
|||
}
|
||||
};
|
||||
|
||||
const currentAccount = useMemo(() => {
|
||||
return store.session.get('currentAccount');
|
||||
}, []);
|
||||
const isSelf = useMemo(() => {
|
||||
return currentAccount && currentAccount === accountId;
|
||||
}, [accountId, currentAccount]);
|
||||
|
||||
const filterContext = useContext(FilterContext);
|
||||
const filterInfo = !isSelf && isFiltered(filtered, filterContext);
|
||||
|
||||
if (filterInfo?.action === 'hide') {
|
||||
return null;
|
||||
}
|
||||
|
||||
console.debug('RENDER Media post', id, status?.account.displayName);
|
||||
|
||||
// const readingExpandSpoilers = useMemo(() => {
|
||||
|
@ -95,6 +113,7 @@ function MediaPost({
|
|||
|
||||
return mediaAttachments.map((media, i) => {
|
||||
const mediaKey = `${sKey}-${media.id}`;
|
||||
const filterTitleStr = filterInfo?.titlesStr;
|
||||
return (
|
||||
<Parent
|
||||
onMouseEnter={debugHover}
|
||||
|
@ -102,10 +121,14 @@ function MediaPost({
|
|||
data-spoiler-text={
|
||||
spoilerText || (sensitive ? 'Sensitive media' : undefined)
|
||||
}
|
||||
data-filtered-text={_filtered ? 'Filtered' : undefined}
|
||||
data-filtered-text={
|
||||
filterInfo
|
||||
? `Filtered${filterTitleStr ? `: ${filterTitleStr}` : ''}`
|
||||
: undefined
|
||||
}
|
||||
class={`
|
||||
media-post
|
||||
${allowFilters && _filtered ? 'filtered' : ''}
|
||||
${filterInfo ? 'filtered' : ''}
|
||||
${hasSpoiler ? 'has-spoiler' : ''}
|
||||
`}
|
||||
>
|
||||
|
|
|
@ -13,6 +13,7 @@ import pThrottle from 'p-throttle';
|
|||
import { memo } from 'preact/compat';
|
||||
import {
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
|
@ -34,6 +35,8 @@ import Poll from '../components/poll';
|
|||
import { api } from '../utils/api';
|
||||
import emojifyText from '../utils/emojify-text';
|
||||
import enhanceContent from '../utils/enhance-content';
|
||||
import FilterContext from '../utils/filter-context';
|
||||
import { isFiltered } from '../utils/filters';
|
||||
import getTranslateTargetLanguage from '../utils/get-translate-target-language';
|
||||
import getHTMLText from '../utils/getHTMLText';
|
||||
import handleContentLinks from '../utils/handle-content-links';
|
||||
|
@ -90,7 +93,7 @@ function Status({
|
|||
enableTranslate,
|
||||
forceTranslate: _forceTranslate,
|
||||
previewMode,
|
||||
allowFilters,
|
||||
// allowFilters,
|
||||
onMediaClick,
|
||||
quoted,
|
||||
onStatusLinkClick = () => {},
|
||||
|
@ -166,9 +169,24 @@ function Status({
|
|||
// Non-API props
|
||||
_deleted,
|
||||
_pinned,
|
||||
_filtered,
|
||||
// _filtered,
|
||||
} = status;
|
||||
|
||||
const currentAccount = useMemo(() => {
|
||||
return store.session.get('currentAccount');
|
||||
}, []);
|
||||
const isSelf = useMemo(() => {
|
||||
return currentAccount && currentAccount === accountId;
|
||||
}, [accountId, currentAccount]);
|
||||
|
||||
const filterContext = useContext(FilterContext);
|
||||
const filterInfo =
|
||||
!isSelf && !readOnly && !previewMode && isFiltered(filtered, filterContext);
|
||||
|
||||
if (filterInfo?.action === 'hide') {
|
||||
return null;
|
||||
}
|
||||
|
||||
console.debug('RENDER Status', id, status?.account.displayName, quoted);
|
||||
|
||||
const debugHover = (e) => {
|
||||
|
@ -179,11 +197,11 @@ function Status({
|
|||
}
|
||||
};
|
||||
|
||||
if (allowFilters && size !== 'l' && _filtered) {
|
||||
if (/*allowFilters && */ size !== 'l' && filterInfo) {
|
||||
return (
|
||||
<FilteredStatus
|
||||
status={status}
|
||||
filterInfo={_filtered}
|
||||
filterInfo={filterInfo}
|
||||
instance={instance}
|
||||
containerProps={{
|
||||
onMouseEnter: debugHover,
|
||||
|
@ -195,13 +213,6 @@ function Status({
|
|||
const createdAtDate = new Date(createdAt);
|
||||
const editedAtDate = new Date(editedAt);
|
||||
|
||||
const currentAccount = useMemo(() => {
|
||||
return store.session.get('currentAccount');
|
||||
}, []);
|
||||
const isSelf = useMemo(() => {
|
||||
return currentAccount && currentAccount === accountId;
|
||||
}, [accountId, currentAccount]);
|
||||
|
||||
let inReplyToAccountRef = mentions?.find(
|
||||
(mention) => mention.id === inReplyToAccountId,
|
||||
);
|
||||
|
|
|
@ -4,6 +4,8 @@ import { InView } from 'react-intersection-observer';
|
|||
import { useDebouncedCallback } from 'use-debounce';
|
||||
import { useSnapshot } from 'valtio';
|
||||
|
||||
import FilterContext from '../utils/filter-context';
|
||||
import { isFiltered } from '../utils/filters';
|
||||
import states, { statusKey } from '../utils/states';
|
||||
import statusPeek from '../utils/status-peek';
|
||||
import { groupBoosts, groupContext } from '../utils/timeline-utils';
|
||||
|
@ -13,7 +15,6 @@ import useScroll from '../utils/useScroll';
|
|||
|
||||
import Icon from './icon';
|
||||
import Link from './link';
|
||||
import Media from './media';
|
||||
import MediaPost from './media-post';
|
||||
import NavMenu from './nav-menu';
|
||||
import Status from './status';
|
||||
|
@ -39,9 +40,10 @@ function Timeline({
|
|||
headerStart,
|
||||
headerEnd,
|
||||
timelineStart,
|
||||
allowFilters,
|
||||
// allowFilters,
|
||||
refresh,
|
||||
view,
|
||||
filterContext,
|
||||
}) {
|
||||
const snapStates = useSnapshot(states);
|
||||
const [items, setItems] = useState([]);
|
||||
|
@ -285,6 +287,7 @@ function Timeline({
|
|||
const hiddenUI = scrollDirection === 'end' && !nearReachStart;
|
||||
|
||||
return (
|
||||
<FilterContext.Provider value={filterContext}>
|
||||
<div
|
||||
id={`${id}-page`}
|
||||
class="deck-container"
|
||||
|
@ -365,7 +368,8 @@ function Timeline({
|
|||
status={status}
|
||||
instance={instance}
|
||||
useItemID={useItemID}
|
||||
allowFilters={allowFilters}
|
||||
// allowFilters={allowFilters}
|
||||
filterContext={filterContext}
|
||||
key={status.id + status?._pinned}
|
||||
view={view}
|
||||
/>
|
||||
|
@ -447,10 +451,18 @@ function Timeline({
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
</FilterContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
function TimelineItem({ status, instance, useItemID, allowFilters, view }) {
|
||||
function TimelineItem({
|
||||
status,
|
||||
instance,
|
||||
useItemID,
|
||||
// allowFilters,
|
||||
filterContext,
|
||||
view,
|
||||
}) {
|
||||
const { id: statusID, reblog, items, type, _pinned } = status;
|
||||
const actualStatusID = reblog?.id || statusID;
|
||||
const url = instance
|
||||
|
@ -467,10 +479,18 @@ function TimelineItem({ status, instance, useItemID, allowFilters, view }) {
|
|||
if (isCarousel) {
|
||||
// Here, we don't hide filtered posts, but we sort them last
|
||||
items.sort((a, b) => {
|
||||
if (a._filtered && !b._filtered) {
|
||||
// if (a._filtered && !b._filtered) {
|
||||
// return 1;
|
||||
// }
|
||||
// if (!a._filtered && b._filtered) {
|
||||
// return -1;
|
||||
// }
|
||||
const aFiltered = isFiltered(a.filtered, filterContext);
|
||||
const bFiltered = isFiltered(b.filtered, filterContext);
|
||||
if (aFiltered && !bFiltered) {
|
||||
return 1;
|
||||
}
|
||||
if (!a._filtered && b._filtered) {
|
||||
if (!aFiltered && bFiltered) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
@ -493,7 +513,7 @@ function TimelineItem({ status, instance, useItemID, allowFilters, view }) {
|
|||
instance={instance}
|
||||
size="s"
|
||||
contentTextWeight
|
||||
allowFilters={allowFilters}
|
||||
// allowFilters={allowFilters}
|
||||
/>
|
||||
) : (
|
||||
<Status
|
||||
|
@ -501,7 +521,7 @@ function TimelineItem({ status, instance, useItemID, allowFilters, view }) {
|
|||
instance={instance}
|
||||
size="s"
|
||||
contentTextWeight
|
||||
allowFilters={allowFilters}
|
||||
// allowFilters={allowFilters}
|
||||
/>
|
||||
)}
|
||||
</Link>
|
||||
|
@ -541,13 +561,13 @@ function TimelineItem({ status, instance, useItemID, allowFilters, view }) {
|
|||
<Status
|
||||
statusID={statusID}
|
||||
instance={instance}
|
||||
allowFilters={allowFilters}
|
||||
// allowFilters={allowFilters}
|
||||
/>
|
||||
) : (
|
||||
<Status
|
||||
status={item}
|
||||
instance={instance}
|
||||
allowFilters={allowFilters}
|
||||
// allowFilters={allowFilters}
|
||||
/>
|
||||
)}
|
||||
</Link>
|
||||
|
@ -566,7 +586,7 @@ function TimelineItem({ status, instance, useItemID, allowFilters, view }) {
|
|||
key={itemKey}
|
||||
statusID={statusID}
|
||||
instance={instance}
|
||||
allowFilters={allowFilters}
|
||||
// allowFilters={allowFilters}
|
||||
/>
|
||||
) : (
|
||||
<MediaPost
|
||||
|
@ -575,7 +595,7 @@ function TimelineItem({ status, instance, useItemID, allowFilters, view }) {
|
|||
key={itemKey}
|
||||
status={status}
|
||||
instance={instance}
|
||||
allowFilters={allowFilters}
|
||||
// allowFilters={allowFilters}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -587,13 +607,13 @@ function TimelineItem({ status, instance, useItemID, allowFilters, view }) {
|
|||
<Status
|
||||
statusID={statusID}
|
||||
instance={instance}
|
||||
allowFilters={allowFilters}
|
||||
// allowFilters={allowFilters}
|
||||
/>
|
||||
) : (
|
||||
<Status
|
||||
status={status}
|
||||
instance={instance}
|
||||
allowFilters={allowFilters}
|
||||
// allowFilters={allowFilters}
|
||||
/>
|
||||
)}
|
||||
</Link>
|
||||
|
|
|
@ -32,7 +32,7 @@ function Following({ title, path, id, ...props }) {
|
|||
console.log('First load', latestItem.current);
|
||||
}
|
||||
|
||||
value = filteredItems(value, 'home');
|
||||
// value = filteredItems(value, 'home');
|
||||
value.forEach((item) => {
|
||||
saveStatus(item, instance);
|
||||
});
|
||||
|
@ -115,7 +115,8 @@ function Following({ title, path, id, ...props }) {
|
|||
useItemID
|
||||
boostsCarousel={snapStates.settings.boostsCarousel}
|
||||
{...props}
|
||||
allowFilters
|
||||
// allowFilters
|
||||
filterContext="home"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ function Hashtags({ media: mediaView, columnMode, ...props }) {
|
|||
latestItem.current = value[0].id;
|
||||
}
|
||||
|
||||
value = filteredItems(value, 'public');
|
||||
// value = filteredItems(value, 'public');
|
||||
value.forEach((item) => {
|
||||
saveStatus(item, instance, {
|
||||
skipThreading: media, // If media view, no need to form threads
|
||||
|
@ -153,7 +153,8 @@ function Hashtags({ media: mediaView, columnMode, ...props }) {
|
|||
useItemID
|
||||
view={media ? 'media' : undefined}
|
||||
refresh={media}
|
||||
allowFilters
|
||||
// allowFilters
|
||||
filterContext="public"
|
||||
headerEnd={
|
||||
<Menu2
|
||||
portal
|
||||
|
|
|
@ -43,7 +43,7 @@ function List(props) {
|
|||
latestItem.current = value[0].id;
|
||||
}
|
||||
|
||||
value = filteredItems(value, 'home');
|
||||
// value = filteredItems(value, 'home');
|
||||
value.forEach((item) => {
|
||||
saveStatus(item, instance);
|
||||
});
|
||||
|
@ -102,7 +102,8 @@ function List(props) {
|
|||
checkForUpdates={checkForUpdates}
|
||||
useItemID
|
||||
boostsCarousel={snapStates.settings.boostsCarousel}
|
||||
allowFilters
|
||||
// allowFilters
|
||||
filterContext="home"
|
||||
// refresh={reloadCount}
|
||||
headerStart={
|
||||
<Link to="/l" class="button plain">
|
||||
|
|
|
@ -195,6 +195,7 @@ function Notifications({ columnMode }) {
|
|||
snapStates.notificationsShowNew &&
|
||||
uiState !== 'loading'
|
||||
) {
|
||||
setShowNew(false);
|
||||
loadNotifications(true);
|
||||
} else {
|
||||
setShowNew(snapStates.notificationsShowNew);
|
||||
|
|
|
@ -41,7 +41,7 @@ function Public({ local, columnMode, ...props }) {
|
|||
latestItem.current = value[0].id;
|
||||
}
|
||||
|
||||
value = filteredItems(value, 'public');
|
||||
// value = filteredItems(value, 'public');
|
||||
value.forEach((item) => {
|
||||
saveStatus(item, instance);
|
||||
});
|
||||
|
@ -91,7 +91,8 @@ function Public({ local, columnMode, ...props }) {
|
|||
useItemID
|
||||
headerStart={<></>}
|
||||
boostsCarousel={snapStates.settings.boostsCarousel}
|
||||
allowFilters
|
||||
// allowFilters
|
||||
filterContext="public"
|
||||
headerEnd={
|
||||
<Menu2
|
||||
portal
|
||||
|
|
|
@ -85,7 +85,7 @@ function Trending({ columnMode, ...props }) {
|
|||
latestItem.current = value[0].id;
|
||||
}
|
||||
|
||||
value = filteredItems(value, 'public'); // Might not work here
|
||||
// value = filteredItems(value, 'public'); // Might not work here
|
||||
value.forEach((item) => {
|
||||
saveStatus(item, instance);
|
||||
});
|
||||
|
@ -257,7 +257,8 @@ function Trending({ columnMode, ...props }) {
|
|||
useItemID
|
||||
headerStart={<></>}
|
||||
boostsCarousel={snapStates.settings.boostsCarousel}
|
||||
allowFilters
|
||||
// allowFilters
|
||||
filterContext="public"
|
||||
timelineStart={TimelineStart}
|
||||
headerEnd={
|
||||
<Menu2
|
||||
|
|
4
src/utils/filter-context.js
Normal file
4
src/utils/filter-context.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
import { createContext } from 'preact';
|
||||
|
||||
const FilterContext = createContext();
|
||||
export default FilterContext;
|
|
@ -1,10 +1,8 @@
|
|||
import mem from './mem';
|
||||
import store from './store';
|
||||
|
||||
export function filteredItem(item, filterContext, currentAccountID) {
|
||||
const { filtered } = item;
|
||||
if (!filtered?.length) return true;
|
||||
const isSelf = currentAccountID && item.account?.id === currentAccountID;
|
||||
if (isSelf) return true;
|
||||
function _isFiltered(filtered, filterContext) {
|
||||
if (!filtered?.length) return false;
|
||||
const appliedFilters = filtered.filter((f) => {
|
||||
const { filter } = f;
|
||||
const hasContext = filter.context.includes(filterContext);
|
||||
|
@ -12,19 +10,35 @@ export function filteredItem(item, filterContext, currentAccountID) {
|
|||
if (!filter.expiresAt) return hasContext;
|
||||
return new Date(filter.expiresAt) > new Date();
|
||||
});
|
||||
if (!appliedFilters.length) return true;
|
||||
if (!appliedFilters.length) return false;
|
||||
const isHidden = appliedFilters.some((f) => f.filter.filterAction === 'hide');
|
||||
console.log({ isHidden, filtered, appliedFilters, item });
|
||||
if (isHidden) return false;
|
||||
if (isHidden)
|
||||
return {
|
||||
action: 'hide',
|
||||
};
|
||||
const isWarn = appliedFilters.some((f) => f.filter.filterAction === 'warn');
|
||||
if (isWarn) {
|
||||
const filterTitles = appliedFilters.map((f) => f.filter.title);
|
||||
item._filtered = {
|
||||
return {
|
||||
action: 'warn',
|
||||
titles: filterTitles,
|
||||
titlesStr: filterTitles.join(' • '),
|
||||
};
|
||||
}
|
||||
return isWarn;
|
||||
return false;
|
||||
}
|
||||
export const isFiltered = mem(_isFiltered);
|
||||
|
||||
export function filteredItem(item, filterContext, currentAccountID) {
|
||||
const { filtered } = item;
|
||||
if (!filtered?.length) return true;
|
||||
const isSelf = currentAccountID && item.account?.id === currentAccountID;
|
||||
if (isSelf) return true;
|
||||
const filterState = isFiltered(filtered, filterContext);
|
||||
if (!filterState) return true;
|
||||
if (filterState.action === 'hide') return false;
|
||||
// item._filtered = filterState;
|
||||
return true;
|
||||
}
|
||||
export function filteredItems(items, filterContext) {
|
||||
if (!items?.length) return [];
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import moize from 'moize';
|
||||
|
||||
window._moize = moize;
|
||||
|
||||
export default function mem(fn, opts = {}) {
|
||||
return moize(fn, { ...opts, maxSize: 100 });
|
||||
return moize(fn, { ...opts, maxSize: 100, isDeepEqual: true });
|
||||
}
|
||||
|
|
|
@ -168,7 +168,7 @@ export function saveStatus(status, instance, opts) {
|
|||
if (!override && oldStatus) return;
|
||||
const key = statusKey(status.id, instance);
|
||||
if (oldStatus?._pinned) status._pinned = oldStatus._pinned;
|
||||
if (oldStatus?._filtered) status._filtered = oldStatus._filtered;
|
||||
// if (oldStatus?._filtered) status._filtered = oldStatus._filtered;
|
||||
states.statuses[key] = status;
|
||||
if (status.reblog) {
|
||||
const key = statusKey(status.reblog.id, instance);
|
||||
|
|
Loading…
Add table
Reference in a new issue