-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
bcfc63e
commit e1300cd
Showing
23 changed files
with
775 additions
and
0 deletions.
There are no files selected for viewing
18 changes: 18 additions & 0 deletions
18
packages/app/modules/feed/components/CreatedAtLabel/CreatedAtLabel.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { CreatedAtLabel } from './CreatedAtLabel'; |
28 changes: 28 additions & 0 deletions
28
packages/app/modules/feed/components/FavoriteButton/FavoriteButton.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { FavoriteButton } from './FavoriteButton'; |
260 changes: 260 additions & 0 deletions
260
packages/app/modules/feed/components/FeedCard/FeedCard.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,260 @@ | ||
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'; | ||
|
||
const convertersByType = { | ||
pack: feedItemPackCardConverter, | ||
}; | ||
|
||
const cardComponentsByType = { | ||
pack: PackCard, | ||
}; | ||
|
||
interface FeedCardProps { | ||
feedType: FeedType; | ||
cardType: CardType; | ||
item: FeedItem; | ||
} | ||
|
||
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, | ||
name, | ||
total_weight, | ||
is_public, | ||
favorited_by, | ||
favorites_count, | ||
owner_id, | ||
destination, | ||
createdAt, | ||
owners, | ||
duration, | ||
itemPacks, | ||
}: FeedCardProps) { | ||
console.log('CardProps:', favorited_by); | ||
const user = useAuthUser(); | ||
const { currentTheme } = useTheme(); | ||
const { addFavorite } = useAddFavorite(); | ||
const [weightUnit] = useItemWeightUnit(); | ||
const router = useRouter(); | ||
const isFavorite = | ||
type !== 'trip' && | ||
favorited_by?.some((obj) => obj?.['userId'] === user?.id && user?.id); | ||
const handleAddToFavorite = () => { | ||
if (!user) return; | ||
const data = { | ||
packId: id, | ||
userId: user.id, | ||
}; | ||
addFavorite(data); | ||
}; | ||
const truncatedName = truncateString(name, 25); | ||
const truncatedDestination = truncateString(destination, 25); | ||
const formattedWeight = convertWeight(total_weight, 'g', weightUnit); | ||
const quantity = | ||
itemPacks?.reduce( | ||
(accumulator, currentValue) => accumulator + currentValue?.item?.quantity, | ||
0, | ||
) ?? 0; | ||
let numberOfNights; | ||
if (duration) numberOfNights = JSON.parse(duration).numberOfNights; | ||
return ( | ||
<Layout> | ||
<XStack flexWrap="wrap" space="$4" padding="$4" style={{ width: '100%' }}> | ||
<Card | ||
elevate | ||
size="$4" | ||
backgroundColor={currentTheme.colors.card} | ||
borderWidth={0} // Remove border | ||
padding="$4" | ||
width="100%" | ||
height={300} | ||
> | ||
<ContextMenu.Root> | ||
<ContextMenu.Trigger> | ||
<YStack space="$4"> | ||
<Card.Header> | ||
<H2> | ||
<RLink | ||
href={type === 'pack' ? '/pack/' + id : '/trip/' + id} | ||
style={{ textDecoration: 'none' }} | ||
> | ||
<RText | ||
fontSize={18} | ||
color={currentTheme.colors.tertiaryBlue} | ||
> | ||
{truncatedName} | ||
</RText> | ||
</RLink> | ||
</H2> | ||
<XStack space="$2" alignItems="center"> | ||
{type === 'pack' && ( | ||
<XStack alignItems="center" space="$2"> | ||
<MaterialIcons | ||
name="backpack" | ||
size={24} | ||
color={currentTheme.colors.cardIconColor} | ||
/> | ||
<DuplicateIcon link={`/pack/${id}?copy=true`} /> | ||
</XStack> | ||
)} | ||
{type === 'trip' && ( | ||
<Entypo | ||
name="location-pin" | ||
size={24} | ||
color={currentTheme.colors.cardIconColor} | ||
/> | ||
)} | ||
</XStack> | ||
</Card.Header> | ||
{type === 'pack' && ( | ||
<> | ||
<Paragraph theme="alt2"> | ||
Total Weight: {formatNumber(formattedWeight)} {weightUnit} | ||
</Paragraph> | ||
<Paragraph theme="alt2"> | ||
Total Quantity: {quantity} | ||
</Paragraph> | ||
</> | ||
)} | ||
{type === 'trip' && ( | ||
<Paragraph theme="alt2">{truncatedDestination}</Paragraph> | ||
)} | ||
<Card.Footer> | ||
<YStack flex={1} justifyContent="flex-end"> | ||
<XStack | ||
justifyContent="space-between" | ||
alignItems="center" | ||
flexDirection="row" | ||
> | ||
<YStack> | ||
<RLink | ||
href={`/profile/${owner_id}`} | ||
style={{ textDecoration: 'none' }} | ||
> | ||
<RText | ||
color={currentTheme.colors.text} | ||
style={{ marginRight: 8 }} | ||
> | ||
View{' '} | ||
{owner?.username ? '@' + owner?.username : 'Owner'} | ||
</RText> | ||
</RLink> | ||
<Paragraph theme="alt2"> | ||
{formatDistanceToNow(new Date(createdAt), { | ||
addSuffix: true, | ||
})} | ||
</Paragraph> | ||
</YStack> | ||
{type === 'pack' && ( | ||
<XStack alignItems="center" space="$2"> | ||
<TouchableOpacity onPress={handleAddToFavorite}> | ||
<AntDesign | ||
name="heart" | ||
size={16} | ||
color={ | ||
isFavorite | ||
? 'red' | ||
: currentTheme.colors.cardIconColor | ||
} | ||
/> | ||
</TouchableOpacity> | ||
<RText fontSize="$2" color={currentTheme.colors.text}> | ||
{favorites_count} | ||
</RText> | ||
</XStack> | ||
)} | ||
{type === 'trip' && ( | ||
<YStack alignItems="center" space="$2"> | ||
<RText | ||
fontSize="$2" | ||
color={currentTheme.colors.tertiaryBlue} | ||
> | ||
Nights | ||
</RText> | ||
<RText | ||
fontSize="$2" | ||
color={currentTheme.colors.tertiaryBlue} | ||
> | ||
{numberOfNights} | ||
</RText> | ||
</YStack> | ||
)} | ||
</XStack> | ||
</YStack> | ||
</Card.Footer> | ||
</YStack> | ||
</ContextMenu.Trigger> | ||
<ContextMenu.Content> | ||
<ContextMenu.Item | ||
textValue={`View ${type}`} | ||
key="view" | ||
onSelect={() => { | ||
router.push(type === 'pack' ? '/pack/' + id : '/trip/' + id); | ||
}} | ||
> | ||
<ContextMenu.ItemTitle>View {type}</ContextMenu.ItemTitle> | ||
</ContextMenu.Item> | ||
<ContextMenu.Item | ||
textValue={`View owner`} | ||
key="owner" | ||
onSelect={() => { | ||
router.push(`/profile/${owner_id}`); | ||
}} | ||
> | ||
<ContextMenu.ItemTitle>View Owner</ContextMenu.ItemTitle> | ||
</ContextMenu.Item> | ||
</ContextMenu.Content> | ||
</ContextMenu.Root> | ||
</Card> | ||
</XStack> | ||
</Layout> | ||
); | ||
} | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { FeedCard } from './FeedCard'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { queryTrpc } from 'app/trpc'; | ||
|
||
export function useAddFavorite() { | ||
const utils = queryTrpc.useContext(); | ||
|
||
const mutation = queryTrpc.addToFavorite.useMutation(); | ||
|
||
// A wrapper function to abstract away the .mutate call | ||
const addFavorite = (newFavorite, userId?: string) => { | ||
mutation.mutate(newFavorite, { | ||
onSuccess: () => { | ||
// Invalidate and refetch. Update to be more specific | ||
utils.getUserFavorites.invalidate(); | ||
utils.getPublicPacks.invalidate(); | ||
utils.getPacks.invalidate(userId ? { ownerId: userId } : undefined); | ||
}, | ||
}); | ||
}; | ||
|
||
// Return the wrapper function and any other data or functions you might need | ||
return { | ||
addFavorite, | ||
...mutation, // This spreads in other properties like isLoading, isError, etc. | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { queryTrpc } from 'app/trpc'; | ||
|
||
export const useFetchUserFavorites = (userId: string) => { | ||
const enabled = !!userId; | ||
|
||
const { data, error, isLoading, refetch } = | ||
queryTrpc.getUserFavorites.useQuery( | ||
{ userId }, | ||
{ | ||
enabled, | ||
refetchOnWindowFocus: false, | ||
keepPreviousData: true, | ||
}, | ||
); | ||
|
||
return { data, error, isLoading, enabled, refetch }; | ||
}; |
Oops, something went wrong.