mirror of
https://github.com/roleypoly/roleypoly.git
synced 2025-04-25 03:49:11 +00:00
feat: add skeleton masthead and generic loading page (#182)
* feat: add skeleton masthead and generic loading page * add generic loader to picker page * smooth out spinner, add no-motion state
This commit is contained in:
parent
fa85b30cf0
commit
e0fcfc310e
28 changed files with 380 additions and 30 deletions
|
@ -7,3 +7,4 @@ export default {
|
||||||
|
|
||||||
export const Dark = () => <DotOverlay />;
|
export const Dark = () => <DotOverlay />;
|
||||||
export const Light = () => <DotOverlay light />;
|
export const Light = () => <DotOverlay light />;
|
||||||
|
export const Skeleton = () => <DotOverlay skeleton />;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { animateOpacity } from '@roleypoly/design-system/atoms/placeholder';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
@ -33,6 +34,22 @@ const DotOverlayLight = styled(dotOverlayBase)`
|
||||||
);
|
);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const DotOverlay = ({ light }: { light?: boolean }) => {
|
const DotOverlaySkeleton = styled(DotOverlayDark)`
|
||||||
return light ? <DotOverlayLight /> : <DotOverlayDark />;
|
${animateOpacity}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const DotOverlay = ({
|
||||||
|
light,
|
||||||
|
skeleton,
|
||||||
|
}: {
|
||||||
|
light?: boolean;
|
||||||
|
skeleton?: boolean;
|
||||||
|
}) => {
|
||||||
|
return skeleton ? (
|
||||||
|
<DotOverlaySkeleton />
|
||||||
|
) : light ? (
|
||||||
|
<DotOverlayLight />
|
||||||
|
) : (
|
||||||
|
<DotOverlayDark />
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { Hero } from '@roleypoly/design-system/atoms/hero';
|
||||||
|
import { LoadingFill } from './Loading';
|
||||||
|
export default {
|
||||||
|
title: 'Atoms/Loading Text',
|
||||||
|
component: LoadingFill,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const loading = (args) => (
|
||||||
|
<Hero>
|
||||||
|
<LoadingFill {...args} />
|
||||||
|
</Hero>
|
||||||
|
);
|
30
packages/design-system/atoms/loading-text/Loading.tsx
Normal file
30
packages/design-system/atoms/loading-text/Loading.tsx
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
const loadingTexts = [
|
||||||
|
'Loading Roleypoly...',
|
||||||
|
'Reticulating splines...',
|
||||||
|
'Mining cryptocoins...',
|
||||||
|
'Going to Mars...',
|
||||||
|
'Building the Box Signature Edition...',
|
||||||
|
'Hiring a new CEO...',
|
||||||
|
'Firing the new CEO...',
|
||||||
|
'Doing a calculation...',
|
||||||
|
'Doin a fixy boi...',
|
||||||
|
'Feeling like a plastic bag...',
|
||||||
|
'Levelling up...',
|
||||||
|
'Your Roleypoly is evolving!!!',
|
||||||
|
'Adding subtitles...',
|
||||||
|
'Rolling... Rolling...',
|
||||||
|
];
|
||||||
|
|
||||||
|
export const LoadingFill = (props: { forceIndex?: keyof typeof loadingTexts }) => {
|
||||||
|
const useEasterEgg = Math.floor(Math.random() * 10) === 0;
|
||||||
|
const index =
|
||||||
|
props.forceIndex !== undefined
|
||||||
|
? props.forceIndex
|
||||||
|
: useEasterEgg
|
||||||
|
? Math.floor(Math.random() * loadingTexts.length)
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
const text = loadingTexts[index];
|
||||||
|
|
||||||
|
return <>{text}</>;
|
||||||
|
};
|
1
packages/design-system/atoms/loading-text/index.ts
Normal file
1
packages/design-system/atoms/loading-text/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export * from './Loading';
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { palette } from '../colors';
|
||||||
|
import { PlaceholderBox } from './Placeholder';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Atoms/Placeholder',
|
||||||
|
component: PlaceholderBox,
|
||||||
|
args: {
|
||||||
|
firstColor: palette.taupe100,
|
||||||
|
secondColor: palette.taupe300,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const placeholderBox = (args) => <PlaceholderBox {...args} />;
|
55
packages/design-system/atoms/placeholder/Placeholder.tsx
Normal file
55
packages/design-system/atoms/placeholder/Placeholder.tsx
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import styled, { css, keyframes } from 'styled-components';
|
||||||
|
import { palette } from '../colors';
|
||||||
|
|
||||||
|
export const fadeInOut = keyframes`
|
||||||
|
from {
|
||||||
|
background-color: var(--placeholder-first-color);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
background-color: var(--placeholder-second-color);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const animateFade = (firstColor?: string, secondColor?: string) => css`
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
animation: ${fadeInOut} 2s ease-in-out infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
--placeholder-first-color: ${firstColor};
|
||||||
|
--placeholder-second-color: ${secondColor};
|
||||||
|
`;
|
||||||
|
|
||||||
|
type PlaceholderProps = {
|
||||||
|
forceReduceMotion?: boolean;
|
||||||
|
firstColor?: string;
|
||||||
|
secondColor?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PlaceholderBox = styled.div<PlaceholderProps>`
|
||||||
|
width: 7em;
|
||||||
|
height: 1em;
|
||||||
|
background-color: ${(props) => props.firstColor || palette.taupe200};
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: 2px;
|
||||||
|
position: relative;
|
||||||
|
top: 0.2em;
|
||||||
|
${(props) =>
|
||||||
|
props.secondColor &&
|
||||||
|
!props.forceReduceMotion &&
|
||||||
|
animateFade(props.firstColor, props.secondColor)}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const opacityInOut = keyframes`
|
||||||
|
from {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const animateOpacity = css`
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
animation: ${opacityInOut} 5s ease-in-out infinite alternate;
|
||||||
|
}
|
||||||
|
`;
|
1
packages/design-system/atoms/placeholder/index.ts
Normal file
1
packages/design-system/atoms/placeholder/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export * from './Placeholder';
|
13
packages/design-system/atoms/spinner/Spinner.stories.tsx
Normal file
13
packages/design-system/atoms/spinner/Spinner.stories.tsx
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { Hero } from '@roleypoly/design-system/atoms/hero';
|
||||||
|
import { Spinner } from './Spinner';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Atoms/Spinner',
|
||||||
|
component: Spinner,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const spinner = (args) => (
|
||||||
|
<Hero>
|
||||||
|
<Spinner {...args} />
|
||||||
|
</Hero>
|
||||||
|
);
|
78
packages/design-system/atoms/spinner/Spinner.tsx
Normal file
78
packages/design-system/atoms/spinner/Spinner.tsx
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
import styled, { css, keyframes } from 'styled-components';
|
||||||
|
import { palette } from '../colors';
|
||||||
|
|
||||||
|
type SpinnerProps = {
|
||||||
|
size?: number;
|
||||||
|
reverse?: boolean;
|
||||||
|
color?: string;
|
||||||
|
speed?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const spinnerKeyframes = keyframes`
|
||||||
|
0%, 100% {
|
||||||
|
border-width: 0.5px;
|
||||||
|
border-right-width: 3px;
|
||||||
|
border-left-width: 0;
|
||||||
|
}
|
||||||
|
25% {
|
||||||
|
border-width: 0.5px;
|
||||||
|
border-top-width: 3px;
|
||||||
|
border-bottom-width: 0;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
border-width: 0.5px;
|
||||||
|
border-left-width: 3px;
|
||||||
|
border-right-width: 0;
|
||||||
|
}
|
||||||
|
75% {
|
||||||
|
border-width: 0.5px;
|
||||||
|
border-bottom-width: 3px;
|
||||||
|
border-top-width: 0;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const SpinnerStyled = styled.div<Required<SpinnerProps>>`
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
animation: ${spinnerKeyframes} ${(props) => props.speed}s linear infinite
|
||||||
|
${(props) => (props.reverse ? `reverse` : '')};
|
||||||
|
transform: rotateZ(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
border: 0.5px solid ${(props) => props.color};
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: ${(props) => props.size}px;
|
||||||
|
height: ${(props) => props.size}px;
|
||||||
|
border-radius: ${(props) => props.size * 0.7}px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
${(props) =>
|
||||||
|
props.reverse
|
||||||
|
? css`
|
||||||
|
border-right-width: 0;
|
||||||
|
border-left-width: 3px;
|
||||||
|
`
|
||||||
|
: css`
|
||||||
|
border-right-width: 3px;
|
||||||
|
border-left-width: 0;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Spinner = (
|
||||||
|
props: SpinnerProps & { innerColor?: string; outerColor?: string }
|
||||||
|
) => (
|
||||||
|
<SpinnerStyled
|
||||||
|
size={props.size || 50}
|
||||||
|
color={props.outerColor || palette.taupe400}
|
||||||
|
reverse={!!props.reverse}
|
||||||
|
speed={props.speed || 1}
|
||||||
|
>
|
||||||
|
<SpinnerStyled
|
||||||
|
size={(props.size || 50) * 0.75}
|
||||||
|
color={props.innerColor || palette.taupe100}
|
||||||
|
reverse={!props.reverse}
|
||||||
|
speed={(props.speed || 1) * 1.25}
|
||||||
|
/>
|
||||||
|
</SpinnerStyled>
|
||||||
|
);
|
1
packages/design-system/atoms/spinner/index.ts
Normal file
1
packages/design-system/atoms/spinner/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export * from './Spinner';
|
|
@ -2,6 +2,7 @@ import { Hero } from '@roleypoly/design-system/atoms/hero';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { user } from '../../fixtures/storyData';
|
import { user } from '../../fixtures/storyData';
|
||||||
import { UserAvatarGroup } from './UserAvatarGroup';
|
import { UserAvatarGroup } from './UserAvatarGroup';
|
||||||
|
import { UserAvatarGroupSkeleton } from './UserAvatarGroupSkeleton';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'Molecules/User Avatar Group',
|
title: 'Molecules/User Avatar Group',
|
||||||
|
@ -17,3 +18,9 @@ export const Default = (args) => (
|
||||||
<UserAvatarGroup {...args} />
|
<UserAvatarGroup {...args} />
|
||||||
</Hero>
|
</Hero>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const skeleton = () => (
|
||||||
|
<Hero>
|
||||||
|
<UserAvatarGroupSkeleton />
|
||||||
|
</Hero>
|
||||||
|
);
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { Avatar } from '@roleypoly/design-system/atoms/avatar';
|
||||||
|
import { palette } from '@roleypoly/design-system/atoms/colors';
|
||||||
|
import { PlaceholderBox } from '@roleypoly/design-system/atoms/placeholder';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { Collapse, Group, GroupText } from './UserAvatarGroup.styled';
|
||||||
|
|
||||||
|
export const UserAvatarGroupSkeleton = () => (
|
||||||
|
<Group>
|
||||||
|
<Collapse preventCollapse={false}>
|
||||||
|
<GroupText>
|
||||||
|
<PlaceholderBox firstColor={palette.taupe200} secondColor={palette.taupe300} />
|
||||||
|
</GroupText>
|
||||||
|
|
||||||
|
</Collapse>
|
||||||
|
<Avatar deliberatelyEmpty size={34}></Avatar>
|
||||||
|
</Group>
|
||||||
|
);
|
|
@ -1 +1,2 @@
|
||||||
export * from './UserAvatarGroup';
|
export * from './UserAvatarGroup';
|
||||||
|
export * from './UserAvatarGroupSkeleton';
|
||||||
|
|
|
@ -15,13 +15,35 @@ export type AppShellProps = {
|
||||||
guilds?: GuildSlug[];
|
guilds?: GuildSlug[];
|
||||||
recentGuilds?: string[];
|
recentGuilds?: string[];
|
||||||
disableGuildPicker?: boolean;
|
disableGuildPicker?: boolean;
|
||||||
|
skeleton?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const OptionallyScroll = (props: {
|
||||||
|
shouldScroll: boolean;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) => {
|
||||||
|
if (props.shouldScroll) {
|
||||||
|
return (
|
||||||
|
<Scrollbars
|
||||||
|
style={{ height: 'calc(100vh - 25px)', margin: 0, padding: 0 }}
|
||||||
|
autoHide
|
||||||
|
universal
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</Scrollbars>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <>{props.children}</>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AppShell = (props: AppShellProps) => (
|
export const AppShell = (props: AppShellProps) => (
|
||||||
<>
|
<>
|
||||||
<GlobalStyles />
|
<GlobalStyles />
|
||||||
<GlobalStyleColors />
|
<GlobalStyleColors />
|
||||||
{props.user ? (
|
{props.skeleton ? (
|
||||||
|
<Masthead.Skeleton />
|
||||||
|
) : props.user ? (
|
||||||
<Masthead.Authed
|
<Masthead.Authed
|
||||||
disableGuildPicker={props.disableGuildPicker}
|
disableGuildPicker={props.disableGuildPicker}
|
||||||
guilds={props.guilds || []}
|
guilds={props.guilds || []}
|
||||||
|
@ -32,13 +54,11 @@ export const AppShell = (props: AppShellProps) => (
|
||||||
) : (
|
) : (
|
||||||
<Masthead.Guest />
|
<Masthead.Guest />
|
||||||
)}
|
)}
|
||||||
<Scrollbars
|
<OptionallyScroll shouldScroll={!props.skeleton}>
|
||||||
style={{ height: 'calc(100vh - 25px)', margin: 0, padding: 0 }}
|
<>
|
||||||
autoHide
|
|
||||||
universal
|
|
||||||
>
|
|
||||||
<Content small={props.small}>{props.children}</Content>
|
<Content small={props.small}>{props.children}</Content>
|
||||||
{props.showFooter && <Footer />}
|
{props.showFooter && <Footer />}
|
||||||
</Scrollbars>
|
</>
|
||||||
|
</OptionallyScroll>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,15 +2,20 @@ import * as React from 'react';
|
||||||
import { guild, mastheadSlugs, user } from '../../fixtures/storyData';
|
import { guild, mastheadSlugs, user } from '../../fixtures/storyData';
|
||||||
import { Authed } from './Authed';
|
import { Authed } from './Authed';
|
||||||
import { Guest } from './Guest';
|
import { Guest } from './Guest';
|
||||||
|
import { Skeleton } from './Skeleton';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'Organisms/Masthead',
|
title: 'Organisms/Masthead',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const HasGuilds = () => (
|
export const hasGuilds = () => (
|
||||||
<Authed guilds={mastheadSlugs} activeGuildId={guild.id} user={user} />
|
<Authed guilds={mastheadSlugs} activeGuildId={guild.id} user={user} recentGuilds={[]} />
|
||||||
);
|
);
|
||||||
|
|
||||||
export const NoGuilds = () => <Authed guilds={[]} activeGuildId={null} user={user} />;
|
export const noGuilds = () => (
|
||||||
|
<Authed guilds={[]} activeGuildId={null} user={user} recentGuilds={[]} />
|
||||||
|
);
|
||||||
|
|
||||||
export const Guest_ = () => <Guest />;
|
export const guest = () => <Guest />;
|
||||||
|
|
||||||
|
export const skeleton = () => <Skeleton />;
|
||||||
|
|
27
packages/design-system/organisms/masthead/Skeleton.tsx
Normal file
27
packages/design-system/organisms/masthead/Skeleton.tsx
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { Logotype } from '@roleypoly/design-system/atoms/branding';
|
||||||
|
import { palette } from '@roleypoly/design-system/atoms/colors';
|
||||||
|
import { UserAvatarGroupSkeleton } from '@roleypoly/design-system/molecules/user-avatar-group';
|
||||||
|
import {
|
||||||
|
MastheadAlignment,
|
||||||
|
MastheadBase,
|
||||||
|
MastheadLeft,
|
||||||
|
MastheadRight,
|
||||||
|
} from './Masthead.styled';
|
||||||
|
|
||||||
|
export const Skeleton = () => (
|
||||||
|
<MastheadBase>
|
||||||
|
<MastheadAlignment>
|
||||||
|
<MastheadLeft>
|
||||||
|
<Logotype
|
||||||
|
height={30}
|
||||||
|
circleFill={palette.taupe300}
|
||||||
|
circleOuterFill={palette.taupe200}
|
||||||
|
typeFill={palette.taupe300}
|
||||||
|
/>
|
||||||
|
</MastheadLeft>
|
||||||
|
<MastheadRight>
|
||||||
|
<UserAvatarGroupSkeleton />
|
||||||
|
</MastheadRight>
|
||||||
|
</MastheadAlignment>
|
||||||
|
</MastheadBase>
|
||||||
|
);
|
|
@ -1,2 +1,3 @@
|
||||||
export * from './Authed';
|
export * from './Authed';
|
||||||
export * from './Guest';
|
export * from './Guest';
|
||||||
|
export * from './Skeleton';
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { GenericLoadingTemplate } from './GenericLoading';
|
||||||
|
export default {
|
||||||
|
title: 'Templates/Generic Loading',
|
||||||
|
component: GenericLoadingTemplate,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const genericLoading = (args) => <GenericLoadingTemplate {...args} />;
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { palette } from '@roleypoly/design-system/atoms/colors';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export const TextStyle = styled.div`
|
||||||
|
color: ${palette.taupe500};
|
||||||
|
`;
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { DotOverlay } from '@roleypoly/design-system/atoms/dot-overlay';
|
||||||
|
import { Hero } from '@roleypoly/design-system/atoms/hero';
|
||||||
|
import { LoadingFill } from '@roleypoly/design-system/atoms/loading-text';
|
||||||
|
import { Space } from '@roleypoly/design-system/atoms/space';
|
||||||
|
import { Spinner } from '@roleypoly/design-system/atoms/spinner';
|
||||||
|
import { AppShell } from '@roleypoly/design-system/organisms/app-shell';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { TextStyle } from './GenericLoading.styled';
|
||||||
|
|
||||||
|
const Center = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const GenericLoadingTemplate = (props: { children?: React.ReactNode }) => (
|
||||||
|
<AppShell skeleton>
|
||||||
|
<DotOverlay skeleton />
|
||||||
|
<Hero topSpacing={0} bottomSpacing={50}>
|
||||||
|
<Center>
|
||||||
|
<Spinner />
|
||||||
|
<Space />
|
||||||
|
<TextStyle>{props.children ? props.children : <LoadingFill />}</TextStyle>
|
||||||
|
</Center>
|
||||||
|
</Hero>
|
||||||
|
</AppShell>
|
||||||
|
);
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './GenericLoading';
|
|
@ -1,11 +1,12 @@
|
||||||
import { Router } from '@reach/router';
|
import { Router } from '@reach/router';
|
||||||
|
import { GenericLoadingTemplate } from '@roleypoly/design-system/templates/generic-loading';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import AuthLogin from '../pages/auth/login';
|
||||||
|
import LandingPage from '../pages/landing';
|
||||||
|
|
||||||
const LandingPage = React.lazy(() => import('../pages/landing'));
|
|
||||||
const ServersPage = React.lazy(() => import('../pages/servers'));
|
const ServersPage = React.lazy(() => import('../pages/servers'));
|
||||||
const PickerPage = React.lazy(() => import('../pages/picker'));
|
const PickerPage = React.lazy(() => import('../pages/picker'));
|
||||||
|
|
||||||
const AuthLogin = React.lazy(() => import('../pages/auth/login'));
|
|
||||||
const MachineryNewSession = React.lazy(() => import('../pages/machinery/new-session'));
|
const MachineryNewSession = React.lazy(() => import('../pages/machinery/new-session'));
|
||||||
const MachineryLogout = React.lazy(() => import('../pages/machinery/logout'));
|
const MachineryLogout = React.lazy(() => import('../pages/machinery/logout'));
|
||||||
const MachineryBotJoin = React.lazy(() => import('../pages/machinery/bot-join'));
|
const MachineryBotJoin = React.lazy(() => import('../pages/machinery/bot-join'));
|
||||||
|
@ -18,7 +19,7 @@ const RouteWrapper = (props: {
|
||||||
path?: string;
|
path?: string;
|
||||||
default?: boolean;
|
default?: boolean;
|
||||||
}) => (
|
}) => (
|
||||||
<React.Suspense fallback={<div>Loading...</div>}>
|
<React.Suspense fallback={<GenericLoadingTemplate />}>
|
||||||
<props.component {...props} />
|
<props.component {...props} />
|
||||||
</React.Suspense>
|
</React.Suspense>
|
||||||
);
|
);
|
||||||
|
@ -26,7 +27,7 @@ const RouteWrapper = (props: {
|
||||||
export const AppRouter = () => {
|
export const AppRouter = () => {
|
||||||
return (
|
return (
|
||||||
<Router>
|
<Router>
|
||||||
<RouteWrapper component={LandingPage} path="/" />
|
<LandingPage path="/" />
|
||||||
<RouteWrapper component={ServersPage} path="/servers" />
|
<RouteWrapper component={ServersPage} path="/servers" />
|
||||||
<RouteWrapper component={PickerPage} path="/s/:serverID" />
|
<RouteWrapper component={PickerPage} path="/s/:serverID" />
|
||||||
|
|
||||||
|
@ -37,7 +38,7 @@ export const AppRouter = () => {
|
||||||
<RouteWrapper component={MachineryLogout} path="/machinery/logout" />
|
<RouteWrapper component={MachineryLogout} path="/machinery/logout" />
|
||||||
<RouteWrapper component={MachineryBotJoin} path="/machinery/bot-join" />
|
<RouteWrapper component={MachineryBotJoin} path="/machinery/bot-join" />
|
||||||
<RouteWrapper component={MachineryBotJoin} path="/machinery/bot-join/:serverID" />
|
<RouteWrapper component={MachineryBotJoin} path="/machinery/bot-join/:serverID" />
|
||||||
<RouteWrapper component={AuthLogin} path="/auth/login" />
|
<AuthLogin path="/auth/login" />
|
||||||
|
|
||||||
<RouteWrapper component={DevToolsSetApi} path="/x/dev-tools/set-api" />
|
<RouteWrapper component={DevToolsSetApi} path="/x/dev-tools/set-api" />
|
||||||
<RouteWrapper component={DevToolsSessionDebug} path="/x/dev-tools/session-debug" />
|
<RouteWrapper component={DevToolsSessionDebug} path="/x/dev-tools/session-debug" />
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import { redirectTo } from '@reach/router';
|
import { redirectTo } from '@reach/router';
|
||||||
import { AuthLogin } from '@roleypoly/design-system/templates/auth-login';
|
import { AuthLogin } from '@roleypoly/design-system/templates/auth-login';
|
||||||
|
import { GenericLoadingTemplate } from '@roleypoly/design-system/templates/generic-loading';
|
||||||
import { GuildSlug } from '@roleypoly/types';
|
import { GuildSlug } from '@roleypoly/types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useApiContext } from '../../contexts/api/ApiContext';
|
import { useApiContext } from '../../contexts/api/ApiContext';
|
||||||
import { useSessionContext } from '../../contexts/session/SessionContext';
|
import { useSessionContext } from '../../contexts/session/SessionContext';
|
||||||
import { Title } from '../../utils/metaTitle';
|
import { Title } from '../../utils/metaTitle';
|
||||||
|
|
||||||
const Login = () => {
|
const Login = (props: { path: string }) => {
|
||||||
const { apiUrl, fetch } = useApiContext();
|
const { apiUrl, fetch } = useApiContext();
|
||||||
const { isAuthenticated } = useSessionContext();
|
const { isAuthenticated } = useSessionContext();
|
||||||
// If ?r is in query, then let's render the slug page
|
// If ?r is in query, then let's render the slug page
|
||||||
|
@ -46,7 +47,7 @@ const Login = () => {
|
||||||
}, [apiUrl, fetch, isAuthenticated]);
|
}, [apiUrl, fetch, isAuthenticated]);
|
||||||
|
|
||||||
if (guildSlug === null) {
|
if (guildSlug === null) {
|
||||||
return <div>Loading...</div>;
|
return <GenericLoadingTemplate>Sending you to Discord...</GenericLoadingTemplate>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { useAppShellProps } from '../contexts/app-shell/AppShellContext';
|
||||||
import { useSessionContext } from '../contexts/session/SessionContext';
|
import { useSessionContext } from '../contexts/session/SessionContext';
|
||||||
import { Title } from '../utils/metaTitle';
|
import { Title } from '../utils/metaTitle';
|
||||||
|
|
||||||
const Landing = () => {
|
const Landing = (props: { path: string }) => {
|
||||||
const { isAuthenticated } = useSessionContext();
|
const { isAuthenticated } = useSessionContext();
|
||||||
const appShellProps = useAppShellProps();
|
const appShellProps = useAppShellProps();
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { GenericLoadingTemplate } from '@roleypoly/design-system/templates/generic-loading';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const Logout = () => {
|
const Logout = () => {
|
||||||
|
@ -7,7 +8,7 @@ const Logout = () => {
|
||||||
window.location.href = '/';
|
window.location.href = '/';
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return <div>Logging you out...</div>;
|
return <GenericLoadingTemplate>Logging you out...</GenericLoadingTemplate>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Logout;
|
export default Logout;
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
import { palette } from '@roleypoly/design-system/atoms/colors';
|
||||||
import { Link } from '@roleypoly/design-system/atoms/typography';
|
import { Link } from '@roleypoly/design-system/atoms/typography';
|
||||||
|
import { GenericLoadingTemplate } from '@roleypoly/design-system/templates/generic-loading';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useSessionContext } from '../../contexts/session/SessionContext';
|
import { useSessionContext } from '../../contexts/session/SessionContext';
|
||||||
import { Title } from '../../utils/metaTitle';
|
import { Title } from '../../utils/metaTitle';
|
||||||
|
@ -32,13 +34,15 @@ const NewSession = (props: { sessionID: string }) => {
|
||||||
)(props.sessionID);
|
)(props.sessionID);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<GenericLoadingTemplate>
|
||||||
<Title title="Logging you into Roleypoly..." />
|
<Title title="Logging you into Roleypoly..." />
|
||||||
|
<div style={{ textAlign: 'center' }}>
|
||||||
<div>Logging you into Roleypoly...</div>
|
<div>Logging you into Roleypoly...</div>
|
||||||
<div>
|
<Link style={{ color: palette.taupe400 }} href={postauthUrl}>
|
||||||
<Link href={postauthUrl}>If you aren't redirected soon, click here.</Link>
|
If you aren't redirected soon, click here.
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</GenericLoadingTemplate>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Redirect } from '@reach/router';
|
import { Redirect } from '@reach/router';
|
||||||
|
import { GenericLoadingTemplate } from '@roleypoly/design-system/templates/generic-loading';
|
||||||
import { RolePickerTemplate } from '@roleypoly/design-system/templates/role-picker';
|
import { RolePickerTemplate } from '@roleypoly/design-system/templates/role-picker';
|
||||||
import { ServerSetupTemplate } from '@roleypoly/design-system/templates/server-setup';
|
import { ServerSetupTemplate } from '@roleypoly/design-system/templates/server-setup';
|
||||||
import { PresentableGuild, RoleUpdate, UserGuildPermissions } from '@roleypoly/types';
|
import { PresentableGuild, RoleUpdate, UserGuildPermissions } from '@roleypoly/types';
|
||||||
|
@ -48,7 +49,7 @@ const Picker = (props: PickerProps) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pickerData === null) {
|
if (pickerData === null) {
|
||||||
return <div>Loading...</div>;
|
return <GenericLoadingTemplate />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pickerData === false) {
|
if (pickerData === false) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue