Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update from main #173

Merged
merged 12 commits into from
Jun 30, 2023
3 changes: 3 additions & 0 deletions src/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -1358,6 +1358,9 @@ body:has(.media-modal-container + .status-deck) .media-post-link {
.tag.collapsed {
margin: 0;
}
.tag.danger {
background-color: var(--red-color);
}

/* MENU POPUP */

Expand Down
35 changes: 23 additions & 12 deletions src/components/account-info.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import shortenNumber from '../utils/shorten-number';
import showToast from '../utils/show-toast';
import states, { hideAllModals } from '../utils/states';
import store from '../utils/store';
import { updateAccount } from '../utils/store-utils';

import AccountBlock from './account-block';
import Avatar from './avatar';
Expand Down Expand Up @@ -483,6 +484,12 @@ function RelatedActions({ info, instance, authenticated }) {
}
}, [info, authenticated]);

useEffect(() => {
if (info && isSelf) {
updateAccount(info);
}
}, [info, isSelf]);

const loading = relationshipUIState === 'loading';
const menuInstanceRef = useRef(null);

Expand Down Expand Up @@ -524,18 +531,22 @@ function RelatedActions({ info, instance, authenticated }) {
</div>
</div>
<p class="actions">
{followedBy ? (
<span class="tag">Following you</span>
) : !!lastStatusAt ? (
<small class="insignificant">
Last post:{' '}
{niceDateTime(lastStatusAt, {
hideTime: true,
})}
</small>
) : (
<span />
)}{' '}
<span>
{followedBy ? (
<span class="tag">Following you</span>
) : !!lastStatusAt ? (
<small class="insignificant">
Last post:{' '}
{niceDateTime(lastStatusAt, {
hideTime: true,
})}
</small>
) : (
<span />
)}
{muting && <span class="tag danger">Muted</span>}
{blocking && <span class="tag danger">Blocked</span>}
</span>{' '}
<span class="buttons">
<Menu
instanceRef={menuInstanceRef}
Expand Down
17 changes: 17 additions & 0 deletions src/components/media.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import Icon from './icon';
import Link from './link';
import { formatDuration } from './status';

const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); // https://stackoverflow.com/a/23522755

