mirror of
https://github.com/roleypoly/roleypoly.git
synced 2025-04-24 19:39: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 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 styled from 'styled-components';
|
||||
|
||||
|
@ -33,6 +34,22 @@ const DotOverlayLight = styled(dotOverlayBase)`
|
|||
);
|
||||
`;
|
||||
|
||||
export const DotOverlay = ({ light }: { light?: boolean }) => {
|
||||
return light ? <DotOverlayLight /> : <DotOverlayDark />;
|
||||
const DotOverlaySkeleton = styled(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 { user } from '../../fixtures/storyData';
|
||||
import { UserAvatarGroup } from './UserAvatarGroup';
|
||||
import { UserAvatarGroupSkeleton } from './UserAvatarGroupSkeleton';
|
||||
|
||||
export default {
|
||||
title: 'Molecules/User Avatar Group',
|
||||
|
@ -17,3 +18,9 @@ export const Default = (args) => (
|
|||
<UserAvatarGroup {...args} />
|
||||
</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 './UserAvatarGroupSkeleton';
|
||||
|
|
|
@ -15,13 +15,35 @@ export type AppShellProps = {
|
|||
guilds?: GuildSlug[];
|
||||
recentGuilds?: string[];
|
||||
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) => (
|
||||
<>
|
||||
<GlobalStyles />
|
||||
<GlobalStyleColors />
|
||||
{props.user ? (
|
||||
{props.skeleton ? (
|
||||
<Masthead.Skeleton />
|
||||
) : props.user ? (
|
||||
<Masthead.Authed
|
||||
disableGuildPicker={props.disableGuildPicker}
|
||||
guilds={props.guilds || []}
|
||||
|
@ -32,13 +54,11 @@ export const AppShell = (props: AppShellProps) => (
|
|||
) : (
|
||||
<Masthead.Guest />
|
||||
)}
|
||||
<Scrollbars
|
||||
style={{ height: 'calc(100vh - 25px)', margin: 0, padding: 0 }}
|
||||
autoHide
|
||||
universal
|
||||
>
|
||||
<Content small={props.small}>{props.children}</Content>
|
||||
{props.showFooter && <Footer />}
|
||||
</Scrollbars>
|
||||
<OptionallyScroll shouldScroll={!props.skeleton}>
|
||||
<>
|
||||
<Content small={props.small}>{props.children}</Content>
|
||||
{props.showFooter && <Footer />}
|
||||
</>
|
||||
</OptionallyScroll>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -2,15 +2,20 @@ import * as React from 'react';
|
|||
import { guild, mastheadSlugs, user } from '../../fixtures/storyData';
|
||||
import { Authed } from './Authed';
|
||||
import { Guest } from './Guest';
|
||||
import { Skeleton } from './Skeleton';
|
||||
|
||||
export default {
|
||||
title: 'Organisms/Masthead',
|
||||
};
|
||||
|
||||
export const HasGuilds = () => (
|
||||
<Authed guilds={mastheadSlugs} activeGuildId={guild.id} user={user} />
|
||||
export const hasGuilds = () => (
|
||||
<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 './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 { GenericLoadingTemplate } from '@roleypoly/design-system/templates/generic-loading';
|
||||
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 PickerPage = React.lazy(() => import('../pages/picker'));
|
||||
|
||||
const AuthLogin = React.lazy(() => import('../pages/auth/login'));
|
||||
const MachineryNewSession = React.lazy(() => import('../pages/machinery/new-session'));
|
||||
const MachineryLogout = React.lazy(() => import('../pages/machinery/logout'));
|
||||
const MachineryBotJoin = React.lazy(() => import('../pages/machinery/bot-join'));
|
||||
|
@ -18,7 +19,7 @@ const RouteWrapper = (props: {
|
|||
path?: string;
|
||||
default?: boolean;
|
||||
}) => (
|
||||
<React.Suspense fallback={<div>Loading...</div>}>
|
||||
<React.Suspense fallback={<GenericLoadingTemplate />}>
|
||||
<props.component {...props} />
|
||||
</React.Suspense>
|
||||
);
|
||||
|
@ -26,7 +27,7 @@ const RouteWrapper = (props: {
|
|||
export const AppRouter = () => {
|
||||
return (
|
||||
<Router>
|
||||
<RouteWrapper component={LandingPage} path="/" />
|
||||
<LandingPage path="/" />
|
||||
<RouteWrapper component={ServersPage} path="/servers" />
|
||||
<RouteWrapper component={PickerPage} path="/s/:serverID" />
|
||||
|
||||
|
@ -37,7 +38,7 @@ export const AppRouter = () => {
|
|||
<RouteWrapper component={MachineryLogout} path="/machinery/logout" />
|
||||
<RouteWrapper component={MachineryBotJoin} path="/machinery/bot-join" />
|
||||
<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={DevToolsSessionDebug} path="/x/dev-tools/session-debug" />
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import { redirectTo } from '@reach/router';
|
||||
import { AuthLogin } from '@roleypoly/design-system/templates/auth-login';
|
||||
import { GenericLoadingTemplate } from '@roleypoly/design-system/templates/generic-loading';
|
||||
import { GuildSlug } from '@roleypoly/types';
|
||||
import React from 'react';
|
||||
import { useApiContext } from '../../contexts/api/ApiContext';
|
||||
import { useSessionContext } from '../../contexts/session/SessionContext';
|
||||
import { Title } from '../../utils/metaTitle';
|
||||
|
||||
const Login = () => {
|
||||
const Login = (props: { path: string }) => {
|
||||
const { apiUrl, fetch } = useApiContext();
|
||||
const { isAuthenticated } = useSessionContext();
|
||||
// If ?r is in query, then let's render the slug page
|
||||
|
@ -46,7 +47,7 @@ const Login = () => {
|
|||
}, [apiUrl, fetch, isAuthenticated]);
|
||||
|
||||
if (guildSlug === null) {
|
||||
return <div>Loading...</div>;
|
||||
return <GenericLoadingTemplate>Sending you to Discord...</GenericLoadingTemplate>;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -5,7 +5,7 @@ import { useAppShellProps } from '../contexts/app-shell/AppShellContext';
|
|||
import { useSessionContext } from '../contexts/session/SessionContext';
|
||||
import { Title } from '../utils/metaTitle';
|
||||
|
||||
const Landing = () => {
|
||||
const Landing = (props: { path: string }) => {
|
||||
const { isAuthenticated } = useSessionContext();
|
||||
const appShellProps = useAppShellProps();
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { GenericLoadingTemplate } from '@roleypoly/design-system/templates/generic-loading';
|
||||
import React from 'react';
|
||||
|
||||
const Logout = () => {
|
||||
|
@ -7,7 +8,7 @@ const Logout = () => {
|
|||
window.location.href = '/';
|
||||
}, []);
|
||||
|
||||
return <div>Logging you out...</div>;
|
||||
return <GenericLoadingTemplate>Logging you out...</GenericLoadingTemplate>;
|
||||
};
|
||||
|
||||
export default Logout;
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { palette } from '@roleypoly/design-system/atoms/colors';
|
||||
import { Link } from '@roleypoly/design-system/atoms/typography';
|
||||
import { GenericLoadingTemplate } from '@roleypoly/design-system/templates/generic-loading';
|
||||
import * as React from 'react';
|
||||
import { useSessionContext } from '../../contexts/session/SessionContext';
|
||||
import { Title } from '../../utils/metaTitle';
|
||||
|
@ -32,13 +34,15 @@ const NewSession = (props: { sessionID: string }) => {
|
|||
)(props.sessionID);
|
||||
|
||||
return (
|
||||
<>
|
||||
<GenericLoadingTemplate>
|
||||
<Title title="Logging you into Roleypoly..." />
|
||||
<div>Logging you into Roleypoly...</div>
|
||||
<div>
|
||||
<Link href={postauthUrl}>If you aren't redirected soon, click here.</Link>
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<div>Logging you into Roleypoly...</div>
|
||||
<Link style={{ color: palette.taupe400 }} href={postauthUrl}>
|
||||
If you aren't redirected soon, click here.
|
||||
</Link>
|
||||
</div>
|
||||
</>
|
||||
</GenericLoadingTemplate>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Redirect } from '@reach/router';
|
||||
import { GenericLoadingTemplate } from '@roleypoly/design-system/templates/generic-loading';
|
||||
import { RolePickerTemplate } from '@roleypoly/design-system/templates/role-picker';
|
||||
import { ServerSetupTemplate } from '@roleypoly/design-system/templates/server-setup';
|
||||
import { PresentableGuild, RoleUpdate, UserGuildPermissions } from '@roleypoly/types';
|
||||
|
@ -48,7 +49,7 @@ const Picker = (props: PickerProps) => {
|
|||
}
|
||||
|
||||
if (pickerData === null) {
|
||||
return <div>Loading...</div>;
|
||||
return <GenericLoadingTemplate />;
|
||||
}
|
||||
|
||||
if (pickerData === false) {
|
||||
|
|
Loading…
Add table
Reference in a new issue