Skip to content

Commit

Permalink
things!
Browse files Browse the repository at this point in the history
  • Loading branch information
cjdenio committed Jan 30, 2024
1 parent 186daaf commit 628166b
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 98 deletions.
5 changes: 4 additions & 1 deletion src/Navigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,10 @@ export default function Navigator() {
controlsColor: palette.primary,
dismissButtonStyle: "cancel",
},
)
).then(() => {
mutate("/user/organizations");
mutate("/user/invitations");
})
}
/>
),
Expand Down
242 changes: 146 additions & 96 deletions src/pages/Receipts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,135 @@ import { useFocusEffect, useTheme } from "@react-navigation/native";
import { NativeStackScreenProps } from "@react-navigation/native-stack";
import { formatDistanceToNow } from "date-fns";
import * as ImagePicker from "expo-image-picker";
import { PropsWithChildren, useContext } from "react";
import { Alert, FlatList, Text, TouchableHighlight, View } from "react-native";
import { useContext, useState } from "react";
import {
ActivityIndicator,
Alert,
FlatList,
Text,
TouchableHighlight,
View,
} from "react-native";
import useSWR from "swr";

import AuthContext from "../auth";
import { ReceiptsStackParamList } from "../lib/NavigatorParamList";
import Organization from "../lib/types/Organization";
import { TransactionCardCharge } from "../lib/types/Transaction";
import p from "../palette";
import { palette } from "../theme";
import { renderMoney } from "../util";

function ReceiptUploadButton({
icon,
}: PropsWithChildren<{
icon: React.ComponentProps<typeof Ionicons>["name"];
}>) {
function Transaction({
transaction,
onComplete,
}: {
transaction: TransactionCardCharge & { organization: Organization };
onComplete: () => void;
}) {
const [status, requestPermission] = ImagePicker.useCameraPermissions();
const { token } = useContext(AuthContext);

const { colors: themeColors } = useTheme();

const [loading, setLoading] = useState(false);

return (
<View
style={{
backgroundColor: "#338eda",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
padding: 12,
<TouchableHighlight
underlayColor={themeColors.background}
onPress={async () => {
if (!status?.granted) {
const { granted } = await requestPermission();
if (!granted) return;
}

const result = await ImagePicker.launchCameraAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
});

if (result.canceled || result.assets.length == 0) return;
const asset = result.assets[0];

setLoading(true);

const body = new FormData();

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
body.append("file", {
uri: asset.uri,
name: asset.fileName || "yeet.jpg",
type: "image/jpeg",
});

try {
await fetch(
process.env.EXPO_PUBLIC_API_BASE +
`/organizations/${transaction.organization.id}/transactions/${transaction.id}/receipts`,
{
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
},
body,
},
);
} catch (e) {
Alert.alert("Something went wrong.");
} finally {
setLoading(false);
}

onComplete();

Alert.alert("Receipt uploaded!");
}}
>
<Ionicons size={26} color={themeColors.text} name={icon} />
{/* <Text style={{ color: themeColors.text, fontSize: 12 }}>Upload</Text> */}
</View>
<View
style={{
marginBottom: 16,
borderRadius: 8,
overflow: "hidden",
backgroundColor: themeColors.card,
flexDirection: "row",
alignItems: "stretch",
}}
>
<View style={{ flex: 1, padding: 10, gap: 8 }}>
<Text style={{ color: themeColors.text }}>{transaction.memo}</Text>
<View style={{ flexDirection: "row", gap: 4 }}>
<Text style={{ color: palette.muted }}>
{renderMoney(Math.abs(transaction.amount_cents))}
</Text>
<Text style={{ color: palette.muted }}>&middot;</Text>
<Text style={{ color: palette.muted }}>
{formatDistanceToNow(new Date(transaction.card_charge.spent_at))}{" "}
ago
</Text>
</View>
</View>
<View
style={{
backgroundColor: loading ? p.sky["600"] : p.sky["500"],
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
padding: 12,
width: 60,
}}
>
{loading ? (
<ActivityIndicator color={themeColors.text} />
) : (
<Ionicons
size={26}
color={themeColors.text}
name="camera-outline"
/>
)}
</View>
</View>
</TouchableHighlight>
);
}

Expand All @@ -43,96 +141,48 @@ type Props = NativeStackScreenProps<
>;

export default function ReceiptsPage({ navigation: _navigation }: Props) {
const { data, mutate } = useSWR<{
const { data, mutate, isLoading } = useSWR<{
data: (TransactionCardCharge & { organization: Organization })[];
}>("/user/transactions/missing_receipt");

const [status, requestPermission] = ImagePicker.useCameraPermissions();
const { token } = useContext(AuthContext);

const { colors: themeColors } = useTheme();

useFocusEffect(() => {
mutate();
});

if (isLoading) {
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<ActivityIndicator />
</View>
);
}

if (data?.data?.length == 0) {
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<View style={{ marginBottom: 10 }}>
<Ionicons name="receipt-outline" color={palette.muted} size={60} />
<Ionicons
name="checkmark"
color={p.emerald["400"]}
size={30}
style={{
position: "absolute",
top: -15,
left: -15,
}}
/>
</View>
<Text style={{ color: palette.muted }}>No receipts to upload.</Text>
</View>
);
}

return (
<FlatList
data={data?.data || []}
renderItem={({ item }) => (
<TouchableHighlight
underlayColor={themeColors.background}
onPress={async () => {
if (!status?.granted) {
const { granted } = await requestPermission();
if (!granted) return;
}

const result = await ImagePicker.launchCameraAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
});

if (result.canceled || result.assets.length == 0) return;
const asset = result.assets[0];

const body = new FormData();

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
body.append("file", {
uri: asset.uri,
name: asset.fileName || "yeet.jpg",
type: "image/jpeg",
});

try {
await fetch(
process.env.EXPO_PUBLIC_API_BASE +
`/organizations/${item.organization.id}/transactions/${item.id}/receipts`,
{
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
},
body,
},
);
} catch (e) {
Alert.alert("Something went wrong.");
}

await mutate();

Alert.alert("Receipt uploaded!");
}}
>
<View
style={{
marginBottom: 16,
borderRadius: 8,
overflow: "hidden",
backgroundColor: themeColors.card,
flexDirection: "row",
alignItems: "stretch",
}}
>
<View style={{ flex: 1, padding: 10, gap: 8 }}>
<Text style={{ color: themeColors.text }}>{item.memo}</Text>
<View style={{ flexDirection: "row", gap: 4 }}>
<Text style={{ color: palette.muted }}>
{renderMoney(Math.abs(item.amount_cents))}
</Text>
<Text style={{ color: palette.muted }}>&middot;</Text>
<Text style={{ color: palette.muted }}>
{formatDistanceToNow(new Date(item.card_charge.spent_at))} ago
</Text>
</View>
</View>
<ReceiptUploadButton icon="camera-outline">
Take photo
</ReceiptUploadButton>
</View>
</TouchableHighlight>
<Transaction transaction={item} onComplete={() => mutate()} />
)}
contentContainerStyle={{ padding: 20 }}
/>
Expand Down
3 changes: 2 additions & 1 deletion src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,8 @@ export default function App({ navigation }: Props) {
invitations.length > 0 && (
<View
style={{
marginVertical: 20,
marginTop: 10,
marginBottom: 20,
borderRadius: 10,
}}
>
Expand Down

0 comments on commit 628166b

Please sign in to comment.