mirror of
https://github.com/filecoin-project/slate.git
synced 2024-11-26 04:19:49 +03:00
Merge pull request #704 from filecoin-project/@aminejv/font-object-preview
improvement: font object preview
This commit is contained in:
commit
f2543c6a1a
@ -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}
|
||||
/>
|
||||
|
50
components/core/FontFrame/Views/FontObjectPreview.js
Normal file
50
components/core/FontFrame/Views/FontObjectPreview.js
Normal 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);
|
@ -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,
|
||||
};
|
||||
|
@ -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}>
|
||||
|
@ -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>
|
||||
|
@ -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}
|
||||
|
@ -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)`,
|
||||
|
@ -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();
|
||||
}}
|
||||
|
@ -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}
|
||||
|
@ -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}
|
||||
/>
|
||||
|
@ -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) ? (
|
||||
|
Loading…
Reference in New Issue
Block a user