Skip to content

Commit

Permalink
Merge branch 'main' into 62-use-bun-for-the-api
Browse files Browse the repository at this point in the history
  • Loading branch information
bnussman committed Mar 9, 2024
2 parents 996e870 + 7ad8878 commit 07c0dd4
Show file tree
Hide file tree
Showing 61 changed files with 4,182 additions and 3,810 deletions.
27 changes: 25 additions & 2 deletions api/src/admin/resolver.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import Redis from "ioredis";
import { UserRole } from "../entities/User";
import { User, UserRole } from "../entities/User";
import { REDIS_HOST, REDIS_PASSWROD } from "../utils/constants";
import { Authorized, Query, Resolver } from "type-graphql";
import { Authorized, Ctx, Query, Resolver } from "type-graphql";
import { Context } from "../utils/context";
import { sql } from "@mikro-orm/core";

@Resolver()
export class AdminResolver {
Expand All @@ -18,4 +20,25 @@ export class AdminResolver {

return channels;
}

@Query(() => [String])
@Authorized(UserRole.ADMIN)
public async getUsersWithDuplicateEmails(@Ctx() ctx: Context): Promise<string[]> {
// SELECT DISTINCT lower(email) FROM public.user
// WHERE UPPER(email) INy
// (SELECT UPPER(email) FROM public.user GROUP BY UPPER(email) HAVING COUNT(*) > 1)
//
// { ['COUNT(email)']: { $gt: 1 }}

const qb2 = ctx.em.createQueryBuilder(User).select(sql`lower(email)`).groupBy(sql`lower(email)`).having("COUNT(*) > 1");

console.log("---", qb2.getKnexQuery())
const qb1 = ctx.em.createQueryBuilder(User).select(sql`lower(email)`, true).where({ [sql`lower(email)`]: { $in: qb2.getKnexQuery() }});

const result = await qb1.execute();

console.log(result);

return [];
}
}
24 changes: 8 additions & 16 deletions app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,17 @@ import * as SplashScreen from "expo-splash-screen";
import * as Sentry from "@sentry/react-native";
import { cache, client } from "./utils/Apollo";
import { ApolloProvider, useSubscription } from "@apollo/client";
import { NativeBaseProvider, useColorMode } from "native-base";
import { colorModeManager } from "./utils/theme";
import { updatePushToken } from "./utils/Notifications";
import { UserData, UserSubscription, useUser } from "./utils/useUser";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { setUserContext } from "./utils/sentry";
import { StatusBar } from "expo-status-bar";
import { NATIVE_BASE_THEME } from "./utils/constants";
import { DarkTheme, DefaultTheme } from "@react-navigation/native";
import { setPurchaseUser, setupPurchase } from "./utils/purchase";
import { Navigation } from "./utils/Navigation";
import { useAutoUpdate } from "./utils/updates";
import { TamaguiProvider, tamaguiConfig } from "@beep/ui";
import { useColorScheme } from "react-native";

