2023-02-14 10:10:10 +01:00
|
|
|
import { useMatches } from '@remix-run/react';
|
|
|
|
|
import { useMemo } from 'react';
|
2023-02-11 03:14:14 +01:00
|
|
|
|
2023-02-14 10:10:10 +01:00
|
|
|
import type { User } from '~/models/user.server';
|
2023-02-11 03:14:14 +01:00
|
|
|
|
2023-02-19 14:07:24 +01:00
|
|
|
export const DEFAULT_COLORS = [
|
|
|
|
|
'dark',
|
|
|
|
|
'gray',
|
|
|
|
|
'red',
|
|
|
|
|
'pink',
|
|
|
|
|
'grape',
|
|
|
|
|
'violet',
|
|
|
|
|
'indigo',
|
|
|
|
|
'blue',
|
|
|
|
|
'cyan',
|
|
|
|
|
'green',
|
|
|
|
|
'lime',
|
|
|
|
|
'yellow',
|
|
|
|
|
'orange',
|
|
|
|
|
'teal'
|
|
|
|
|
];
|
|
|
|
|
export const COLORS_MAP: Record<string, string> = {
|
|
|
|
|
dark: '#25262b',
|
|
|
|
|
gray: '#868e96',
|
|
|
|
|
red: '#fa5252',
|
|
|
|
|
pink: '#e64980',
|
|
|
|
|
grape: '#be4bdb',
|
|
|
|
|
violet: '#7950f2',
|
|
|
|
|
indigo: '#4c6ef5',
|
|
|
|
|
blue: '#228be6',
|
|
|
|
|
cyan: '#15aabf',
|
|
|
|
|
green: '#12b886',
|
|
|
|
|
lime: '#40c057',
|
|
|
|
|
yellow: '#82c91e',
|
|
|
|
|
orange: '#fab005',
|
|
|
|
|
teal: '#fd7e14'
|
|
|
|
|
};
|
|
|
|
|
export const randomColorName = () =>
|
|
|
|
|
DEFAULT_COLORS[Math.floor(Math.random() * DEFAULT_COLORS.length)];
|
|
|
|
|
export const randomColor = () => {
|
|
|
|
|
const colorName = randomColorName();
|
|
|
|
|
return {
|
|
|
|
|
name: colorName,
|
|
|
|
|
hex: COLORS_MAP[colorName]
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
2023-02-14 10:10:10 +01:00
|
|
|
const DEFAULT_REDIRECT = '/';
|
2023-02-11 03:14:14 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This should be used any time the redirect path is user-provided
|
|
|
|
|
* (Like the query string on our login/signup pages). This avoids
|
|
|
|
|
* open-redirect vulnerabilities.
|
|
|
|
|
* @param {string} to The redirect destination
|
|
|
|
|
* @param {string} defaultRedirect The redirect to use if the to is unsafe.
|
|
|
|
|
*/
|
|
|
|
|
export function safeRedirect(
|
|
|
|
|
to: FormDataEntryValue | string | null | undefined,
|
|
|
|
|
defaultRedirect: string = DEFAULT_REDIRECT
|
|
|
|
|
) {
|
2023-02-14 10:10:10 +01:00
|
|
|
if (!to || typeof to !== 'string') {
|
2023-02-11 03:14:14 +01:00
|
|
|
return defaultRedirect;
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-14 10:10:10 +01:00
|
|
|
if (!to.startsWith('/') || to.startsWith('//')) {
|
2023-02-11 03:14:14 +01:00
|
|
|
return defaultRedirect;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return to;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This base hook is used in other hooks to quickly search for specific data
|
|
|
|
|
* across all loader data using useMatches.
|
|
|
|
|
* @param {string} id The route id
|
|
|
|
|
* @returns {JSON|undefined} The router data or undefined if not found
|
|
|
|
|
*/
|
|
|
|
|
export function useMatchesData(
|
|
|
|
|
id: string
|
|
|
|
|
): Record<string, unknown> | undefined {
|
|
|
|
|
const matchingRoutes = useMatches();
|
|
|
|
|
const route = useMemo(
|
|
|
|
|
() => matchingRoutes.find((route) => route.id === id),
|
|
|
|
|
[matchingRoutes, id]
|
|
|
|
|
);
|
|
|
|
|
return route?.data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isUser(user: any): user is User {
|
2023-02-14 10:10:10 +01:00
|
|
|
return user && typeof user === 'object' && typeof user.email === 'string';
|
2023-02-11 03:14:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function useOptionalUser(): User | undefined {
|
2023-02-14 10:10:10 +01:00
|
|
|
const data = useMatchesData('root');
|
2023-02-11 03:14:14 +01:00
|
|
|
if (!data || !isUser(data.user)) {
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
return data.user;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function useUser(): User {
|
|
|
|
|
const maybeUser = useOptionalUser();
|
|
|
|
|
if (!maybeUser) {
|
|
|
|
|
throw new Error(
|
2023-02-14 10:10:10 +01:00
|
|
|
'No user found in root loader, but user is required by useUser. If user is optional, try useOptionalUser instead.'
|
2023-02-11 03:14:14 +01:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return maybeUser;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function validateEmail(email: unknown): email is string {
|
2023-02-14 10:10:10 +01:00
|
|
|
return typeof email === 'string' && email.length > 3 && email.includes('@');
|
2023-02-11 03:14:14 +01:00
|
|
|
}
|