Skip to content

Commit

Permalink
Merge pull request #43 from prtcl/chore/nicer-loading-states
Browse files Browse the repository at this point in the history
improve viewer loading state by hoisting queries
  • Loading branch information
prtcl authored Nov 15, 2024
2 parents 7f5c777 + 4809dcb commit 3675acb
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 38 deletions.
91 changes: 53 additions & 38 deletions src/feat/Projects/components/ProjectDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
type TransitionTo,
} from '@react-spring/web';
import { useQuery } from 'convex/react';
import { useCallback, useState } from 'react';
import { useState } from 'react';
import useMeasure from 'react-use/lib/useMeasure';
import { Flex, type FlexProps } from 'styled-system/jsx';
import { api } from '~/convex/api';
Expand All @@ -14,7 +14,12 @@ import { Loader } from '~/ui/Loader';
import { Markdown } from '~/ui/Markdown';
import { MediaEmbed } from '~/ui/MediaEmbed';
import type { Directions } from '../hooks/useNextPrev';
import type { ProjectId } from '../types';
import type {
ProjectId,
ContentEntity,
CoverImageEntity,
EmbedCodeEntity,
} from '../types';

const fromTransition = (direction: Directions): TransitionFrom<ProjectId> => {
const distance = 25;
Expand Down Expand Up @@ -56,7 +61,9 @@ const ScrollContainer = (props: FlexProps) => {
width="100%"
height="100%"
overflowX="hidden"
transition="opacity 168ms cubic-bezier(.79,.31,.59,.83)"
overflowY="auto"
opacity={0}
{...flexProps}
>
{children}
Expand All @@ -65,24 +72,13 @@ const ScrollContainer = (props: FlexProps) => {
};

const CoverImage = (props: {
hasSeenImages: Set<ProjectId>;
projectId: ProjectId;
coverImage: CoverImageEntity;
useAnimation: boolean;
onLoad: () => void;
}) => {
const { projectId, hasSeenImages } = props;
const { coverImage, onLoad, useAnimation = false } = props;
const [containerRef, containerRect] = useMeasure();
const [isLoading, setLoading] = useState(true);
const coverImage = useQuery(api.projects.loadProjectCoverImage, {
projectId,
});
const handleLoad = useCallback(() => {
hasSeenImages.add(projectId);
setLoading(false);
}, [hasSeenImages, projectId]);

if (!coverImage) {
return null;
}

const { aspectRatio } = coverImage;
const calculatedHeight = containerRect.width
? Math.min(
Expand Down Expand Up @@ -113,11 +109,14 @@ const CoverImage = (props: {
height="100%"
objectFit="contain"
objectPosition="center"
onLoad={() => handleLoad()}
onLoad={() => {
setLoading(false);
onLoad();
}}
options={{ width: 1280, quality: 75, fit: 'cover' }}
src={coverImage.publicUrl}
style={{ minHeight: `${calculatedHeight}px` }}
useAnimation={!hasSeenImages.has(projectId)}
useAnimation={useAnimation}
width="100%"
_selection={{ bg: 'transparent' }}
/>
Expand All @@ -135,13 +134,8 @@ const CoverImage = (props: {
);
};

const EmbedCode = (props: { projectId: ProjectId }) => {
const { projectId } = props;
const embedCode = useQuery(api.projects.loadProjectEmbed, { projectId });

if (!embedCode) {
return null;
}
const EmbedCode = (props: { embedCode: EmbedCodeEntity }) => {
const { embedCode } = props;

return (
<Flex flex={1} minHeight="fit-content" width="100%">
Expand All @@ -154,13 +148,8 @@ const EmbedCode = (props: { projectId: ProjectId }) => {
);
};

const Content = (props: { projectId: ProjectId }) => {
const { projectId } = props;
const content = useQuery(api.projects.loadProjectContent, { projectId });

if (!content) {
return null;
}
const Content = (props: { content: ContentEntity }) => {
const { content } = props;

return (
<Flex
Expand Down Expand Up @@ -188,6 +177,36 @@ const Content = (props: { projectId: ProjectId }) => {
);
};

const checkLoading = (...items: unknown[]) =>
items.some((item) => typeof item === 'undefined');

const InnerDetails = (props: {
projectId: ProjectId;
hasSeenImages: Set<ProjectId>;
}) => {
const { projectId, hasSeenImages } = props;
const coverImage = useQuery(api.projects.loadProjectCoverImage, {
projectId,
});
const content = useQuery(api.projects.loadProjectContent, { projectId });
const embedCode = useQuery(api.projects.loadProjectEmbed, { projectId });
const isLoading = checkLoading(coverImage, content, embedCode);

return (
<ScrollContainer opacity={isLoading ? 0 : 1}>
{coverImage && (
<CoverImage
coverImage={coverImage}
onLoad={() => hasSeenImages.add(projectId)}
useAnimation={!hasSeenImages.has(projectId)}
/>
)}
{content && <Content content={content} />}
{embedCode && <EmbedCode embedCode={embedCode} />}
</ScrollContainer>
);
};

export const ProjectDetails = (props: {
direction: Directions;
projectId: ProjectId;
Expand Down Expand Up @@ -221,11 +240,7 @@ export const ProjectDetails = (props: {
position: 'absolute',
}}
>
<ScrollContainer>
<CoverImage projectId={currentId} hasSeenImages={hasSeenImages} />
<Content projectId={currentId} />
<EmbedCode projectId={currentId} />
</ScrollContainer>
<InnerDetails projectId={currentId} hasSeenImages={hasSeenImages} />
</animated.div>
))}
</Flex>
Expand Down
3 changes: 3 additions & 0 deletions src/feat/Projects/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ import type { Doc, Id } from '~/convex/dataModel';

export type ProjectId = Id<'projects'>;
export type ProjectEntity = Doc<'projects'>;
export type CoverImageEntity = Doc<'images'> & { publicUrl: string };
export type EmbedCodeEntity = Doc<'embeds'> & { title: string };
export type ContentEntity = Doc<'content'>;

0 comments on commit 3675acb

Please sign in to comment.