Merge pull request #704 from filecoin-project/@aminejv/font-object-preview

improvement: font object preview
This commit is contained in:
martinalong 2021-04-17 14:40:15 -07:00 committed by GitHub
commit f2543c6a1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 132 additions and 12 deletions

View File

@ -747,6 +747,7 @@ export default class DataView extends React.Component {
url={Strings.getCIDGatewayURL(each.cid)}
title={each.name || each.file}
type={each.type}
cid={each.cid}
coverImage={each.coverImage}
dataView={true}
/>

View File

@ -0,0 +1,50 @@
import * as React from "react";
import { css } from "@emotion/react";
import { useFont } from "../hooks";
const withView = (Component) => (props) => {
const ref = React.useRef(null);
const [isIntersecting, setIntersecting] = React.useState(false);
const observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) setIntersecting(true);
});
React.useEffect(() => {
observer.observe(ref.current);
return () => {
observer.disconnect();
};
}, []);
return <div ref={ref}>{isIntersecting ? <Component {...props} /> : null}</div>;
};
const STYLES_LETTER = (theme) => css`
overflow: hidden;
font-size: ${theme.typescale.lvl8};
@media (max-width: ${theme.sizes.tablet}px) {
font-size: ${theme.typescale.lvl4};
}
@media (max-width: ${theme.sizes.mobile}px) {
font-size: ${theme.typescale.lvl5};
}
`;
const FontObjectPreview = React.memo(
({ url, cid, fallback }) => {
const { isFontLoading, error, fontName } = useFont({ url, name: cid }, [cid]);
if (error || isFontLoading) {
return fallback;
}
return (
<div style={{ fontFamily: fontName }}>
<div css={STYLES_LETTER}>Aa</div>
</div>
);
},
(prevProps, nextProps) => prevProps.cid === nextProps.cid && prevProps.url == nextProps.url
);
export default withView(FontObjectPreview);

View File

