Preliminary steps in adding filter bar

This commit is contained in:
Lim Chee Aun 2023-04-03 10:36:31 +08:00
parent 04b4101e55
commit c7f4087ed2
4 changed files with 135 additions and 10 deletions

View file

@ -1641,6 +1641,63 @@ ul.link-list li a .icon {
} }
} }
/* FILTER BAR */
.filter-bar {
padding: 8px 16px;
background-color: var(--bg-faded-color);
display: flex;
gap: 8px;
overflow-x: auto;
mask-image: linear-gradient(
to right,
transparent,
black 16px,
black calc(100% - 16px),
transparent
);
align-items: center;
}
@media (min-width: 40em) {
.filter-bar {
background-color: transparent;
}
}
.filter-bar > a {
padding: 8px 16px;
border-radius: 999px;
background-color: var(--bg-color);
color: var(--link-color);
text-decoration: none;
white-space: nowrap;
border: 2px solid transparent;
transition: all 0.3s ease-out;
display: inline-flex;
align-items: center;
gap: 8px;
}
.filter-bar > a:is(:hover, :focus) {
border-color: var(--link-light-color);
}
.filter-bar > a > * {
vertical-align: middle;
}
.filter-bar > a.is-active {
border-color: var(--link-color);
box-shadow: inset 0 0 8px var(--link-faded-color);
}
.filter-bar > a > .filter-count {
font-size: 80%;
display: inline-block;
color: var(--text-insignificant-color);
min-width: 16px;
min-height: 16px;
padding: 4px;
margin: -4px -8px -4px 0;
background-color: var(--bg-faded-color);
border-radius: 999px;
}
/* OTHERS */ /* OTHERS */
@media (min-width: 40em) { @media (min-width: 40em) {

View file

@ -74,6 +74,7 @@ const ICONS = {
time: 'mingcute:time-line', time: 'mingcute:time-line',
refresh: 'mingcute:refresh-2-line', refresh: 'mingcute:refresh-2-line',
emoji2: 'mingcute:emoji-2-line', emoji2: 'mingcute:emoji-2-line',
filter: 'mingcute:filter-2-line',
}; };
const modules = import.meta.glob('/node_modules/@iconify-icons/mingcute/*.js'); const modules = import.meta.glob('/node_modules/@iconify-icons/mingcute/*.js');

View file

@ -33,6 +33,7 @@ function Timeline({
headerEnd, headerEnd,
timelineStart, timelineStart,
allowFilters, allowFilters,
refresh,
}) { }) {
const [items, setItems] = useState([]); const [items, setItems] = useState([]);
const [uiState, setUIState] = useState('default'); const [uiState, setUIState] = useState('default');
@ -184,6 +185,9 @@ function Timeline({
scrollableRef.current?.scrollTo({ top: 0 }); scrollableRef.current?.scrollTo({ top: 0 });
loadItems(true); loadItems(true);
}, []); }, []);
useEffect(() => {
loadItems(true);
}, [refresh]);
useEffect(() => { useEffect(() => {
if (reachStart) { if (reachStart) {

View file

@ -1,8 +1,10 @@
import { useEffect, useMemo, useRef, useState } from 'preact/hooks'; import { useEffect, useMemo, useRef, useState } from 'preact/hooks';
import { useParams } from 'react-router-dom'; import { useParams, useSearchParams } from 'react-router-dom';
import { useSnapshot } from 'valtio'; import { useSnapshot } from 'valtio';
import AccountInfo from '../components/account-info'; import AccountInfo from '../components/account-info';
import Icon from '../components/icon';
import Link from '../components/link';
import Timeline from '../components/timeline'; import Timeline from '../components/timeline';
import { api } from '../utils/api'; import { api } from '../utils/api';
import emojifyText from '../utils/emojify-text'; import emojifyText from '../utils/emojify-text';
@ -15,6 +17,11 @@ const LIMIT = 20;
function AccountStatuses() { function AccountStatuses() {
const snapStates = useSnapshot(states); const snapStates = useSnapshot(states);
const { id, ...params } = useParams(); const { id, ...params } = useParams();
const [searchParams, setSearchParams] = useSearchParams();
const excludeReplies = !searchParams.get('replies');
const tagged = searchParams.get('tagged');
const media = !!searchParams.get('media');
console.log({ excludeReplies });
const { masto, instance, authenticated } = api({ instance: params.instance }); const { masto, instance, authenticated } = api({ instance: params.instance });
const accountStatusesIterator = useRef(); const accountStatusesIterator = useRef();
async function fetchAccountStatuses(firstLoad) { async function fetchAccountStatuses(firstLoad) {
@ -25,7 +32,7 @@ function AccountStatuses() {
pinned: true, pinned: true,
}) })
.next(); .next();
if (pinnedStatuses?.length) { if (pinnedStatuses?.length && !tagged && !media) {
pinnedStatuses.forEach((status) => { pinnedStatuses.forEach((status) => {
status._pinned = true; status._pinned = true;
saveStatus(status, instance); saveStatus(status, instance);
@ -45,6 +52,9 @@ function AccountStatuses() {
if (firstLoad || !accountStatusesIterator.current) { if (firstLoad || !accountStatusesIterator.current) {
accountStatusesIterator.current = masto.v1.accounts.listStatuses(id, { accountStatusesIterator.current = masto.v1.accounts.listStatuses(id, {
limit: LIMIT, limit: LIMIT,
exclude_replies: excludeReplies,
only_media: media,
tagged,
}); });
} }
const { value, done } = await accountStatusesIterator.current.next(); const { value, done } = await accountStatusesIterator.current.next();
@ -62,6 +72,7 @@ function AccountStatuses() {
} }
const [account, setAccount] = useState(); const [account, setAccount] = useState();
const [featuredTags, setFeaturedTags] = useState([]);
useTitle( useTitle(
`${account?.displayName ? account.displayName + ' ' : ''}@${ `${account?.displayName ? account.displayName + ' ' : ''}@${
account?.acct ? account.acct : 'Account posts' account?.acct ? account.acct : 'Account posts'
@ -77,6 +88,13 @@ function AccountStatuses() {
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
try {
const featuredTags = await masto.v1.accounts.listFeaturedTags(id);
console.log({ featuredTags });
setFeaturedTags(featuredTags);
} catch (e) {
console.error(e);
}
})(); })();
}, [id]); }, [id]);
@ -85,15 +103,59 @@ function AccountStatuses() {
const TimelineStart = useMemo(() => { const TimelineStart = useMemo(() => {
const cachedAccount = snapStates.accounts[`${id}@${instance}`]; const cachedAccount = snapStates.accounts[`${id}@${instance}`];
return ( return (
<AccountInfo <>
instance={instance} <AccountInfo
account={cachedAccount || id} instance={instance}
fetchAccount={() => masto.v1.accounts.fetch(id)} account={cachedAccount || id}
authenticated={authenticated} fetchAccount={() => masto.v1.accounts.fetch(id)}
standalone authenticated={authenticated}
/> standalone
/>
<div class="filter-bar">
<Icon icon="filter" class="insignificant" size="l" />
<Link
to={`/${instance}/a/${id}${excludeReplies ? '?replies=1' : ''}`}
class={excludeReplies ? '' : 'is-active'}
>
+ Replies
</Link>
<Link
to={`/${instance}/a/${id}${media ? '' : '?media=1'}`}
class={media ? 'is-active' : ''}
>
Media
</Link>
{featuredTags.map((tag) => (
<Link
to={`/${instance}/a/${id}${
tagged === tag.name
? ''
: `?tagged=${encodeURIComponent(tag.name)}`
}`}
class={tagged === tag.name ? 'is-active' : ''}
>
<span>
<span class="more-insignificant">#</span>
{tag.name}
</span>
{
// The count differs based on instance 😅
}
{/* <span class="filter-count">{tag.statusesCount}</span> */}
</Link>
))}
</div>
</>
); );
}, [id, instance, authenticated]); }, [
id,
instance,
authenticated,
excludeReplies,
featuredTags,
tagged,
media,
]);
return ( return (
<Timeline <Timeline
@ -127,6 +189,7 @@ function AccountStatuses() {
useItemID useItemID
boostsCarousel={snapStates.settings.boostsCarousel} boostsCarousel={snapStates.settings.boostsCarousel}
timelineStart={TimelineStart} timelineStart={TimelineStart}
refresh={excludeReplies + tagged + media}
/> />
); );
} }