Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions apps/expo/src/app/(auth)/Loader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ActivityIndicator } from 'react-native';

interface Status {
loading: boolean;
// TODO(@zkirby)
// isError: boolean;
// isSuccess: boolean;
}

const Loader = ({
status,
className,
children,
}: React.PropsWithChildren<{
status: Status;
className?: string;
}>) => {
if (status.loading) return <ActivityIndicator className={className} />;
return children;
};

export default Loader;
35 changes: 35 additions & 0 deletions apps/expo/src/app/(auth)/UserLoader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useEffect } from 'react';
import { api } from '../../utils/api';
import Loader from './Loader';

/**
* This component is responsible for ensuring that the user *in our DB*
* is created. This is different from (but 1:1 with) the Clerk user.
*
* It's important that this mounts high in the component tree and
* prevents any children that might make API calls from rendering
* since almost every API call relies on the User being created
* in our DB.
*
* TODO(@zkirby): Think up a better solution here. We're essentially blocking
* the entire app from rendering for no reason (other than when the user is first created).
* Likely, we're not using Clerk correctly or under utilizing some functionality.
*/
const UserLoader = ({ children }: React.PropsWithChildren) => {
const ensureUserIsCreated = api.user.create.useMutation();

useEffect(() => {
ensureUserIsCreated.mutate();
}, []);

return (
// NOTE(@zkirby): Keep spinning until we're sure the user is created,
// if we use 'isFetching' here, we'll render sub-tree once until the
// api calls kicks off.
<Loader status={{ loading: !ensureUserIsCreated.isSuccess }}>
{children}
</Loader>
);
};

export default UserLoader;
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ const AlertListItem = ({
onSelfAssign,
}: {
alert: Alert;
user: User;
user?: User;
onAck: () => void;
onClose: () => void;
onReopen: () => void;
onSelfAssign: () => void;
}) => {
const { status, title, createdAt, summary } = alert;
const initials =
(user.firstName?.slice(0, 1) ?? '') + (user.lastName?.slice(0, 1) ?? '');
(user?.firstName?.slice(0, 1) ?? '') + (user?.lastName?.slice(0, 1) ?? '');

const StatusColors = StatusToColor[status as keyof typeof StatusToColor];
const { LeftSwipe, RightSwipe, action } = createSwipeAnimations(status, {
Expand Down
13 changes: 6 additions & 7 deletions apps/expo/src/app/(tabs)/(alerts)/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { SafeAreaView } from 'react-native-safe-area-context';

import { Stack } from 'expo-router';
import { api } from '~/utils/api';
import { useUser } from '../../hooks/useUser';
import AlertListItem from './_components/AlertListItem';
import type { Alert } from './alerts.types';
import { useSearch } from './hooks/useSearch';
Expand All @@ -33,7 +32,7 @@ const AlertListPage = () => {
},
],
});
const user = useUser();
const user = api.user.me.useQuery();
const updateAlert = api.alert.update.useMutation();

