More support for Pixelfed

This commit is contained in:
Lim Chee Aun 2024-04-14 17:20:18 +08:00
parent afdfdb86da
commit 06c6360cae
5 changed files with 124 additions and 66 deletions

View file

@ -1917,7 +1917,8 @@ body > .szh-menu-container {
/* two columns only */ /* two columns only */
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
} }
.szh-menu .menu-horizontal:has(> .szh-menu__item:only-child) { .szh-menu .menu-horizontal:has(> .szh-menu__item:only-child),
.szh-menu .menu-horizontal:has(> .szh-menu__submenu:only-child) {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
.szh-menu .menu-horizontal > .szh-menu__item:not(:only-child):first-child, .szh-menu .menu-horizontal > .szh-menu__item:not(:only-child):first-child,

View file

@ -12,6 +12,7 @@ import safeBoundingBoxPadding from '../utils/safe-bounding-box-padding';
import states from '../utils/states'; import states from '../utils/states';
import store from '../utils/store'; import store from '../utils/store';
import { getCurrentAccountID } from '../utils/store-utils'; import { getCurrentAccountID } from '../utils/store-utils';
import supports from '../utils/supports';
import Avatar from './avatar'; import Avatar from './avatar';
import Icon from './icon'; import Icon from './icon';
@ -83,8 +84,10 @@ function NavMenu(props) {
return results; return results;
} }
const supportsLists = supports('@mastodon/lists');
const [lists, setLists] = useState([]); const [lists, setLists] = useState([]);
useEffect(() => { useEffect(() => {
if (!supportsLists) return;
if (menuState === 'open') { if (menuState === 'open') {
getLists().then(setLists); getLists().then(setLists);
} }
@ -186,9 +189,11 @@ function NavMenu(props) {
<Icon icon="history2" size="l" /> <Icon icon="history2" size="l" />
<span>Catch-up</span> <span>Catch-up</span>
</MenuLink> </MenuLink>
<MenuLink to="/mentions"> {supports('@mastodon/mentions') && (
<Icon icon="at" size="l" /> <span>Mentions</span> <MenuLink to="/mentions">
</MenuLink> <Icon icon="at" size="l" /> <span>Mentions</span>
</MenuLink>
)}
<MenuLink to="/notifications"> <MenuLink to="/notifications">
<Icon icon="notification" size="l" /> <span>Notifications</span> <Icon icon="notification" size="l" /> <span>Notifications</span>
{snapStates.notificationsShowNew && ( {snapStates.notificationsShowNew && (
@ -232,10 +237,12 @@ function NavMenu(props) {
)} )}
</SubMenu2> </SubMenu2>
) : ( ) : (
<MenuLink to="/l"> supportsLists && (
<Icon icon="list" size="l" /> <MenuLink to="/l">
<span>Lists</span> <Icon icon="list" size="l" />
</MenuLink> <span>Lists</span>
</MenuLink>
)
)} )}
<MenuLink to="/b"> <MenuLink to="/b">
<Icon icon="bookmark" size="l" /> <span>Bookmarks</span> <Icon icon="bookmark" size="l" /> <span>Bookmarks</span>
@ -260,10 +267,12 @@ function NavMenu(props) {
<span>Followed Hashtags</span> <span>Followed Hashtags</span>
</MenuLink> </MenuLink>
<MenuDivider /> <MenuDivider />
<MenuLink to="/ft"> {supports('@mastodon/filters') && (
<Icon icon="filters" size="l" /> <MenuLink to="/ft">
Filters <Icon icon="filters" size="l" />
</MenuLink> Filters
</MenuLink>
)}
<MenuItem <MenuItem
onClick={() => { onClick={() => {
states.showGenericAccounts = { states.showGenericAccounts = {

View file

@ -55,6 +55,7 @@ import states, { getStatus, saveStatus, statusKey } from '../utils/states';
import statusPeek from '../utils/status-peek'; import statusPeek from '../utils/status-peek';
import store from '../utils/store'; import store from '../utils/store';
import { getCurrentAccountID } from '../utils/store-utils'; import { getCurrentAccountID } from '../utils/store-utils';
import supports from '../utils/supports';
import unfurlMastodonLink from '../utils/unfurl-link'; import unfurlMastodonLink from '../utils/unfurl-link';
import useHotkeys from '../utils/useHotkeys'; import useHotkeys from '../utils/useHotkeys';
import useTruncated from '../utils/useTruncated'; import useTruncated from '../utils/useTruncated';
@ -149,6 +150,12 @@ const PostContent = memo(
}, },
); );
const SIZE_CLASS = {
s: 'small',
m: 'medium',
l: 'large',
};
function Status({ function Status({
statusID, statusID,
status, status,
@ -174,7 +181,11 @@ function Status({
}) { }) {
if (skeleton) { if (skeleton) {
return ( return (
<div class={`status skeleton ${mediaFirst ? 'status-media-first' : ''}`}> <div
class={`status skeleton ${
mediaFirst ? 'status-media-first small' : ''
}`}
>
{!mediaFirst && <Avatar size="xxl" />} {!mediaFirst && <Avatar size="xxl" />}
<div class="container"> <div class="container">
<div class="meta"> <div class="meta">
@ -640,6 +651,7 @@ function Status({
}; };
const bookmarkStatus = async () => { const bookmarkStatus = async () => {
if (!supports('@mastodon/post-bookmark')) return;
if (!sameInstance || !authenticated) { if (!sameInstance || !authenticated) {
alert(unauthInteractionErrorMessage); alert(unauthInteractionErrorMessage);
return false; return false;
@ -827,13 +839,15 @@ function Status({
: 'Like'} : 'Like'}
</span> </span>
</MenuItem> </MenuItem>
<MenuItem {supports('@mastodon/post-bookmark') && (
onClick={bookmarkStatusNotify} <MenuItem
className={`menu-bookmark ${bookmarked ? 'checked' : ''}`} onClick={bookmarkStatusNotify}
> className={`menu-bookmark ${bookmarked ? 'checked' : ''}`}
<Icon icon="bookmark" /> >
<span>{bookmarked ? 'Unbookmark' : 'Bookmark'}</span> <Icon icon="bookmark" />
</MenuItem> <span>{bookmarked ? 'Unbookmark' : 'Bookmark'}</span>
</MenuItem>
)}
</div> </div>
</> </>
)} )}
@ -1077,16 +1091,18 @@ function Status({
)} )}
{isSelf && ( {isSelf && (
<div class="menu-horizontal"> <div class="menu-horizontal">
<MenuItem {supports('@mastodon/post-edit') && (
onClick={() => { <MenuItem
states.showCompose = { onClick={() => {
editStatus: status, states.showCompose = {
}; editStatus: status,
}} };
> }}
<Icon icon="pencil" /> >
<span>Edit</span> <Icon icon="pencil" />
</MenuItem> <span>Edit</span>
</MenuItem>
)}
{isSizeLarge && ( {isSizeLarge && (
<MenuConfirm <MenuConfirm
subMenu subMenu
@ -1395,11 +1411,7 @@ function Status({
? 'status-reply-to' ? 'status-reply-to'
: '' : ''
} visibility-${visibility} ${_pinned ? 'status-pinned' : ''} ${ } visibility-${visibility} ${_pinned ? 'status-pinned' : ''} ${
{ SIZE_CLASS[size]
s: 'small',
m: 'medium',
l: 'large',
}[size]
} ${_deleted ? 'status-deleted' : ''} ${quoted ? 'status-card' : ''} ${ } ${_deleted ? 'status-deleted' : ''} ${quoted ? 'status-card' : ''} ${
isContextMenuOpen ? 'status-menu-open' : '' isContextMenuOpen ? 'status-menu-open' : ''
} ${mediaFirst && hasMediaAttachments ? 'status-media-first' : ''}`} } ${mediaFirst && hasMediaAttachments ? 'status-media-first' : ''}`}
@ -2160,16 +2172,18 @@ function Status({
onClick={favouriteStatus} onClick={favouriteStatus}
/> />
</div> </div>
<div class="action"> {supports('@mastodon/post-bookmark') && (
<StatusButton <div class="action">
checked={bookmarked} <StatusButton
title={['Bookmark', 'Unbookmark']} checked={bookmarked}
alt={['Bookmark', 'Bookmarked']} title={['Bookmark', 'Unbookmark']}
class="bookmark-button" alt={['Bookmark', 'Bookmarked']}
icon="bookmark" class="bookmark-button"
onClick={bookmarkStatus} icon="bookmark"
/> onClick={bookmarkStatus}
</div> />
</div>
)}
<Menu2 <Menu2
portal={{ portal={{
target: target:

View file

@ -19,6 +19,7 @@ import pmem from '../utils/pmem';
import shortenNumber from '../utils/shorten-number'; import shortenNumber from '../utils/shorten-number';
import states from '../utils/states'; import states from '../utils/states';
import { saveStatus } from '../utils/states'; import { saveStatus } from '../utils/states';
import supports from '../utils/supports';
import useTitle from '../utils/useTitle'; import useTitle from '../utils/useTitle';
const LIMIT = 20; const LIMIT = 20;
@ -33,6 +34,17 @@ const fetchLinks = pmem(
}, },
); );
function fetchTrends(masto) {
if (supports('@pixelfed/trending')) {
return masto.pixelfed.v2.discover.posts.trending.list({
range: 'daily',
});
}
return masto.v1.trends.statuses.list({
limit: LIMIT,
});
}
function Trending({ columnMode, ...props }) { function Trending({ columnMode, ...props }) {
const snapStates = useSnapshot(states); const snapStates = useSnapshot(states);
const params = columnMode ? {} : useParams(); const params = columnMode ? {} : useParams();
@ -48,36 +60,39 @@ function Trending({ columnMode, ...props }) {
const [hashtags, setHashtags] = useState([]); const [hashtags, setHashtags] = useState([]);
const [links, setLinks] = useState([]); const [links, setLinks] = useState([]);
const trendIterator = useRef(); const trendIterator = useRef();
async function fetchTrend(firstLoad) { async function fetchTrend(firstLoad) {
if (firstLoad || !trendIterator.current) { if (firstLoad || !trendIterator.current) {
trendIterator.current = masto.v1.trends.statuses.list({ trendIterator.current = fetchTrends(masto);
limit: LIMIT,
});
// Get hashtags // Get hashtags
try { if (supports('@mastodon/trending-hashtags')) {
const iterator = masto.v1.trends.tags.list(); try {
const { value: tags } = await iterator.next(); const iterator = masto.v1.trends.tags.list();
console.log('tags', tags); const { value: tags } = await iterator.next();
if (tags?.length) { console.log('tags', tags);
setHashtags(tags); if (tags?.length) {
setHashtags(tags);
}
} catch (e) {
console.error(e);
} }
} catch (e) {
console.error(e);
} }
// Get links // Get links
try { if (supports('@mastodon/trending-links')) {
const { value } = await fetchLinks(masto, instance); try {
// 4 types available: link, photo, video, rich const { value } = await fetchLinks(masto, instance);
// Only want links for now // 4 types available: link, photo, video, rich
const links = value?.filter?.((link) => link.type === 'link'); // Only want links for now
console.log('links', links); const links = value?.filter?.((link) => link.type === 'link');
if (links?.length) { console.log('links', links);
setLinks(links); if (links?.length) {
setLinks(links);
}
} catch (e) {
console.error(e);
} }
} catch (e) {
console.error(e);
} }
} }
const results = await trendIterator.current.next(); const results = await trendIterator.current.next();

View file

@ -4,6 +4,20 @@ import features from '../data/features.json';
import { getCurrentInstance } from './store-utils'; import { getCurrentInstance } from './store-utils';
// Non-semver(?) UA string detection
// Can't put this inside features.json due to regex
const containPixelfed = /pixelfed/i;
const notContainPixelfed = /^(?!.*pixelfed).*$/i;
const platformFeatures = {
'@mastodon/lists': notContainPixelfed,
'@mastodon/filters': notContainPixelfed,
'@mastodon/mentions': notContainPixelfed,
'@mastodon/trending-hashtags': notContainPixelfed,
'@mastodon/trending-links': notContainPixelfed,
'@mastodon/post-bookmark': notContainPixelfed,
'@mastodon/post-edit': notContainPixelfed,
'@pixelfed/trending': containPixelfed,
};
const supportsCache = {}; const supportsCache = {};
function supports(feature) { function supports(feature) {
@ -11,6 +25,11 @@ function supports(feature) {
const { version, domain } = getCurrentInstance(); const { version, domain } = getCurrentInstance();
const key = `${domain}-${feature}`; const key = `${domain}-${feature}`;
if (supportsCache[key]) return supportsCache[key]; if (supportsCache[key]) return supportsCache[key];
if (platformFeatures[feature]) {
return (supportsCache[key] = platformFeatures[feature].test(version));
}
const range = features[feature]; const range = features[feature];
if (!range) return false; if (!range) return false;
return (supportsCache[key] = satisfies(version, range, { return (supportsCache[key] = satisfies(version, range, {