/*
Media type
===
Expand Down Expand Up @@ -117,6 +119,20 @@ function Media({ media, to, showOriginal, autoAnimate, onClick = () => {} }) {
if (isImage) {
// Note: type: unknown might not have width/height
quickPinchZoomProps.containerProps.style.display = 'inherit';

useLayoutEffect(() => {
if (!isSafari) return;
if (!showOriginal) return;
(async () => {
try {
await fetch(mediaURL, { mode: 'no-cors' });
mediaRef.current.src = mediaURL;
} catch (e) {
// Ignore
}
})();
}, [mediaURL]);

return (
<Parent
ref={parentRef}
Expand Down Expand Up @@ -170,6 +186,7 @@ function Media({ media, to, showOriginal, autoAnimate, onClick = () => {} }) {
}}
onLoad={(e) => {
e.target.closest('.media-image').style.backgroundImage = '';
e.target.dataset.loaded = true;
}}
onError={(e) => {
const { src } = e.target;
Expand Down
155 changes: 131 additions & 24 deletions src/pages/search.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import './search.css';

import { forwardRef } from 'preact/compat';
import { useEffect, useImperativeHandle, useRef, useState } from 'preact/hooks';
import {
useEffect,
useImperativeHandle,
useLayoutEffect,
useRef,
useState,
} from 'preact/hooks';
import { InView } from 'react-intersection-observer';
import { useParams, useSearchParams } from 'react-router-dom';

import AccountBlock from '../components/account-block';
Expand All @@ -13,6 +20,9 @@ import Status from '../components/status';
import { api } from '../utils/api';
import useTitle from '../utils/useTitle';

const SHORT_LIMIT = 5;
const LIMIT = 40;

function Search(props) {
const params = useParams();
const { masto, instance, authenticated } = api({
Expand Down Expand Up @@ -40,35 +50,78 @@ function Search(props) {
`/search`,
);

const [showMore, setShowMore] = useState(false);
const offsetRef = useRef(0);
useEffect(() => {
offsetRef.current = 0;
}, [type]);

const scrollableRef = useRef();
useLayoutEffect(() => {
scrollableRef.current?.scrollTo?.(0, 0);
}, [q, type]);

const [statusResults, setStatusResults] = useState([]);
const [accountResults, setAccountResults] = useState([]);
const [hashtagResults, setHashtagResults] = useState([]);

function loadResults(firstLoad) {
setUiState('loading');
if (firstLoad && !type) {
setStatusResults(statusResults.slice(0, SHORT_LIMIT));
setAccountResults(accountResults.slice(0, SHORT_LIMIT));
setHashtagResults(hashtagResults.slice(0, SHORT_LIMIT));
}

(async () => {
const params = {
q,
resolve: authenticated,
limit: SHORT_LIMIT,
};
if (type) {
params.limit = LIMIT;
params.type = type;
params.offset = offsetRef.current;
}
try {
const results = await masto.v2.search(params);
console.log(results);
if (type && !firstLoad) {
if (type === 'statuses') {
setStatusResults((prev) => [...prev, ...results.statuses]);
} else if (type === 'accounts') {
setAccountResults((prev) => [...prev, ...results.accounts]);
} else if (type === 'hashtags') {
setHashtagResults((prev) => [...prev, ...results.hashtags]);
}
offsetRef.current = offsetRef.current + LIMIT;
setShowMore(!!results[type]?.length);
} else {
setStatusResults(results.statuses);
setAccountResults(results.accounts);
setHashtagResults(results.hashtags);
}
setUiState('default');
} catch (err) {
console.error(err);
setUiState('error');
}
})();
}

useEffect(() => {
// searchFieldRef.current?.focus?.();
// searchFormRef.current?.focus?.();
if (q) {
// searchFieldRef.current.value = q;
searchFormRef.current?.setValue?.(q);

setUiState('loading');
(async () => {
const results = await masto.v2.search({
q,
limit: type ? 40 : 5,
resolve: authenticated,
type,
});
console.log(results);
setStatusResults(results.statuses);
setAccountResults(results.accounts);
setHashtagResults(results.hashtags);
setUiState('default');
})();
loadResults(true);
}
}, [q, type, instance]);

return (
<div id="search-page" class="deck-container">
<div id="search-page" class="deck-container" ref={scrollableRef}>
<div class="timeline-deck deck">
<header>
<div class="header-grid">
Expand Down Expand Up @@ -110,7 +163,7 @@ function Search(props) {
))}
</div>
)}
{!!q && uiState !== 'loading' ? (
{!!q ? (
<>
{(!type || type === 'accounts') && (
<>
Expand All @@ -121,7 +174,7 @@ function Search(props) {
<>
<ul class="timeline flat accounts-list">
{accountResults.map((account) => (
<li>
<li key={account.id}>
<AccountBlock
account={account}
instance={instance}
Expand All @@ -141,7 +194,14 @@ function Search(props) {
)}
</>
) : (
<p class="ui-state">No accounts found.</p>
!type &&
(uiState === 'loading' ? (
<p class="ui-state">
<Loader abrupt />
</p>
) : (
<p class="ui-state">No accounts found.</p>
))
)}
</>
)}
Expand All @@ -154,7 +214,7 @@ function Search(props) {
<>
<ul class="link-list hashtag-list">
{hashtagResults.map((hashtag) => (
<li>
<li key={hashtag.name}>
<Link
to={
instance
Expand All @@ -180,7 +240,14 @@ function Search(props) {
)}
</>
) : (
<p class="ui-state">No hashtags found.</p>
!type &&
(uiState === 'loading' ? (
<p class="ui-state">
<Loader abrupt />
</p>
) : (
<p class="ui-state">No hashtags found.</p>
))
)}
</>
)}
Expand All @@ -193,7 +260,7 @@ function Search(props) {
<>
<ul class="timeline">
{statusResults.map((status) => (
<li>
<li key={status.id}>
<Link
class="status-link"
to={
Expand All @@ -219,10 +286,50 @@ function Search(props) {
)}
</>
) : (
<p class="ui-state">No posts found.</p>
!type &&
(uiState === 'loading' ? (
<p class="ui-state">
<Loader abrupt />
</p>
) : (
<p class="ui-state">No posts found.</p>
))
)}
</>
)}
{!!type &&
(uiState === 'default' ? (
showMore ? (
<InView
onChange={(inView) => {
if (inView) {
loadResults();
}
}}
>
<button
type="button"
class="plain block"
onClick={() => loadResults()}
style={{ marginBlockEnd: '6em' }}
>
Show more&hellip;
</button>
</InView>
) : (
<p class="ui-state insignificant">The end.</p>
)
) : (
!!(
hashtagResults.length ||
accountResults.length ||
statusResults.length
) && (
<p class="ui-state">
<Loader abrupt />
</p>
)
))}
</>
) : uiState === 'loading' ? (
<p class="ui-state">
Expand Down
13 changes: 12 additions & 1 deletion src/pages/status.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,10 @@ function StatusPage(params) {
instance={instance}
index={mediaIndex - 1}
onClose={() => {
if (snapStates.prevLocation) {
if (
!window.matchMedia('(min-width: calc(40em + 350px))').matches &&
snapStates.prevLocation
) {
history.back();
} else {
if (showMediaOnly) {
Expand Down Expand Up @@ -638,6 +641,14 @@ function StatusThread({ id, closeLink = '/', instance: propInstance }) {
} ${initialPageState.current === 'status' ? 'slide-in' : ''} ${
viewMode ? `deck-view-${viewMode}` : ''
}`}
onAnimationEnd={(e) => {
// Fix the bounce effect when switching viewMode
// `slide-in` animation kicks in when switching viewMode
if (initialPageState.current === 'status') {
// e.target.classList.remove('slide-in');
initialPageState.current = null;
}
}}
>
<header
class={`${heroInView ? 'inview' : ''} ${
Expand Down
Loading