const update = useCallback(async (id: string, alert: Partial<Alert>) => {
Expand All @@ -42,8 +41,8 @@ const AlertListPage = () => {
}, []);

const isLoading = useMemo(
() => updateAlert.isPending || alerts.isFetching,
[alerts.isFetching, updateAlert.isPending],
() => updateAlert.isPending || alerts.isFetching || user.isFetching,
[alerts.isFetching, updateAlert.isPending, user.isFetching],
);

return (
Expand All @@ -68,7 +67,7 @@ const AlertListPage = () => {
ALERTS
</Text>
<View className="bg-white h-screen">
{isLoading || !user ? (
{isLoading ? (
<ActivityIndicator />
) : (
<FlatList
Expand All @@ -77,12 +76,12 @@ const AlertListPage = () => {
renderItem={({ item }) => (
<AlertListItem
alert={item}
user={user}
user={user.data?.user}
onAck={() => update(item.id, { status: 'ACKED' })}
onReopen={() => update(item.id, { status: 'OPEN' })}
onClose={() => update(item.id, { status: 'CLOSED' })}
onSelfAssign={() =>
update(item.id, { assignedToId: user.id })
update(item.id, { assignedToId: user.data?.user?.id })
}
/>
)}
Expand Down
9 changes: 7 additions & 2 deletions apps/expo/src/app/(tabs)/(profile)/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { useAuth } from '@clerk/clerk-expo';
import { Stack } from 'expo-router';
import { Button, Text, View } from 'react-native';

import { useUser } from '../../hooks/useUser';
import { useMemo } from 'react';
import { api } from '../../../utils/api';

const SignOut = () => {
const { isLoaded, signOut } = useAuth();
Expand All @@ -23,7 +24,11 @@ const SignOut = () => {
};

const ProfilePage = () => {
const user = useUser();
const apiUser = api.user.me.useQuery();

const user = useMemo(() => {
return apiUser.data?.user;
}, [apiUser.data]);

return (
<View className="relative h-full bg-white">
Expand Down
8 changes: 4 additions & 4 deletions apps/expo/src/app/(tabs)/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import FeatherIcons from '@expo/vector-icons/Feather';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Text, View } from 'react-native';

import { api } from '../../utils/api';
import { useNotification } from '../hooks/useNotification';
import { useUser } from '../hooks/useUser';
import AlertListPage from './(alerts)';
import ProfilePage from './(profile)';

const Tab = createBottomTabNavigator();

const TabsLayout = () => {
useNotification();
const user = useUser();
const user = api.user.me.useQuery();

return (
<Tab.Navigator
Expand Down Expand Up @@ -43,8 +43,8 @@ const TabsLayout = () => {
options={{
tabBarIcon: ({ size }) => {
const initials =
(user?.firstName?.charAt(0) ?? '') +
(user?.lastName?.charAt(0) ?? '');
(user.data?.user?.firstName?.charAt(0) ?? '') +
(user.data?.user?.lastName?.charAt(0) ?? '');
return (
<View
style={{ height: size, width: size, borderRadius: 100 }}
Expand Down
11 changes: 7 additions & 4 deletions apps/expo/src/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { SignedIn, SignedOut } from '@clerk/clerk-react';
import Constants from 'expo-constants';

import SignInWithOAuth from './(auth)/SignInWithOAuth';
import UserLoader from './(auth)/UserLoader';

const tokenCache = {
async getToken(key: string) {
Expand All @@ -38,10 +39,12 @@ const RootLayout = () => {
>
<SignedIn>
<TRPCProvider>
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
</Stack>
<StatusBar />
<UserLoader>
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
</Stack>
<StatusBar />
</UserLoader>
</TRPCProvider>
</SignedIn>
<SignedOut>
Expand Down
21 changes: 0 additions & 21 deletions apps/expo/src/app/hooks/useUser.ts

This file was deleted.

5 changes: 2 additions & 3 deletions apps/nextjs/src/app/_components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,12 @@ const Button = ({
onClick,
disabled,
children,
}: {
}: React.PropsWithChildren<{
type?: ButtonType;
className?: string;
onClick?: () => void | Promise<void>;
disabled?: boolean;
children: React.ReactNode | React.ReactNode[];
}) => (
}>) => (
<button
disabled={disabled}
onClick={onClick}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ const Styles = {
export const WeeklyEvent = ({
days: cols,
children,
}: {
}: React.PropsWithChildren<{
days: number;
children: React.ReactNode[] | React.ReactNode;
}) => {
}>) => {
return (
<div className={classNames(`col-span-${cols} h-[90px]`, Styles.rowBg)}>
{children}
Expand All @@ -28,7 +27,7 @@ export const WeeklyCalendar = ({
totalDays,
daysBeforeToday = 0,
children,
}: {
}: React.PropsWithChildren<{
/**
* The total number of days to show in the calendar
*/
Expand All @@ -39,8 +38,7 @@ export const WeeklyCalendar = ({
* then the cal would be | today - 3 | today - 2 | today - 1 | today | today + 1 | ...
*/
daysBeforeToday?: number;
children: React.ReactNode[] | React.ReactNode;
}) => {
}>) => {
const today = new Date();
const days = Array.from({ length: totalDays }, (_, i) => {
const date = addDays(today, i - daysBeforeToday);
Expand Down
7 changes: 3 additions & 4 deletions apps/nextjs/src/app/_components/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Menu, Transition } from '@headlessui/react';
import classNames from 'classnames';
import type { ReactElement, ReactNode } from 'react';
import type { ReactElement } from 'react';
import React, { Fragment } from 'react';

const Dropdown = ({
Expand All @@ -10,14 +10,13 @@ const Dropdown = ({
position = 'left',
size = 'md',
noHighlight = false,
}: {
children: ReactElement | ReactNode[];
}: React.PropsWithChildren<{
OpenButton: ReactElement;
openButtonClass?: string;
position?: 'left' | 'right';
size?: 'sm' | 'md' | 'lg';
noHighlight?: boolean;
}) => {
}>) => {
return (
<Menu as="div" className="relative inline-block">
<Menu.Button className={openButtonClass}>{OpenButton}</Menu.Button>
Expand Down
5 changes: 2 additions & 3 deletions apps/nextjs/src/app/_components/Loader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,10 @@ const Loader = ({
status,
className,
children,
}: {
}: React.PropsWithChildren<{
status: Status;
className?: string;
children: React.ReactNode | React.ReactNode[];
}) => {
}>) => {
if (status.loading) return <Spinner className={className} />;
return children;
};
Expand Down
5 changes: 2 additions & 3 deletions apps/nextjs/src/app/_components/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@ const Modal = ({
onClose,
title,
children,
}: {
}: React.PropsWithChildren<{
open: boolean;
onClose: () => void;
title: string;
children: React.ReactElement | React.ReactElement[];
}) => {
}>) => {
return (
<Transition appear show={open} as={Fragment}>
<Dialog as="div" className="relative z-10" onClose={onClose}>
Expand Down
3 changes: 1 addition & 2 deletions apps/nextjs/src/app/_components/Pill.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { ReactNode } from 'react';
import React from 'react';

const Pill = ({ children }: { children: ReactNode }) => {
const Pill = ({ children }: React.PropsWithChildren) => {
const childArray = React.Children.toArray(children);
const tLength = childArray.length - 1;

Expand Down
9 changes: 4 additions & 5 deletions apps/nextjs/src/app/_components/SideNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import classNames from 'classnames';
import Image from 'next/image';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import type { ReactNode } from 'react';
import { FaGithub, FaSlack } from 'react-icons/fa';
import { HiOutlineMail } from 'react-icons/hi';
import {
Expand All @@ -15,7 +14,7 @@ import {
TbUsersGroup,
} from 'react-icons/tb';

import { useUser } from '../../hooks/useUser';
import { api } from '../../utils/api';
import Dropdown from './Dropdown';
import UserIcon from './UserIcon';

Expand Down Expand Up @@ -47,9 +46,9 @@ const NavItem = ({
/**
* Sidebar navigation
*/
const SideNav = ({ children }: { children: ReactNode }) => {
const SideNav = ({ children }: React.PropsWithChildren) => {
const pathname = usePathname();
const user = useUser();
const user = api.user.me.useQuery();

return (
<div className="flex">
Expand All @@ -68,7 +67,7 @@ const SideNav = ({ children }: { children: ReactNode }) => {
</div>
<Dropdown
position="right"
OpenButton={<UserIcon {...user} />}
OpenButton={<UserIcon {...(user.data?.user ?? {})} />}
openButtonClass="flex"
>
{/* TODO(@zkirby): Enable dark mode */}
Expand Down
Loading