SplashScreen.preventAutoHideAsync();
Sentry.init({
Expand All @@ -29,7 +28,7 @@ Sentry.init({
setupPurchase();

function Beep() {
const { colorMode } = useColorMode();
const colorScheme = useColorScheme();
const { data, loading } = useUser({
errorPolicy: "none",
onCompleted: () => {
Expand Down Expand Up @@ -61,31 +60,24 @@ function Beep() {

return (
<>
<StatusBar style={colorMode === "dark" ? "light" : "dark"} />
<StatusBar style={colorScheme === "dark" ? "light" : "dark"} />
<Navigation
linking={{ enabled: true, prefixes: ["beep://", "https://app.ridebeep.app"] }}
theme={colorMode === "dark" ? DarkTheme : DefaultTheme}
theme={colorScheme === "dark" ? DarkTheme : DefaultTheme}
/>
</>
);
}

function App() {
const colorScheme = useColorScheme();
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<NativeBaseProvider
theme={NATIVE_BASE_THEME}
colorModeManager={colorModeManager}
config={{
dependencies: {
"linear-gradient": require("expo-linear-gradient").LinearGradient,
},
}}
>
<TamaguiProvider config={tamaguiConfig} defaultTheme={colorScheme ?? "light"}>
<ApolloProvider client={client}>
<Beep />
</ApolloProvider>
</NativeBaseProvider>
</TamaguiProvider>
</GestureHandlerRootView>
);
}
Expand Down
20 changes: 5 additions & 15 deletions app/components/AcceptDenyButton.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import React, { useEffect, useState } from "react";
import { Button, Icon } from "native-base";
import { Button, Spinner } from "@beep/ui";
import { useMutation } from "@apollo/client";
import { MaterialCommunityIcons } from "@expo/vector-icons";
import { Alert } from "../utils/Alert";
import { Unpacked } from "../utils/constants";
import { UpdateBeeperQueue } from "./ActionButton";
import { Status } from "../utils/types";
import { ResultOf } from "gql.tada";
import { GetInitialQueue } from "../routes/beep/StartBeeping";
import { Check, X } from "@tamagui/lucide-icons";

interface Props {
type: "accept" | "deny";
Expand Down Expand Up @@ -41,20 +41,10 @@ export function AcceptDenyButton(props: Props) {
return (
<Button
flexGrow={isAccept ? 1 : undefined}
colorScheme={isAccept ? "green" : "red"}
_text={{ color: "white" }}
isLoading={loading}
theme={isAccept ? "green" : "red"}
iconAfter={loading ? <Spinner /> : undefined}
onPress={onPress}
bg={isAccept ? "green.500" : "red.500"}
_pressed={{ bg: isAccept ? "green.600" : "red.600" }}
endIcon={
<Icon
as={MaterialCommunityIcons}
name={isAccept ? "check" : "close"}
size={22}
color="white"
/>
}
icon={isAccept ? <Check /> : <X />}
>
{isAccept ? "Accept" : "Deny"}
</Button>
Expand Down
10 changes: 3 additions & 7 deletions app/components/ActionButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useState } from "react";
import { Unpacked } from "../utils/constants";
import { ApolloError, useMutation } from "@apollo/client";
import { useEffect } from "react";
import { Button } from "native-base";
import { Button, Spinner } from "@beep/ui";
import { Status } from "../utils/types";
import { ResultOf, graphql } from "gql.tada";
import { GetInitialQueue } from "../routes/beep/StartBeeping";
Expand Down Expand Up @@ -48,7 +48,7 @@ export const UpdateBeeperQueue = graphql(`
}
`);

function _Button(props: Props) {
export function ActionButton(props: Props) {
const { beep } = props;

const [isLoading, setIsLoading] = useState(false);
Expand Down Expand Up @@ -90,14 +90,10 @@ function _Button(props: Props) {

return (
<Button
size="lg"
isLoading={isLoading}
iconAfter={isLoading ? <Spinner /> : undefined}
onPress={onPress}
_text={{ fontWeight: "extrabold" }}
>
{getMessage()}
</Button>
);
}

export const ActionButton = React.memo(_Button);
31 changes: 7 additions & 24 deletions app/components/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,18 @@
import React from "react";
import { Avatar as _Avatar, IAvatarProps } from "native-base";
import { ImageSourcePropType } from "react-native";
import { Avatar as _Avatar, AvatarProps } from "@beep/ui";
import AvatarImage from "../assets/avatarDark.png";
import { IAvatarBadgeProps } from "native-base/lib/typescript/components/composites/Avatar";

interface Props {
interface Props extends AvatarProps {
url: string | null | undefined;
online?: boolean;
badgeSize?: IAvatarBadgeProps["size"];
}

export function __Avatar(props: Props & IAvatarProps) {
const { url, online, badgeSize, ...rest } = props;

const source: ImageSourcePropType = url ? { uri: url } : AvatarImage;

const key = url ? url : "default";
export function Avatar(props: Props) {
const { url, ...rest } = props;

return (
<_Avatar
_image={{
defaultSource: AvatarImage,
fallbackSource: AvatarImage,
}}
{...rest}
key={key}
source={source}
>
{online && <_Avatar.Badge size={badgeSize ?? "4"} bg="green.400" />}
<_Avatar circular size="$6" {...rest}>
<_Avatar.Image src={url ?? AvatarImage} defaultSource={AvatarImage} />
<_Avatar.Fallback bc="red" />
</_Avatar>
);
}

export const Avatar = React.memo(__Avatar);
71 changes: 30 additions & 41 deletions app/components/Beep.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import React from "react";
import { useNavigation } from "@react-navigation/native";
import { Badge, HStack, Spacer, Stack, Text } from "native-base";
import { Card, XStack, Stack, Text } from "@beep/ui";
import { useUser } from "../utils/useUser";
import { Avatar } from "./Avatar";
import { Unpacked } from "../utils/constants";
import { Card } from "./Card";
import { Status } from "../utils/types";
import { ResultOf } from "gql.tada";
import { GetBeepHistory } from "../routes/Beeps";
Expand All @@ -15,14 +14,14 @@ interface Props {
}

export const beepStatusMap: Record<Status, string> = {
[Status.WAITING]: "orange.400",
[Status.ON_THE_WAY]: "orange.400",
[Status.ACCEPTED]: "green.500",
[Status.IN_PROGRESS]: "green.500",
[Status.HERE]: "green.500",
[Status.DENIED]: "red.400",
[Status.CANCELED]: "red.400",
[Status.COMPLETE]: "green.500",
[Status.WAITING]: "$orange9",
[Status.ON_THE_WAY]: "$orange9",
[Status.ACCEPTED]: "$green9",
[Status.IN_PROGRESS]: "$green9",
[Status.HERE]: "$green9",
[Status.DENIED]: "$red9",
[Status.CANCELED]: "$red9",
[Status.COMPLETE]: "$green9",
};

export function Beep({ item }: Props) {
Expand All @@ -31,57 +30,47 @@ export function Beep({ item }: Props) {
const otherUser = user?.id === item.rider.id ? item.beeper : item.rider;
const isRider = user?.id === item.rider.id;

const showBadge = [Status.CANCELED, Status.DENIED].includes(
item.status as Status
);

return (
<Card
mt={2}
mx={1}
pressable
mt="$2"
mx="$2"
p="$4"
pressTheme
hoverTheme
onPress={() =>
navigation.navigate("User", { id: otherUser.id, beepId: item.id })
}
>
<HStack alignItems="center" mb={2}>
<Avatar size={12} mr={2} url={otherUser.photo} />
<XStack alignItems="center" mb="$2">
<Avatar size="$4" mr="$2" url={otherUser.photo} />
<Stack flexShrink={1}>
<Text
fontSize="xl"
letterSpacing="sm"
fontWeight="extrabold"
isTruncated
>
<Text fontWeight="bold">
{otherUser.name}
</Text>
<Text color="gray.400" fontSize="xs" isTruncated>
<Text color="$gray10">
{`${isRider ? "Ride" : "Beep"} - ${new Date(
item.start as string
).toLocaleString()}`}
</Text>
</Stack>
<Spacer />
{showBadge && (
<Badge
variant="solid"
bg={beepStatusMap[item.status as Status]}
borderRadius="lg"
_text={{ textTransform: "capitalize", fontWeight: "bold" }}
>
{item.status}
</Badge>
)}
</HStack>
<Stack flexGrow={1} />
<Card
backgroundColor={beepStatusMap[item.status as Status]}
borderRadius="$4"
px="$1.5"
>
<Text textTransform="capitalize" color="white" fontWeight="bold" fontSize="$2">{item.status}</Text>
</Card>
</XStack>
<Stack>
<Text>
<Text bold>Group size</Text> <Text>{item.groupSize}</Text>
<Text fontWeight="bold">Group size</Text> <Text>{item.groupSize}</Text>
</Text>
<Text>
<Text bold>Pick Up</Text> <Text>{item.origin}</Text>
<Text fontWeight="bold">Pick Up</Text> <Text>{item.origin}</Text>
</Text>
<Text>
<Text bold>Drop Off</Text> <Text>{item.destination}</Text>
<Text fontWeight="bold">Drop Off</Text> <Text>{item.destination}</Text>
</Text>
</Stack>
</Card>
Expand Down
54 changes: 0 additions & 54 deletions app/components/BottomSheet.tsx

This file was deleted.

Loading

0 comments on commit 07c0dd4

Please sign in to comment.