@ -5,28 +5,37 @@ import * as Content from "./Views/content";
import { generateNumberByStep } from "~/common/utilities";
export const useFont = ({ url, name }, deps) => {
const [loading, setLoading] = React.useState(false);
const [fetchState, setFetchState] = React.useState({ loading: false, error: null });
const prevName = React.useRef(name);
if (!window.$SLATES_LOADED_FONTS) window.$SLATES_LOADED_FONTS = [];
const alreadyLoaded = window.$SLATES_LOADED_FONTS.includes(name);
React.useEffect(() => {
if (alreadyLoaded) return;
if (alreadyLoaded) {
setFetchState((prev) => ({ ...prev, error: null }));
return;
}
setLoading(true);
setFetchState((prev) => ({ ...prev, error: null, loading: true }));
const customFonts = new FontFace(name, `url(${url})`);
customFonts.load().then((loadedFont) => {
document.fonts.add(loadedFont);
prevName.current = name;
setLoading(false);
customFonts
.load()
.then((loadedFont) => {
document.fonts.add(loadedFont);
prevName.current = name;
window.$SLATES_LOADED_FONTS.push(name);
});
setFetchState((prev) => ({ ...prev, loading: false }));
window.$SLATES_LOADED_FONTS.push(name);
})
.catch((err) => {
setFetchState({ loading: false, error: err });
});
}, deps);
return {
isFontLoading: loading,
isFontLoading: fetchState.loading,
error: fetchState.error,
// NOTE(Amine): show previous font while we load the new one.
fontName: alreadyLoaded ? name : prevName.current,
};

View File

@ -44,8 +44,9 @@ const FontLoader = () => (
<p>loading...</p>
</div>
);
export default function FontFrame({ cid, url, ...props }) {
const { isFontLoading, fontName } = useFont({ url, name: cid }, [cid]);
export default function FontFrame({ cid, url, fallback, ...props }) {
const { isFontLoading, error, fontName } = useFont({ url, name: cid }, [cid, url]);
const [
currentState,
{
@ -65,6 +66,10 @@ export default function FontFrame({ cid, url, ...props }) {
},
] = useFontControls();
if (error) {
return fallback;
}
return (
<div css={GET_STYLES_CONTAINER} style={{ fontFamily: fontName }} {...props}>
<div css={STYLES_MOBILE_HIDDEN}>

View File

@ -274,6 +274,7 @@ const FilePreview = ({ file, slate, user, viewerId }) => {
url={file.url ? file.url : Strings.getCIDGatewayURL(file.cid)}
title={file.title || file.name || file.file}
type={file.type}
cid={file.cide}
coverImage={file.coverImage}
/>
</div>

View File

@ -1310,6 +1310,7 @@ export class SlateLayout extends React.Component {
charCap={70}
type={this.state.items[i].type}
url={this.state.items[i].url}
cid={this.state.items[i].cid}
title={this.state.items[i].title || this.state.items[i].name}
coverImage={this.state.items[i].coverImage}
height={pos.h * unit}

View File

@ -39,6 +39,7 @@ export class SlateLayoutMobile extends React.Component {
type={item.type}
url={item.url}
title={item.title || item.name}
cid={item.cid}
coverImage={item.coverImage}
style={{
height: `calc(100vw - 48px)`,

View File

@ -161,6 +161,7 @@ export default class SlateMediaObject extends React.Component {
name={this.props.data.file || this.props.data.name}
cid={this.props.data.cid}
url={url}
fallback={element}
onClick={(e) => {
e.stopPropagation();
}}

View File

@ -8,6 +8,9 @@ import { css } from "@emotion/react";
import { FileTypeIcon } from "~/components/core/FileTypeIcon";
import { Blurhash } from "react-blurhash";
import { isBlurhashValid } from "blurhash";
import { endsWithAny } from "~/common/utilities";
import FontObjectPreview from "~/components/core/FontFrame/Views/FontObjectPreview";
const STYLES_IMAGE_CONTAINER = css`
background-color: ${Constants.system.white};
@ -189,6 +192,51 @@ export default class SlateMediaObjectPreview extends React.Component {
style={{ color: Constants.system.textGray }}
/>
);
if (endsWithAny([".ttf", ".otf", ".woff", ".woff2"], this.props.title)) {
return (
<article
css={STYLES_ENTITY}
style={{
...this.props.style,
border: this.props.previewPanel ? `1px solid ${Constants.system.bgGray}` : "auto",
}}
>
<FontObjectPreview
url={this.props.url}
cid={this.props.cid}
fallback={
<div style={{ display: "flex", alignItems: "center", justifyContent: "center" }}>
<img
src="https://slate.textile.io/ipfs/bafkreib5mnvds3cpe7ot7ibrakmrnja2hv5tast3giiarpl5nun7jpdt5m"
alt=""
height={this.props.previewPanel ? "80" : "64"}
/>
<div style={{ position: "absolute" }}>{element}</div>
</div>
}
/>
{this.props.title && !this.props.iconOnly && !this.props.previewPanel ? (
<div style={{ position: "absolute", bottom: 16, left: 16, width: "inherit" }}>
<div css={STYLES_TITLE}>{title}</div>
{extension ? (
<div
css={STYLES_TITLE}
style={{
fontSize: 12,
color: Constants.system.textGrayLight,
fontFamily: Constants.font.medium,
}}
>
{extension}
</div>
) : null}
</div>
) : null}
</article>
);
}
return (
<article
css={STYLES_ENTITY}

View File

@ -73,6 +73,7 @@ export class SlatePreviewRow extends React.Component {
type={each.type}
url={each.url}
style={this.props.previewStyle}
cid={each.cid}
title={each.title || each.name}
iconOnly={this.props.small}
coverImage={each.coverImage}
@ -358,6 +359,7 @@ export class SlatePreviewBlock extends React.Component {
charCap={30}
type={first.type}
url={first.url}
cid={first.cid}
title={first.title || first.name}
coverImage={first.coverImage}
/>

View File

@ -130,6 +130,7 @@ class ActivitySquare extends React.Component {
title={item.file.title || item.file.name}
type={item.file.type}
style={{ border: "none" }}
cid={item.file.cid}
imageStyle={{ border: "none" }}
/>
{isImage && (this.state.showText || this.props.mobile) ? (