diff --git a/src/components/status.jsx b/src/components/status.jsx
index f0c90894..aa48edc7 100644
--- a/src/components/status.jsx
+++ b/src/components/status.jsx
@@ -20,466 +20,6 @@ import visibilityIconsMap from '../utils/visibility-icons-map';
import Avatar from './avatar';
import Icon from './icon';
-/*
-Media type
-===
-unknown = unsupported or unrecognized file type
-image = Static image
-gifv = Looping, soundless animation
-video = Video clip
-audio = Audio track
-*/
-
-function Media({ media, showOriginal, onClick }) {
- const { blurhash, description, meta, previewUrl, remoteUrl, url, type } =
- media;
- const { original, small, focus } = meta || {};
-
- const width = showOriginal ? original?.width : small?.width;
- const height = showOriginal ? original?.height : small?.height;
- const mediaURL = showOriginal ? url : previewUrl;
-
- const rgbAverageColor = blurhash ? getBlurHashAverageColor(blurhash) : null;
-
- const videoRef = useRef();
-
- let focalBackgroundPosition;
- if (focus) {
- // Convert focal point to CSS background position
- // Formula from jquery-focuspoint
- // x = -1, y = 1 => 0% 0%
- // x = 0, y = 0 => 50% 50%
- // x = 1, y = -1 => 100% 100%
- const x = ((focus.x + 1) / 2) * 100;
- const y = ((1 - focus.y) / 2) * 100;
- focalBackgroundPosition = `${x.toFixed(0)}% ${y.toFixed(0)}%`;
- }
-
- if (type === 'image' || (type === 'unknown' && previewUrl && url)) {
- // Note: type: unknown might not have width/height
- return (
-
- );
- } else if (type === 'gifv' || type === 'video') {
- // 20 seconds, treat as a gif
- const isGIF = type === 'gifv' && original.duration <= 20;
- return (
-
- );
- } else if (type === 'audio') {
- return (
-
- );
- }
-}
-
-function Card({ card }) {
- const {
- blurhash,
- title,
- description,
- html,
- providerName,
- authorName,
- width,
- height,
- image,
- url,
- type,
- embedUrl,
- } = card;
-
- /* type
- link = Link OEmbed
- photo = Photo OEmbed
- video = Video OEmbed
- rich = iframe OEmbed. Not currently accepted, so won’t show up in practice.
- */
-
- const hasText = title || providerName || authorName;
-
- if (hasText && image) {
- const domain = new URL(url).hostname.replace(/^www\./, '');
- return (
-
- {
- try {
- e.target.style.display = 'none';
- } catch (e) {}
- }}
- />
-
-
- );
- } else if (type === 'photo') {
- return (
-
-
-
- );
- } else if (type === 'video') {
- return (
-
- );
- }
-}
-
-function Poll({ poll, readOnly }) {
- const [pollSnapshot, setPollSnapshot] = useState(poll);
- const [uiState, setUIState] = useState('default');
-
- useEffect(() => {
- setPollSnapshot(poll);
- }, [poll]);
-
- const {
- expired,
- expiresAt,
- id,
- multiple,
- options,
- ownVotes,
- voted,
- votersCount,
- votesCount,
- } = pollSnapshot;
-
- const expiresAtDate = !!expiresAt && new Date(expiresAt);
-
- return (
-
- {voted || expired ? (
- options.map((option, i) => {
- const { title, votesCount: optionVotesCount } = option;
- const pollVotesCount = votersCount || votesCount;
- const percentage =
- Math.round((optionVotesCount / pollVotesCount) * 100) || 0;
- // check if current poll choice is the leading one
- const isLeading =
- optionVotesCount > 0 &&
- optionVotesCount === Math.max(...options.map((o) => o.votesCount));
- return (
-
-
- {title}
- {voted && ownVotes.includes(i) && (
- <>
- {' '}
-
- >
- )}
-
-
- {percentage}%
-
-
- );
- })
- ) : (
-
- )}
- {!readOnly && (
-
- {shortenNumber(votersCount)}{' '}
- {votersCount === 1 ? 'voter' : 'voters'}
- {votersCount !== votesCount && (
- <>
- {' '}
- •
- {shortenNumber(votesCount)}
- {' '}
- vote
- {votesCount === 1 ? '' : 's'}
- >
- )}{' '}
- • {expired ? 'Ended' : 'Ending'}{' '}
- {!!expiresAtDate && (
-
- )}
-
- )}
-
- );
-}
-
-function EditedAtModal({ statusID, onClose = () => {} }) {
- const [uiState, setUIState] = useState('default');
- const [editHistory, setEditHistory] = useState([]);
-
- useEffect(() => {
- setUIState('loading');
- (async () => {
- try {
- const editHistory = await masto.statuses.fetchHistory(statusID);
- console.log(editHistory);
- setEditHistory(editHistory);
- setUIState('default');
- } catch (e) {
- console.error(e);
- setUIState('error');
- }
- })();
- }, []);
-
- const currentYear = new Date().getFullYear();
-
- return (
-
-
-
Edit History
- {uiState === 'error' &&
Failed to load history
}
- {uiState === 'loading' && (
-
- Loading…
-
- )}
- {editHistory.length > 0 && (
-
- {editHistory.map((status) => {
- const { createdAt } = status;
- const createdAtDate = new Date(createdAt);
- return (
- -
-
-
-
-
-
- );
- })}
-
- )}
-
- );
-}
-
function fetchAccount(id) {
return masto.accounts.fetch(id);
}
@@ -1150,6 +690,466 @@ function Status({
);
}
+/*
+Media type
+===
+unknown = unsupported or unrecognized file type
+image = Static image
+gifv = Looping, soundless animation
+video = Video clip
+audio = Audio track
+*/
+
+function Media({ media, showOriginal, onClick }) {
+ const { blurhash, description, meta, previewUrl, remoteUrl, url, type } =
+ media;
+ const { original, small, focus } = meta || {};
+
+ const width = showOriginal ? original?.width : small?.width;
+ const height = showOriginal ? original?.height : small?.height;
+ const mediaURL = showOriginal ? url : previewUrl;
+
+ const rgbAverageColor = blurhash ? getBlurHashAverageColor(blurhash) : null;
+
+ const videoRef = useRef();
+
+ let focalBackgroundPosition;
+ if (focus) {
+ // Convert focal point to CSS background position
+ // Formula from jquery-focuspoint
+ // x = -1, y = 1 => 0% 0%
+ // x = 0, y = 0 => 50% 50%
+ // x = 1, y = -1 => 100% 100%
+ const x = ((focus.x + 1) / 2) * 100;
+ const y = ((1 - focus.y) / 2) * 100;
+ focalBackgroundPosition = `${x.toFixed(0)}% ${y.toFixed(0)}%`;
+ }
+
+ if (type === 'image' || (type === 'unknown' && previewUrl && url)) {
+ // Note: type: unknown might not have width/height
+ return (
+
+ );
+ } else if (type === 'gifv' || type === 'video') {
+ // 20 seconds, treat as a gif
+ const isGIF = type === 'gifv' && original.duration <= 20;
+ return (
+
+ );
+ } else if (type === 'audio') {
+ return (
+
+ );
+ }
+}
+
+function Card({ card }) {
+ const {
+ blurhash,
+ title,
+ description,
+ html,
+ providerName,
+ authorName,
+ width,
+ height,
+ image,
+ url,
+ type,
+ embedUrl,
+ } = card;
+
+ /* type
+ link = Link OEmbed
+ photo = Photo OEmbed
+ video = Video OEmbed
+ rich = iframe OEmbed. Not currently accepted, so won’t show up in practice.
+ */
+
+ const hasText = title || providerName || authorName;
+
+ if (hasText && image) {
+ const domain = new URL(url).hostname.replace(/^www\./, '');
+ return (
+
+ {
+ try {
+ e.target.style.display = 'none';
+ } catch (e) {}
+ }}
+ />
+
+
+ );
+ } else if (type === 'photo') {
+ return (
+
+
+
+ );
+ } else if (type === 'video') {
+ return (
+
+ );
+ }
+}
+
+function Poll({ poll, readOnly }) {
+ const [pollSnapshot, setPollSnapshot] = useState(poll);
+ const [uiState, setUIState] = useState('default');
+
+ useEffect(() => {
+ setPollSnapshot(poll);
+ }, [poll]);
+
+ const {
+ expired,
+ expiresAt,
+ id,
+ multiple,
+ options,
+ ownVotes,
+ voted,
+ votersCount,
+ votesCount,
+ } = pollSnapshot;
+
+ const expiresAtDate = !!expiresAt && new Date(expiresAt);
+
+ return (
+
+ {voted || expired ? (
+ options.map((option, i) => {
+ const { title, votesCount: optionVotesCount } = option;
+ const pollVotesCount = votersCount || votesCount;
+ const percentage =
+ Math.round((optionVotesCount / pollVotesCount) * 100) || 0;
+ // check if current poll choice is the leading one
+ const isLeading =
+ optionVotesCount > 0 &&
+ optionVotesCount === Math.max(...options.map((o) => o.votesCount));
+ return (
+
+
+ {title}
+ {voted && ownVotes.includes(i) && (
+ <>
+ {' '}
+
+ >
+ )}
+
+
+ {percentage}%
+
+
+ );
+ })
+ ) : (
+
+ )}
+ {!readOnly && (
+
+ {shortenNumber(votersCount)}{' '}
+ {votersCount === 1 ? 'voter' : 'voters'}
+ {votersCount !== votesCount && (
+ <>
+ {' '}
+ •
+ {shortenNumber(votesCount)}
+ {' '}
+ vote
+ {votesCount === 1 ? '' : 's'}
+ >
+ )}{' '}
+ • {expired ? 'Ended' : 'Ending'}{' '}
+ {!!expiresAtDate && (
+
+ )}
+
+ )}
+
+ );
+}
+
+function EditedAtModal({ statusID, onClose = () => {} }) {
+ const [uiState, setUIState] = useState('default');
+ const [editHistory, setEditHistory] = useState([]);
+
+ useEffect(() => {
+ setUIState('loading');
+ (async () => {
+ try {
+ const editHistory = await masto.statuses.fetchHistory(statusID);
+ console.log(editHistory);
+ setEditHistory(editHistory);
+ setUIState('default');
+ } catch (e) {
+ console.error(e);
+ setUIState('error');
+ }
+ })();
+ }, []);
+
+ const currentYear = new Date().getFullYear();
+
+ return (
+
+
+
Edit History
+ {uiState === 'error' &&
Failed to load history
}
+ {uiState === 'loading' && (
+
+ Loading…
+
+ )}
+ {editHistory.length > 0 && (
+
+ {editHistory.map((status) => {
+ const { createdAt } = status;
+ const createdAtDate = new Date(createdAt);
+ return (
+ -
+
+
+
+
+
+ );
+ })}
+
+ )}
+
+ );
+}
+
function StatusButton({
checked,
count,