Skip to content

Commit

Permalink
Merge pull request #1220 from andrew-bierman/feat/card-component
Browse files Browse the repository at this point in the history
  • Loading branch information
andrew-bierman authored Sep 7, 2024
2 parents 844a027 + 298f64e commit 91f0965
Show file tree
Hide file tree
Showing 36 changed files with 579 additions and 301 deletions.
2 changes: 0 additions & 2 deletions packages/app/hooks/favorites/index.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { XStack, RText } from '@packrat/ui';
import { Clock } from '@tamagui/lucide-icons';
import React, { type FC } from 'react';

interface CreatedAtLabelProps {
date: string;
}
export const CreatedAtLabel: FC<CreatedAtLabelProps> = ({ date }) => {
return (
<XStack
style={{ marginTop: 4, alignItems: 'center', maxWidth: '100%' }}
space={4}
>
<Clock size={16} />
<RText>{date}</RText>
</XStack>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { CreatedAtLabel } from './CreatedAtLabel';
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { AntDesign } from '@expo/vector-icons';
import { RText, XStack } from '@packrat/ui';
import React, { type FC } from 'react';
import { Pressable, type GestureResponderEvent } from 'react-native';

interface FavoriteButtonProps {
isAuthUserFavorite: boolean;
onClick: (e: GestureResponderEvent) => void;
count: number;
}
export const FavoriteButton: FC<FavoriteButtonProps> = ({
onClick,
isAuthUserFavorite,
count,
}) => {
return (
<XStack style={{ alignItems: 'center' }} space="$2">
<Pressable onPress={onClick}>
<AntDesign
name="heart"
size={16}
color={isAuthUserFavorite ? 'red' : undefined}
/>
</Pressable>
<RText style={{ fontSize: 14 }}>{count}</RText>
</XStack>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { FavoriteButton } from './FavoriteButton';
Original file line number Diff line number Diff line change
@@ -1,49 +1,60 @@
import { AntDesign } from '@expo/vector-icons';
import { formatDistanceToNow } from 'date-fns';
import { MaterialIcons, Entypo } from '@expo/vector-icons';
import useTheme from 'app/hooks/useTheme';
import { TouchableOpacity } from 'react-native';
import { DuplicateIcon } from 'app/assets/icons';
import { truncateString } from 'app/utils/truncateString';
import { RLink, RText as OriginalRText, ContextMenu } from '@packrat/ui';
import { formatNumber } from 'app/utils/formatNumber';
import { useAddFavorite } from 'app/hooks/favorites';
import React, { type FC } from 'react';
import { type FeedItem, type FeedType } from 'app/modules/feed/model';
import { feedItemPackCardConverter } from './utils';
import { PackCard } from 'app/modules/pack';
import { type CardType } from '@packrat/ui';
import { useAddFavorite } from 'app/modules/feed';
import { useAuthUser } from 'app/modules/auth';
import { useRouter } from 'app/hooks/router';
import { useItemWeightUnit } from 'app/modules/item';
import { convertWeight } from 'app/utils/convertWeight';
import Layout from 'app/components/layout/Layout';
import { Button, Card, H2, Paragraph, XStack, YStack } from 'tamagui';

const RText: any = OriginalRText;

interface CardProps {
type: string;
id: string;
owner: {
id: string;
username: string;
};
name: string;
total_weight: number;
is_public: boolean;
favorited_by: Array<{
id: string;
}>;
favorites_count: number;
owner_id: string | { id: string };
destination: string;
createdAt: string;
owners: Array<{ any: any }>;
duration: string;
itemPacks?: any[];
}

interface User {
id: string;
const convertersByType = {
pack: feedItemPackCardConverter,
};

const cardComponentsByType = {
pack: PackCard,
};

interface FeedCardProps {
feedType: FeedType;
cardType: CardType;
item: FeedItem;
}

export function FeedCard({
export const FeedCard: FC<FeedCardProps> = ({ item, cardType, feedType }) => {
const { addFavorite } = useAddFavorite();
const user = useAuthUser();
const cardProps =
typeof convertersByType[feedType] === 'function'
? convertersByType[feedType](item, user?.id)
: null;

const handleAddToFavorite = () => {
if (!user) return;
const data = {
packId: item.id,
userId: user.id,
};

addFavorite(data);
};

if (!cardProps) {
return null;
}

const CardComponent = cardComponentsByType[feedType];

return (
<CardComponent
{...cardProps}
cardType={cardType}
toggleFavorite={handleAddToFavorite}
/>
);
};

/*
export function FeedCardOld({
type,
id,
owner,
Expand All @@ -58,7 +69,7 @@ export function FeedCard({
owners,
duration,
itemPacks,
}: CardProps) {
}: FeedCardProps) {
console.log('CardProps:', favorited_by);
const user = useAuthUser();
const { currentTheme } = useTheme();
Expand Down Expand Up @@ -246,3 +257,4 @@ export function FeedCard({
</Layout>
);
}
*/
1 change: 1 addition & 0 deletions packages/app/modules/feed/components/FeedCard/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { FeedCard } from './FeedCard';
40 changes: 40 additions & 0 deletions packages/app/modules/feed/components/FeedCard/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { type FeedCardProps, type FeedItem } from 'modules/feed/model';
import { formatDistanceToNowStrict } from 'date-fns';
import { type PackDetails } from 'app/modules/pack';
import { truncateString } from 'app/utils/truncateString';

type Converter<Input, Result> = (
input: Input,
currentUserId?: string | number,
) => Result;

export const feedItemPackCardConverter: Converter<
FeedItem,
Omit<FeedCardProps<PackDetails>, 'cardType' | 'toggleFavorite'>
> = (input, currentUserId) => {
return {
id: input.id,
createdAt: formatDistanceToNowStrict(new Date(input.createdAt), {
addSuffix: false,
}),
title: truncateString(input.name, 25),
ownerId:
typeof input.owner_id === 'string'
? input.owner_id
: input.owner_id?.id || '',
details: {
score: input.total_score,
weight: input.total_weight,
quantity:
input?.itemPacks?.reduce(
(accumulator, currentValue) =>
accumulator + currentValue?.item?.quantity,
0,
) ?? 0,
},
isUserFavorite: input?.userFavoritePacks?.some(
(obj) => obj?.userId === currentUserId,
),
favoriteCount: input.favorites_count,
};
};
2 changes: 2 additions & 0 deletions packages/app/modules/feed/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export { FeedCard } from './FeedCard';
export { FeedSearchFilter } from './FeedSearchFilter';
export { SearchProvider, SearchContext } from './SearchProvider';
export { FavoriteButton } from './FavoriteButton';
export { CreatedAtLabel } from './CreatedAtLabel';
2 changes: 2 additions & 0 deletions packages/app/modules/feed/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export { useFeed } from './useFeed';
export { useAddFavorite } from './useAddFavorite';
export { useFetchUserFavorites } from './useFetchUserFavorites';
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { queryTrpc } from '../../trpc';
import { queryTrpc } from 'app/trpc';

export function useAddFavorite() {
const utils = queryTrpc.useContext();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { queryTrpc } from '../../trpc';
import { queryTrpc } from 'app/trpc';

export const useFetchUserFavorites = (userId: string) => {
const enabled = !!userId;
Expand Down
11 changes: 9 additions & 2 deletions packages/app/modules/feed/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
export * from './widgets';
export * from './screens';
export { useFeed } from './hooks';
export { SearchProvider, FeedSearchFilter, FeedCard } from './components';
export { useFeed, useAddFavorite, useFetchUserFavorites } from './hooks';
export {
SearchProvider,
FeedSearchFilter,
FeedCard,
FavoriteButton,
CreatedAtLabel,
} from './components';
export { type FeedCardProps, type FeedItem } from './model';
39 changes: 39 additions & 0 deletions packages/app/modules/feed/model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { type CardType } from '@packrat/ui';

export type FeedType = 'pack';

export interface FeedItem {
id: string;
owner: {
id: string;
username: string;
};
name: string;
total_weight: number;
total_score: number;
is_public: boolean;
favorited_by: Array<{
id: string;
}>;
userFavoritePacks?: Array<{ userId: string }>;
favorites_count: number;
owner_id: string | { id: string };
destination: string;
createdAt: string;
owners: Array<{ any: any }>;
duration: string;
type: FeedType;
itemPacks?: any[];
}

export interface FeedCardProps<Details> {
id: string;
title: string;
cardType: CardType;
createdAt: string;
details: Details;
ownerId: string;
favoriteCount: number;
isUserFavorite: boolean;
toggleFavorite: () => void;
}
9 changes: 6 additions & 3 deletions packages/app/modules/feed/screens/FeedScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,16 @@ const Feed = ({ feedType = 'public' }: FeedProps) => {
<FlatList
data={filteredData}
horizontal={false}
ItemSeparatorComponent={() => (
<View style={{ height: 12, width: '100%' }} />
)}
keyExtractor={(item) => item?.id + item?.type}
renderItem={({ item }) => (
<FeedCard
key={item?.id}
type={item?.type}
favorited_by={item?.userFavoritePacks}
{...item}
item={item}
cardType="primary"
feedType={item.type}
/>
)}
ListFooterComponent={() => <View style={{ height: 50 }} />}
Expand Down
9 changes: 6 additions & 3 deletions packages/app/modules/feed/widgets/FeedPreview/FeedPreview.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React from 'react';
import Carousel from 'app/components/carousel';
import { useFeed } from '../../hooks';
import { default as FeedPreviewCard, type FeedItem } from './FeedPreviewCard';
import Loader from 'app/components/Loader';
import { FeedCard, type FeedItem } from 'app/modules/feed';
import { View } from 'tamagui';

interface FeedPreviewScrollProps {
itemWidth: number;
Expand All @@ -24,9 +25,11 @@ const FeedPreviewScroll: React.FC<FeedPreviewScrollProps> = ({
{feedData
?.filter((item): item is FeedItem => item.type !== null)
.map((item: FeedItem) => {
const linkStr = `/${item.type}/${item.id}`;
const linkStr = `/pack/${item.id}`;
return linkStr ? (
<FeedPreviewCard {...{ linkStr, item, feedType }} key={linkStr} />
<View style={{ marginBottom: 10 }}>
<FeedCard item={item} cardType="secondary" feedType="pack" />
</View>
) : null;
})}
</Carousel>
Expand Down
Loading

0 comments on commit 91f0965

Please sign in to comment.