Skip to content

Commit

Permalink
[Glitch] Change embedded posts to use web UI
Browse files Browse the repository at this point in the history
Port 3d46f47 to glitch-soc

Co-authored-by: Claire <claire.github-309c@sitedethib.com>
Signed-off-by: Claire <claire.github-309c@sitedethib.com>
  • Loading branch information
Gargron and ClearlyClaire committed Sep 12, 2024
1 parent 3465d39 commit e705ec1
Show file tree
Hide file tree
Showing 14 changed files with 650 additions and 766 deletions.
6 changes: 4 additions & 2 deletions app/javascript/flavours/glitch/actions/statuses.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,13 @@ export function fetchStatusRequest(id, skipLoading) {
};
}

export function fetchStatus(id, forceFetch = false) {
export function fetchStatus(id, forceFetch = false, alsoFetchContext = true) {
return (dispatch, getState) => {
const skipLoading = !forceFetch && getState().getIn(['statuses', id], null) !== null;

dispatch(fetchContext(id));
if (alsoFetchContext) {
dispatch(fetchContext(id));
}

if (skipLoading) {
return;
Expand Down
7 changes: 7 additions & 0 deletions app/javascript/flavours/glitch/components/logo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ export const WordmarkLogo: React.FC = () => (
</svg>
);

export const IconLogo: React.FC = () => (
<svg viewBox='0 0 79 79' className='logo logo--icon' role='img'>
<title>Mastodon</title>
<use xlinkHref='#logo-symbol-icon' />
</svg>
);

export const SymbolLogo: React.FC = () => (
<img src={logo} alt='Mastodon' className='logo logo--icon' />
);
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@ import PropTypes from 'prop-types';

import { FormattedMessage } from 'react-intl';

import { IconLogo } from 'flavours/glitch/components/logo';
import { AuthorLink } from 'flavours/glitch/features/explore/components/author_link';

export const MoreFromAuthor = ({ accountId }) => (
<div className='more-from-author'>
<svg viewBox='0 0 79 79' className='logo logo--icon' role='img'>
<use xlinkHref='#logo-symbol-icon' />
</svg>

<IconLogo />
<FormattedMessage id='link_preview.more_from_author' defaultMessage='More from {name}' values={{ name: <AuthorLink accountId={accountId} /> }} />
</div>
);
Expand Down
74 changes: 74 additions & 0 deletions app/javascript/flavours/glitch/entrypoints/embed.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { createRoot } from 'react-dom/client';

import '@/entrypoints/public-path';

import { start } from 'flavours/glitch/common';
import { Status } from 'flavours/glitch/features/standalone/status';
import { afterInitialRender } from 'flavours/glitch/hooks/useRenderSignal';
import { loadPolyfills } from 'flavours/glitch/polyfills';
import ready from 'flavours/glitch/ready';

start();

function loaded() {
const mountNode = document.getElementById('mastodon-status');

if (mountNode) {
const attr = mountNode.getAttribute('data-props');

if (!attr) return;

const props = JSON.parse(attr) as { id: string; locale: string };
const root = createRoot(mountNode);

root.render(<Status {...props} />);
}
}

function main() {
ready(loaded).catch((error: unknown) => {
console.error(error);
});
}

loadPolyfills()
.then(main)
.catch((error: unknown) => {
console.error(error);
});

interface SetHeightMessage {
type: 'setHeight';
id: string;
height: number;
}

function isSetHeightMessage(data: unknown): data is SetHeightMessage {
if (
data &&
typeof data === 'object' &&
'type' in data &&
data.type === 'setHeight'
)
return true;
else return false;
}

window.addEventListener('message', (e) => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- typings are not correct, it can be null in very rare cases
if (!e.data || !isSetHeightMessage(e.data) || !window.parent) return;

const data = e.data;

// We use a timeout to allow for the React page to render before calculating the height
afterInitialRender(() => {
window.parent.postMessage(
{
type: 'setHeight',
id: data.id,
height: document.getElementsByTagName('html')[0]?.scrollHeight,
},
'*',
);
});
});
37 changes: 0 additions & 37 deletions app/javascript/flavours/glitch/entrypoints/public.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,43 +37,6 @@ const messages = defineMessages({
},
});

interface SetHeightMessage {
type: 'setHeight';
id: string;
height: number;
}

function isSetHeightMessage(data: unknown): data is SetHeightMessage {
if (
data &&
typeof data === 'object' &&
'type' in data &&
data.type === 'setHeight'
)
return true;
else return false;
}

window.addEventListener('message', (e) => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- typings are not correct, it can be null in very rare cases
if (!e.data || !isSetHeightMessage(e.data) || !window.parent) return;

const data = e.data;

ready(() => {
window.parent.postMessage(
{
type: 'setHeight',
id: data.id,
height: document.getElementsByTagName('html')[0]?.scrollHeight,
},
'*',
);
}).catch((e: unknown) => {
console.error('Error in setHeightMessage postMessage', e);
});
});

function loaded() {
const { messages: localeData } = getLocale();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/* eslint-disable @typescript-eslint/no-unsafe-return,
@typescript-eslint/no-explicit-any,
@typescript-eslint/no-unsafe-assignment */

import { useEffect, useCallback } from 'react';

import { Provider } from 'react-redux';

import {
fetchStatus,
toggleStatusSpoilers,
} from 'flavours/glitch/actions/statuses';
import { hydrateStore } from 'flavours/glitch/actions/store';
import { Router } from 'flavours/glitch/components/router';
import { DetailedStatus } from 'flavours/glitch/features/status/components/detailed_status';
import { useRenderSignal } from 'flavours/glitch/hooks/useRenderSignal';
import initialState from 'flavours/glitch/initial_state';
import { IntlProvider } from 'flavours/glitch/locales';
import {
makeGetStatus,
makeGetPictureInPicture,
} from 'flavours/glitch/selectors';
import { store, useAppSelector, useAppDispatch } from 'flavours/glitch/store';

const getStatus = makeGetStatus() as unknown as (arg0: any, arg1: any) => any;
const getPictureInPicture = makeGetPictureInPicture() as unknown as (
arg0: any,
arg1: any,
) => any;

const Embed: React.FC<{ id: string }> = ({ id }) => {
const status = useAppSelector((state) => getStatus(state, { id }));
const pictureInPicture = useAppSelector((state) =>
getPictureInPicture(state, { id }),
);
const domain = useAppSelector((state) => state.meta.get('domain'));
const dispatch = useAppDispatch();
const dispatchRenderSignal = useRenderSignal();

useEffect(() => {
dispatch(fetchStatus(id, false, false));
}, [dispatch, id]);

const handleToggleHidden = useCallback(() => {
dispatch(toggleStatusSpoilers(id));
}, [dispatch, id]);

// This allows us to calculate the correct page height for embeds
if (status) {
dispatchRenderSignal();
}

// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
const permalink = status?.get('url') as string;

return (
<div className='embed'>
<DetailedStatus
status={status}
domain={domain}
pictureInPicture={pictureInPicture}
onToggleHidden={handleToggleHidden}
expanded={false}
withLogo
/>

<a
className='embed__overlay'
href={permalink}
target='_blank'
rel='noreferrer noopener'
aria-label=''
/>
</div>
);
};

export const Status: React.FC<{ id: string }> = ({ id }) => {
useEffect(() => {
if (initialState) {
store.dispatch(hydrateStore(initialState));
}
}, []);

return (
<IntlProvider>
<Provider store={store}>
<Router>
<Embed id={id} />
</Router>
</Provider>
</IntlProvider>
);
};
Loading

0 comments on commit e705ec1

Please sign in to comment.