mirror of
https://github.com/cheeaun/phanpy.git
synced 2024-11-22 01:05:34 +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 './media-post.css';
|
||||||
|
|
||||||
import { memo } from 'preact/compat';
|
import { memo } from 'preact/compat';
|
||||||
|
import { useContext, useMemo } from 'preact/hooks';
|
||||||
import { useSnapshot } from 'valtio';
|
import { useSnapshot } from 'valtio';
|
||||||
|
|
||||||
|
import FilterContext from '../utils/filter-context';
|
||||||
|
import { isFiltered } from '../utils/filters';
|
||||||
import states, { statusKey } from '../utils/states';
|
import states, { statusKey } from '../utils/states';
|
||||||
|
import store from '../utils/store';
|
||||||
|
|
||||||
import Media from './media';
|
import Media from './media';
|
||||||
|
|
||||||
|
@ -13,7 +17,7 @@ function MediaPost({
|
||||||
status,
|
status,
|
||||||
instance,
|
instance,
|
||||||
parent,
|
parent,
|
||||||
allowFilters,
|
// allowFilters,
|
||||||
onMediaClick,
|
onMediaClick,
|
||||||
}) {
|
}) {
|
||||||
let sKey = statusKey(statusID, instance);
|
let sKey = statusKey(statusID, instance);
|
||||||
|
@ -68,7 +72,7 @@ function MediaPost({
|
||||||
// Non-API props
|
// Non-API props
|
||||||
_deleted,
|
_deleted,
|
||||||
_pinned,
|
_pinned,
|
||||||
_filtered,
|
// _filtered,
|
||||||
} = status;
|
} = status;
|
||||||
|
|
||||||
if (!mediaAttachments?.length) {
|
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);
|
console.debug('RENDER Media post', id, status?.account.displayName);
|
||||||
|
|
||||||
// const readingExpandSpoilers = useMemo(() => {
|
// const readingExpandSpoilers = useMemo(() => {
|
||||||
|
@ -95,6 +113,7 @@ function MediaPost({
|
||||||
|
|
||||||
return mediaAttachments.map((media, i) => {
|
return mediaAttachments.map((media, i) => {
|
||||||
const mediaKey = `${sKey}-${media.id}`;
|
const mediaKey = `${sKey}-${media.id}`;
|
||||||
|
const filterTitleStr = filterInfo?.titlesStr;
|
||||||
return (
|
return (
|
||||||
<Parent
|
<Parent
|
||||||
onMouseEnter={debugHover}
|
onMouseEnter={debugHover}
|
||||||
|
@ -102,10 +121,14 @@ function MediaPost({
|
||||||
data-spoiler-text={
|
data-spoiler-text={
|
||||||
spoilerText || (sensitive ? 'Sensitive media' : undefined)
|
spoilerText || (sensitive ? 'Sensitive media' : undefined)
|
||||||
}
|
}
|
||||||
data-filtered-text={_filtered ? 'Filtered' : undefined}
|
data-filtered-text={
|
||||||
|
filterInfo
|
||||||
|
? `Filtered${filterTitleStr ? `: ${filterTitleStr}` : ''}`
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
class={`
|
class={`
|
||||||
media-post
|
media-post
|
||||||
${allowFilters && _filtered ? 'filtered' : ''}
|
${filterInfo ? 'filtered' : ''}
|
||||||
${hasSpoiler ? 'has-spoiler' : ''}
|
${hasSpoiler ? 'has-spoiler' : ''}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
|
|
|
@ -13,6 +13,7 @@ import pThrottle from 'p-throttle';
|
||||||
import { memo } from 'preact/compat';
|
import { memo } from 'preact/compat';
|
||||||
import {
|
import {
|
||||||
useCallback,
|
useCallback,
|
||||||
|
useContext,
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
|
@ -34,6 +35,8 @@ import Poll from '../components/poll';
|
||||||
import { api } from '../utils/api';
|
import { api } from '../utils/api';
|
||||||
import emojifyText from '../utils/emojify-text';
|
import emojifyText from '../utils/emojify-text';
|
||||||
import enhanceContent from '../utils/enhance-content';
|
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 getTranslateTargetLanguage from '../utils/get-translate-target-language';
|
||||||
import getHTMLText from '../utils/getHTMLText';
|
import getHTMLText from '../utils/getHTMLText';
|
||||||
import handleContentLinks from '../utils/handle-content-links';
|
import handleContentLinks from '../utils/handle-content-links';
|
||||||
|
@ -90,7 +93,7 @@ function Status({
|
||||||
enableTranslate,
|
enableTranslate,
|
||||||
forceTranslate: _forceTranslate,
|
forceTranslate: _forceTranslate,
|
||||||
previewMode,
|
previewMode,
|
||||||
allowFilters,
|
// allowFilters,
|
||||||
onMediaClick,
|
onMediaClick,
|
||||||
quoted,
|
quoted,
|
||||||
onStatusLinkClick = () => {},
|
onStatusLinkClick = () => {},
|
||||||
|
@ -166,9 +169,24 @@ function Status({
|
||||||
// Non-API props
|
// Non-API props
|
||||||
_deleted,
|
_deleted,
|
||||||
_pinned,
|
_pinned,
|
||||||
_filtered,
|
// _filtered,
|
||||||
} = status;
|
} = 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);
|
console.debug('RENDER Status', id, status?.account.displayName, quoted);
|
||||||
|
|
||||||
const debugHover = (e) => {
|
const debugHover = (e) => {
|
||||||
|
@ -179,11 +197,11 @@ function Status({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (allowFilters && size !== 'l' && _filtered) {
|
if (/*allowFilters && */ size !== 'l' && filterInfo) {
|
||||||
return (
|
return (
|
||||||
<FilteredStatus
|
<FilteredStatus
|
||||||
status={status}
|
status={status}
|
||||||
filterInfo={_filtered}
|
filterInfo={filterInfo}
|
||||||
instance={instance}
|
instance={instance}
|
||||||
containerProps={{
|
containerProps={{
|
||||||
onMouseEnter: debugHover,
|
onMouseEnter: debugHover,
|
||||||
|
@ -195,13 +213,6 @@ function Status({
|
||||||
const createdAtDate = new Date(createdAt);
|
const createdAtDate = new Date(createdAt);
|
||||||
const editedAtDate = new Date(editedAt);
|
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(
|
let inReplyToAccountRef = mentions?.find(
|
||||||
(mention) => mention.id === inReplyToAccountId,
|
(mention) => mention.id === inReplyToAccountId,
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,6 +4,8 @@ import { InView } from 'react-intersection-observer';
|
||||||
import { useDebouncedCallback } from 'use-debounce';
|
import { useDebouncedCallback } from 'use-debounce';
|
||||||
import { useSnapshot } from 'valtio';
|
import { useSnapshot } from 'valtio';
|
||||||
|
|
||||||
|
import FilterContext from '../utils/filter-context';
|
||||||
|
import { isFiltered } from '../utils/filters';
|
||||||
import states, { statusKey } from '../utils/states';
|
import states, { statusKey } from '../utils/states';
|
||||||
import statusPeek from '../utils/status-peek';
|
import statusPeek from '../utils/status-peek';
|
||||||
import { groupBoosts, groupContext } from '../utils/timeline-utils';
|
import { groupBoosts, groupContext } from '../utils/timeline-utils';
|
||||||
|
@ -13,7 +15,6 @@ import useScroll from '../utils/useScroll';
|
||||||
|
|
||||||
import Icon from './icon';
|
import Icon from './icon';
|
||||||
import Link from './link';
|
import Link from './link';
|
||||||
import Media from './media';
|
|
||||||
import MediaPost from './media-post';
|
import MediaPost from './media-post';
|
||||||
import NavMenu from './nav-menu';
|
import NavMenu from './nav-menu';
|
||||||
import Status from './status';
|
import Status from './status';
|
||||||
|
@ -39,9 +40,10 @@ function Timeline({
|
||||||
headerStart,
|
headerStart,
|
||||||
headerEnd,
|
headerEnd,
|
||||||
timelineStart,
|
timelineStart,
|
||||||
allowFilters,
|
// allowFilters,
|
||||||
refresh,
|
refresh,
|
||||||
view,
|
view,
|
||||||
|
filterContext,
|
||||||
}) {
|
}) {
|
||||||
const snapStates = useSnapshot(states);
|
const snapStates = useSnapshot(states);
|
||||||
const [items, setItems] = useState([]);
|
const [items, setItems] = useState([]);
|
||||||
|
@ -285,172 +287,182 @@ function Timeline({
|
||||||
const hiddenUI = scrollDirection === 'end' && !nearReachStart;
|
const hiddenUI = scrollDirection === 'end' && !nearReachStart;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<FilterContext.Provider value={filterContext}>
|
||||||
id={`${id}-page`}
|
<div
|
||||||
class="deck-container"
|
id={`${id}-page`}
|
||||||
ref={(node) => {
|
class="deck-container"
|
||||||
scrollableRef.current = node;
|
ref={(node) => {
|
||||||
jRef.current = node;
|
scrollableRef.current = node;
|
||||||
kRef.current = node;
|
jRef.current = node;
|
||||||
oRef.current = node;
|
kRef.current = node;
|
||||||
}}
|
oRef.current = node;
|
||||||
tabIndex="-1"
|
}}
|
||||||
>
|
tabIndex="-1"
|
||||||
<div class="timeline-deck deck">
|
>
|
||||||
<header
|
<div class="timeline-deck deck">
|
||||||
hidden={hiddenUI}
|
<header
|
||||||
onClick={(e) => {
|
hidden={hiddenUI}
|
||||||
if (!e.target.closest('a, button')) {
|
onClick={(e) => {
|
||||||
scrollableRef.current?.scrollTo({
|
if (!e.target.closest('a, button')) {
|
||||||
top: 0,
|
scrollableRef.current?.scrollTo({
|
||||||
behavior: 'smooth',
|
top: 0,
|
||||||
});
|
behavior: 'smooth',
|
||||||
}
|
});
|
||||||
}}
|
}
|
||||||
onDblClick={(e) => {
|
}}
|
||||||
if (!e.target.closest('a, button')) {
|
onDblClick={(e) => {
|
||||||
loadItems(true);
|
if (!e.target.closest('a, button')) {
|
||||||
}
|
loadItems(true);
|
||||||
}}
|
}
|
||||||
class={uiState === 'loading' ? 'loading' : ''}
|
}}
|
||||||
>
|
class={uiState === 'loading' ? 'loading' : ''}
|
||||||
<div class="header-grid">
|
|
||||||
<div class="header-side">
|
|
||||||
<NavMenu />
|
|
||||||
{headerStart !== null && headerStart !== undefined ? (
|
|
||||||
headerStart
|
|
||||||
) : (
|
|
||||||
<Link to="/" class="button plain home-button">
|
|
||||||
<Icon icon="home" size="l" />
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{title && (titleComponent ? titleComponent : <h1>{title}</h1>)}
|
|
||||||
<div class="header-side">
|
|
||||||
{/* <Loader hidden={uiState !== 'loading'} /> */}
|
|
||||||
{!!headerEnd && headerEnd}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{items.length > 0 &&
|
|
||||||
uiState !== 'loading' &&
|
|
||||||
!hiddenUI &&
|
|
||||||
showNew && (
|
|
||||||
<button
|
|
||||||
class="updates-button shiny-pill"
|
|
||||||
type="button"
|
|
||||||
onClick={() => {
|
|
||||||
loadItems(true);
|
|
||||||
scrollableRef.current?.scrollTo({
|
|
||||||
top: 0,
|
|
||||||
behavior: 'smooth',
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon icon="arrow-up" /> New posts
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</header>
|
|
||||||
{!!timelineStart && (
|
|
||||||
<div
|
|
||||||
class={`timeline-start ${uiState === 'loading' ? 'loading' : ''}`}
|
|
||||||
>
|
>
|
||||||
{timelineStart}
|
<div class="header-grid">
|
||||||
</div>
|
<div class="header-side">
|
||||||
)}
|
<NavMenu />
|
||||||
{!!items.length ? (
|
{headerStart !== null && headerStart !== undefined ? (
|
||||||
<>
|
headerStart
|
||||||
<ul class={`timeline ${view ? `timeline-${view}` : ''}`}>
|
) : (
|
||||||
{items.map((status) => (
|
<Link to="/" class="button plain home-button">
|
||||||
<TimelineItem
|
<Icon icon="home" size="l" />
|
||||||
status={status}
|
</Link>
|
||||||
instance={instance}
|
)}
|
||||||
useItemID={useItemID}
|
</div>
|
||||||
allowFilters={allowFilters}
|
{title && (titleComponent ? titleComponent : <h1>{title}</h1>)}
|
||||||
key={status.id + status?._pinned}
|
<div class="header-side">
|
||||||
view={view}
|
{/* <Loader hidden={uiState !== 'loading'} /> */}
|
||||||
/>
|
{!!headerEnd && headerEnd}
|
||||||
))}
|
</div>
|
||||||
{showMore &&
|
</div>
|
||||||
uiState === 'loading' &&
|
{items.length > 0 &&
|
||||||
(view === 'media' ? null : (
|
uiState !== 'loading' &&
|
||||||
<>
|
!hiddenUI &&
|
||||||
<li
|
showNew && (
|
||||||
style={{
|
<button
|
||||||
height: '20vh',
|
class="updates-button shiny-pill"
|
||||||
}}
|
type="button"
|
||||||
>
|
onClick={() => {
|
||||||
<Status skeleton />
|
loadItems(true);
|
||||||
</li>
|
scrollableRef.current?.scrollTo({
|
||||||
<li
|
top: 0,
|
||||||
style={{
|
behavior: 'smooth',
|
||||||
height: '25vh',
|
});
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Status skeleton />
|
|
||||||
</li>
|
|
||||||
</>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
{uiState === 'default' &&
|
|
||||||
(showMore ? (
|
|
||||||
<InView
|
|
||||||
onChange={(inView) => {
|
|
||||||
if (inView) {
|
|
||||||
loadItems();
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<button
|
<Icon icon="arrow-up" /> New posts
|
||||||
type="button"
|
</button>
|
||||||
class="plain block"
|
)}
|
||||||
onClick={() => loadItems()}
|
</header>
|
||||||
style={{ marginBlockEnd: '6em' }}
|
{!!timelineStart && (
|
||||||
>
|
<div
|
||||||
Show more…
|
class={`timeline-start ${uiState === 'loading' ? 'loading' : ''}`}
|
||||||
</button>
|
|
||||||
</InView>
|
|
||||||
) : (
|
|
||||||
<p class="ui-state insignificant">The end.</p>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
) : uiState === 'loading' ? (
|
|
||||||
<ul class="timeline">
|
|
||||||
{Array.from({ length: 5 }).map((_, i) =>
|
|
||||||
view === 'media' ? (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
height: '50vh',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<li key={i}>
|
|
||||||
<Status skeleton />
|
|
||||||
</li>
|
|
||||||
),
|
|
||||||
)}
|
|
||||||
</ul>
|
|
||||||
) : (
|
|
||||||
uiState !== 'error' && <p class="ui-state">{emptyText}</p>
|
|
||||||
)}
|
|
||||||
{uiState === 'error' && (
|
|
||||||
<p class="ui-state">
|
|
||||||
{errorText}
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<button
|
|
||||||
class="button plain"
|
|
||||||
onClick={() => loadItems(!items.length)}
|
|
||||||
>
|
>
|
||||||
Try again
|
{timelineStart}
|
||||||
</button>
|
</div>
|
||||||
</p>
|
)}
|
||||||
)}
|
{!!items.length ? (
|
||||||
|
<>
|
||||||
|
<ul class={`timeline ${view ? `timeline-${view}` : ''}`}>
|
||||||
|
{items.map((status) => (
|
||||||
|
<TimelineItem
|
||||||
|
status={status}
|
||||||
|
instance={instance}
|
||||||
|
useItemID={useItemID}
|
||||||
|
// allowFilters={allowFilters}
|
||||||
|
filterContext={filterContext}
|
||||||
|
key={status.id + status?._pinned}
|
||||||
|
view={view}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{showMore &&
|
||||||
|
uiState === 'loading' &&
|
||||||
|
(view === 'media' ? null : (
|
||||||
|
<>
|
||||||
|
<li
|
||||||
|
style={{
|
||||||
|
height: '20vh',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Status skeleton />
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
style={{
|
||||||
|
height: '25vh',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Status skeleton />
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
{uiState === 'default' &&
|
||||||
|
(showMore ? (
|
||||||
|
<InView
|
||||||
|
onChange={(inView) => {
|
||||||
|
if (inView) {
|
||||||
|
loadItems();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="plain block"
|
||||||
|
onClick={() => loadItems()}
|
||||||
|
style={{ marginBlockEnd: '6em' }}
|
||||||
|
>
|
||||||
|
Show more…
|
||||||
|
</button>
|
||||||
|
</InView>
|
||||||
|
) : (
|
||||||
|
<p class="ui-state insignificant">The end.</p>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
) : uiState === 'loading' ? (
|
||||||
|
<ul class="timeline">
|
||||||
|
{Array.from({ length: 5 }).map((_, i) =>
|
||||||
|
view === 'media' ? (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: '50vh',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<li key={i}>
|
||||||
|
<Status skeleton />
|
||||||
|
</li>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
) : (
|
||||||
|
uiState !== 'error' && <p class="ui-state">{emptyText}</p>
|
||||||
|
)}
|
||||||
|
{uiState === 'error' && (
|
||||||
|
<p class="ui-state">
|
||||||
|
{errorText}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<button
|
||||||
|
class="button plain"
|
||||||
|
onClick={() => loadItems(!items.length)}
|
||||||
|
>
|
||||||
|
Try again
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</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 { id: statusID, reblog, items, type, _pinned } = status;
|
||||||
const actualStatusID = reblog?.id || statusID;
|
const actualStatusID = reblog?.id || statusID;
|
||||||
const url = instance
|
const url = instance
|
||||||
|
@ -467,10 +479,18 @@ function TimelineItem({ status, instance, useItemID, allowFilters, view }) {
|
||||||
if (isCarousel) {
|
if (isCarousel) {
|
||||||
// Here, we don't hide filtered posts, but we sort them last
|
// Here, we don't hide filtered posts, but we sort them last
|
||||||
items.sort((a, b) => {
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
if (!a._filtered && b._filtered) {
|
if (!aFiltered && bFiltered) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -493,7 +513,7 @@ function TimelineItem({ status, instance, useItemID, allowFilters, view }) {
|
||||||
instance={instance}
|
instance={instance}
|
||||||
size="s"
|
size="s"
|
||||||
contentTextWeight
|
contentTextWeight
|
||||||
allowFilters={allowFilters}
|
// allowFilters={allowFilters}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Status
|
<Status
|
||||||
|
@ -501,7 +521,7 @@ function TimelineItem({ status, instance, useItemID, allowFilters, view }) {
|
||||||
instance={instance}
|
instance={instance}
|
||||||
size="s"
|
size="s"
|
||||||
contentTextWeight
|
contentTextWeight
|
||||||
allowFilters={allowFilters}
|
// allowFilters={allowFilters}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -541,13 +561,13 @@ function TimelineItem({ status, instance, useItemID, allowFilters, view }) {
|
||||||
<Status
|
<Status
|
||||||
statusID={statusID}
|
statusID={statusID}
|
||||||
instance={instance}
|
instance={instance}
|
||||||
allowFilters={allowFilters}
|
// allowFilters={allowFilters}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Status
|
<Status
|
||||||
status={item}
|
status={item}
|
||||||
instance={instance}
|
instance={instance}
|
||||||
allowFilters={allowFilters}
|
// allowFilters={allowFilters}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -566,7 +586,7 @@ function TimelineItem({ status, instance, useItemID, allowFilters, view }) {
|
||||||
key={itemKey}
|
key={itemKey}
|
||||||
statusID={statusID}
|
statusID={statusID}
|
||||||
instance={instance}
|
instance={instance}
|
||||||
allowFilters={allowFilters}
|
// allowFilters={allowFilters}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<MediaPost
|
<MediaPost
|
||||||
|
@ -575,7 +595,7 @@ function TimelineItem({ status, instance, useItemID, allowFilters, view }) {
|
||||||
key={itemKey}
|
key={itemKey}
|
||||||
status={status}
|
status={status}
|
||||||
instance={instance}
|
instance={instance}
|
||||||
allowFilters={allowFilters}
|
// allowFilters={allowFilters}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -587,13 +607,13 @@ function TimelineItem({ status, instance, useItemID, allowFilters, view }) {
|
||||||
<Status
|
<Status
|
||||||
statusID={statusID}
|
statusID={statusID}
|
||||||
instance={instance}
|
instance={instance}
|
||||||
allowFilters={allowFilters}
|
// allowFilters={allowFilters}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Status
|
<Status
|
||||||
status={status}
|
status={status}
|
||||||
instance={instance}
|
instance={instance}
|
||||||
allowFilters={allowFilters}
|
// allowFilters={allowFilters}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
@ -32,7 +32,7 @@ function Following({ title, path, id, ...props }) {
|
||||||
console.log('First load', latestItem.current);
|
console.log('First load', latestItem.current);
|
||||||
}
|
}
|
||||||
|
|
||||||
value = filteredItems(value, 'home');
|
// value = filteredItems(value, 'home');
|
||||||
value.forEach((item) => {
|
value.forEach((item) => {
|
||||||
saveStatus(item, instance);
|
saveStatus(item, instance);
|
||||||
});
|
});
|
||||||
|
@ -115,7 +115,8 @@ function Following({ title, path, id, ...props }) {
|
||||||
useItemID
|
useItemID
|
||||||
boostsCarousel={snapStates.settings.boostsCarousel}
|
boostsCarousel={snapStates.settings.boostsCarousel}
|
||||||
{...props}
|
{...props}
|
||||||
allowFilters
|
// allowFilters
|
||||||
|
filterContext="home"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@ function Hashtags({ media: mediaView, columnMode, ...props }) {
|
||||||
latestItem.current = value[0].id;
|
latestItem.current = value[0].id;
|
||||||
}
|
}
|
||||||
|
|
||||||
value = filteredItems(value, 'public');
|
// value = filteredItems(value, 'public');
|
||||||
value.forEach((item) => {
|
value.forEach((item) => {
|
||||||
saveStatus(item, instance, {
|
saveStatus(item, instance, {
|
||||||
skipThreading: media, // If media view, no need to form threads
|
skipThreading: media, // If media view, no need to form threads
|
||||||
|
@ -153,7 +153,8 @@ function Hashtags({ media: mediaView, columnMode, ...props }) {
|
||||||
useItemID
|
useItemID
|
||||||
view={media ? 'media' : undefined}
|
view={media ? 'media' : undefined}
|
||||||
refresh={media}
|
refresh={media}
|
||||||
allowFilters
|
// allowFilters
|
||||||
|
filterContext="public"
|
||||||
headerEnd={
|
headerEnd={
|
||||||
<Menu2
|
<Menu2
|
||||||
portal
|
portal
|
||||||
|
|
|
@ -43,7 +43,7 @@ function List(props) {
|
||||||
latestItem.current = value[0].id;
|
latestItem.current = value[0].id;
|
||||||
}
|
}
|
||||||
|
|
||||||
value = filteredItems(value, 'home');
|
// value = filteredItems(value, 'home');
|
||||||
value.forEach((item) => {
|
value.forEach((item) => {
|
||||||
saveStatus(item, instance);
|
saveStatus(item, instance);
|
||||||
});
|
});
|
||||||
|
@ -102,7 +102,8 @@ function List(props) {
|
||||||
checkForUpdates={checkForUpdates}
|
checkForUpdates={checkForUpdates}
|
||||||
useItemID
|
useItemID
|
||||||
boostsCarousel={snapStates.settings.boostsCarousel}
|
boostsCarousel={snapStates.settings.boostsCarousel}
|
||||||
allowFilters
|
// allowFilters
|
||||||
|
filterContext="home"
|
||||||
// refresh={reloadCount}
|
// refresh={reloadCount}
|
||||||
headerStart={
|
headerStart={
|
||||||
<Link to="/l" class="button plain">
|
<Link to="/l" class="button plain">
|
||||||
|
|
|
@ -195,6 +195,7 @@ function Notifications({ columnMode }) {
|
||||||
snapStates.notificationsShowNew &&
|
snapStates.notificationsShowNew &&
|
||||||
uiState !== 'loading'
|
uiState !== 'loading'
|
||||||
) {
|
) {
|
||||||
|
setShowNew(false);
|
||||||
loadNotifications(true);
|
loadNotifications(true);
|
||||||
} else {
|
} else {
|
||||||
setShowNew(snapStates.notificationsShowNew);
|
setShowNew(snapStates.notificationsShowNew);
|
||||||
|
|
|
@ -41,7 +41,7 @@ function Public({ local, columnMode, ...props }) {
|
||||||
latestItem.current = value[0].id;
|
latestItem.current = value[0].id;
|
||||||
}
|
}
|
||||||
|
|
||||||
value = filteredItems(value, 'public');
|
// value = filteredItems(value, 'public');
|
||||||
value.forEach((item) => {
|
value.forEach((item) => {
|
||||||
saveStatus(item, instance);
|
saveStatus(item, instance);
|
||||||
});
|
});
|
||||||
|
@ -91,7 +91,8 @@ function Public({ local, columnMode, ...props }) {
|
||||||
useItemID
|
useItemID
|
||||||
headerStart={<></>}
|
headerStart={<></>}
|
||||||
boostsCarousel={snapStates.settings.boostsCarousel}
|
boostsCarousel={snapStates.settings.boostsCarousel}
|
||||||
allowFilters
|
// allowFilters
|
||||||
|
filterContext="public"
|
||||||
headerEnd={
|
headerEnd={
|
||||||
<Menu2
|
<Menu2
|
||||||
portal
|
portal
|
||||||
|
|
|
@ -85,7 +85,7 @@ function Trending({ columnMode, ...props }) {
|
||||||
latestItem.current = value[0].id;
|
latestItem.current = value[0].id;
|
||||||
}
|
}
|
||||||
|
|
||||||
value = filteredItems(value, 'public'); // Might not work here
|
// value = filteredItems(value, 'public'); // Might not work here
|
||||||
value.forEach((item) => {
|
value.forEach((item) => {
|
||||||
saveStatus(item, instance);
|
saveStatus(item, instance);
|
||||||
});
|
});
|
||||||
|
@ -257,7 +257,8 @@ function Trending({ columnMode, ...props }) {
|
||||||
useItemID
|
useItemID
|
||||||
headerStart={<></>}
|
headerStart={<></>}
|
||||||
boostsCarousel={snapStates.settings.boostsCarousel}
|
boostsCarousel={snapStates.settings.boostsCarousel}
|
||||||
allowFilters
|
// allowFilters
|
||||||
|
filterContext="public"
|
||||||
timelineStart={TimelineStart}
|
timelineStart={TimelineStart}
|
||||||
headerEnd={
|
headerEnd={
|
||||||
<Menu2
|
<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';
|
import store from './store';
|
||||||
|
|
||||||
export function filteredItem(item, filterContext, currentAccountID) {
|
function _isFiltered(filtered, filterContext) {
|
||||||
const { filtered } = item;
|
if (!filtered?.length) return false;
|
||||||
if (!filtered?.length) return true;
|
|
||||||
const isSelf = currentAccountID && item.account?.id === currentAccountID;
|
|
||||||
if (isSelf) return true;
|
|
||||||
const appliedFilters = filtered.filter((f) => {
|
const appliedFilters = filtered.filter((f) => {
|
||||||
const { filter } = f;
|
const { filter } = f;
|
||||||
const hasContext = filter.context.includes(filterContext);
|
const hasContext = filter.context.includes(filterContext);
|
||||||
|
@ -12,19 +10,35 @@ export function filteredItem(item, filterContext, currentAccountID) {
|
||||||
if (!filter.expiresAt) return hasContext;
|
if (!filter.expiresAt) return hasContext;
|
||||||
return new Date(filter.expiresAt) > new Date();
|
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');
|
const isHidden = appliedFilters.some((f) => f.filter.filterAction === 'hide');
|
||||||
console.log({ isHidden, filtered, appliedFilters, item });
|
if (isHidden)
|
||||||
if (isHidden) return false;
|
return {
|
||||||
|
action: 'hide',
|
||||||
|
};
|
||||||
const isWarn = appliedFilters.some((f) => f.filter.filterAction === 'warn');
|
const isWarn = appliedFilters.some((f) => f.filter.filterAction === 'warn');
|
||||||
if (isWarn) {
|
if (isWarn) {
|
||||||
const filterTitles = appliedFilters.map((f) => f.filter.title);
|
const filterTitles = appliedFilters.map((f) => f.filter.title);
|
||||||
item._filtered = {
|
return {
|
||||||
|
action: 'warn',
|
||||||
titles: filterTitles,
|
titles: filterTitles,
|
||||||
titlesStr: filterTitles.join(' • '),
|
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) {
|
export function filteredItems(items, filterContext) {
|
||||||
if (!items?.length) return [];
|
if (!items?.length) return [];
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import moize from 'moize';
|
import moize from 'moize';
|
||||||
|
|
||||||
|
window._moize = moize;
|
||||||
|
|
||||||
export default function mem(fn, opts = {}) {
|
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;
|
if (!override && oldStatus) return;
|
||||||
const key = statusKey(status.id, instance);
|
const key = statusKey(status.id, instance);
|
||||||
if (oldStatus?._pinned) status._pinned = oldStatus._pinned;
|
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;
|
states.statuses[key] = status;
|
||||||
if (status.reblog) {
|
if (status.reblog) {
|
||||||
const key = statusKey(status.reblog.id, instance);
|
const key = statusKey(status.reblog.id, instance);
|
||||||
|
|
Loading…
Reference in a new issue