Skip to content

Commit

Permalink
Merge pull request #29 from simonyiszk/5-list-favorite-events
Browse files Browse the repository at this point in the history
5 list favorite events
  • Loading branch information
berenteb authored Feb 10, 2024
2 parents ecca094 + f3c5a90 commit ed8b2f7
Show file tree
Hide file tree
Showing 11 changed files with 130 additions and 45 deletions.
23 changes: 23 additions & 0 deletions app/(tabs)/presentation/favorite-presentations.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';

import { Screen } from '../../../components/base/screen';
import { ErrorMessage } from '../../../components/common/error-message';
import { Header } from '../../../components/common/header';
import { Title } from '../../../components/common/title';
import { PresentationItemSkeletonList } from '../../../components/schedule/layouts/presentation-item-skeleton-list';
import { PresentationList } from '../../../components/schedule/layouts/presentation-list';
import { useFavoritePresentationsList } from '../../../hooks/use-favorite-presentations-list';

export default function FavoritePresentationsScreen() {
const { data, isLoading, isError } = useFavoritePresentationsList();
return (
<Screen>
<Header>
<Title>Kedvenc előadásaim</Title>
</Header>
{isLoading && <PresentationItemSkeletonList />}
{!isError && !isLoading && <PresentationList presentations={data ?? []} />}
{isError && <ErrorMessage>Nem sikerült betölteni az előadásokat</ErrorMessage>}
</Screen>
);
}
7 changes: 7 additions & 0 deletions app/(tabs)/presentation/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Link } from 'expo-router';
import React from 'react';

import { Screen } from '../../../components/base/screen';
import { ErrorMessage } from '../../../components/common/error-message';
import { Header } from '../../../components/common/header';
import { StyledButton } from '../../../components/common/styled-button';
import { Title } from '../../../components/common/title';
import { PresentationItemSkeletonList } from '../../../components/schedule/layouts/presentation-item-skeleton-list';
import { PresentationList } from '../../../components/schedule/layouts/presentation-list';
Expand All @@ -15,6 +17,11 @@ export default function PresentationListPage() {
<Header>
<Title>Programterv</Title>
</Header>
<Link href='/presentation/favorite-presentations' asChild>
<StyledButton variant='outline' rightIcon='arrow-right' className='mt-4 mx-5'>
Kedvenceim
</StyledButton>
</Link>
{isLoading && <PresentationItemSkeletonList />}
{!isError && !isLoading && <PresentationList presentations={data?.presentations ?? []} />}
{isError && <ErrorMessage>Nem sikerült betölteni az előadásokat</ErrorMessage>}
Expand Down
23 changes: 3 additions & 20 deletions components/base/item-card.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,12 @@
import { useState } from 'react';
import { GestureResponderEvent, Pressable, PressableProps } from 'react-native';
import { Pressable, PressableProps } from 'react-native';

import { cn } from '../../utils/common.utils';

export function ItemCard({ className, onPressIn, onPressOut, ...props }: PressableProps) {
const [isPressed, setIsPressed] = useState(false);

const handlePressIn = (event: GestureResponderEvent) => {
setIsPressed(true);
onPressIn?.(event);
};

const handlePressOut = (event: GestureResponderEvent) => {
setIsPressed(false);
onPressOut?.(event);
};
export function ItemCard({ className, ...props }: PressableProps) {
return (
<Pressable
onPressIn={handlePressIn}
onPressOut={handlePressOut}
className={cn(
'mb-5 rounded-xl bg-white p-3 shadow-md shadow-slate-500/10 relative overflow-hidden',
{
'bg-slate-50': isPressed,
},
'mb-5 rounded-xl bg-white active:bg-slate-50 p-3 shadow-md shadow-slate-500/10 relative overflow-hidden',
className
)}
{...props}
Expand Down
20 changes: 14 additions & 6 deletions components/common/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,28 @@ import { Feather } from '@expo/vector-icons';
import { useNavigation } from 'expo-router';
import { Pressable, View, ViewProps } from 'react-native';

import { colors } from '../../theme/colors';
import { cn } from '../../utils/common.utils';
import { Separator } from './separator';

interface HeaderProps extends ViewProps {}
interface HeaderProps extends ViewProps {
corner?: React.ReactNode;
}

export function Header({ children, className, ...props }: HeaderProps) {
export function Header({ children, className, corner, ...props }: HeaderProps) {
const navigation = useNavigation();
const showBackButton = navigation.canGoBack();
return (
<View className={cn('space-y-5 mx-5', className)} {...props}>
{showBackButton && (
<Pressable onPress={navigation.goBack}>
<Feather name='arrow-left' size={30} color='#d45b7e' />
</Pressable>
{(showBackButton || corner) && (
<View className='flex flex-row items-center justify-between'>
{showBackButton && (
<Pressable onPress={navigation.goBack}>
<Feather name='arrow-left' size={30} color={colors.primary['500']} />
</Pressable>
)}
{corner}
</View>
)}
{children}
<Separator className='my-0' />
Expand Down
57 changes: 57 additions & 0 deletions components/common/styled-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Feather } from '@expo/vector-icons';
import { forwardRef } from 'react';
import { Pressable, PressableProps, View } from 'react-native';

import { colors } from '../../theme/colors';
import { cn } from '../../utils/common.utils';
import { StyledText } from '../base/text';

type ButtonVariant = 'primary' | 'outline';

const buttonStyles: Record<ButtonVariant, string> = {
primary: 'bg-primary-500 active:bg-primary-600',
outline: 'bg-transparent border-2 border-primary-500 active:bg-primary-100',
};

const textStyles: Record<ButtonVariant, string> = {
primary: 'text-white',
outline: 'text-primary-500',
};

interface StyledButtonProps extends Omit<PressableProps, 'children'> {
children: string;
leftIcon?: React.ComponentProps<typeof Feather>['name'];
rightIcon?: React.ComponentProps<typeof Feather>['name'];
variant?: keyof typeof buttonStyles;
}

const StyledButton = forwardRef<View, StyledButtonProps>(
({ children, variant = 'primary', leftIcon, rightIcon, className, ...props }, ref) => {
return (
<Pressable
className={cn(
buttonStyles[variant],
'rounded-md',
'px-4',
'py-2',
'shadow-md items-center justify-center flex-row space-x-2',
className
)}
ref={ref}
{...props}
>
{leftIcon && (
<Feather name={leftIcon} size={24} color={variant === 'primary' ? 'white' : colors.primary['500']} />
)}
<StyledText className={cn(textStyles[variant], 'font-raleway-bold text-center text-lg')}>{children}</StyledText>
{rightIcon && (
<Feather name={rightIcon} size={24} color={variant === 'primary' ? 'white' : colors.primary['500']} />
)}
</Pressable>
);
}
);

StyledButton.displayName = 'StyledButton';

export { StyledButton };
7 changes: 2 additions & 5 deletions components/schedule/elements/favorite-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,8 @@ export function FavoriteButton({ presentation }: FavoriteButtonProps) {
};

return (
<Pressable
className='absolute right-5 bottom-36 shadow-md shadow-slate-500/30 p-5 bg-white rounded-xl'
onPress={onPress}
>
<AntDesign name={isFavorite ? 'star' : 'staro'} color={isFavorite ? 'orange' : 'gray'} size={30} />
<Pressable onPress={onPress}>
<AntDesign name={isFavorite ? 'star' : 'staro'} color={isFavorite ? '#eab308' : 'gray'} size={30} />
</Pressable>
);
}
5 changes: 4 additions & 1 deletion components/schedule/elements/presentation-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Image, PressableProps, View } from 'react-native';
import { NativeStackNavigationProp } from 'react-native-screens/native-stack';

import { useFavoritePresentations } from '../../../contexts/favorite-presentations.context';
import { ConferenceService } from '../../../services/conference.service';
import { PresentationDto } from '../../../types/conference-api.type';
import { cn } from '../../../utils/common.utils';
import { isPresentationPast } from '../../../utils/presentation.utils';
Expand All @@ -20,6 +21,8 @@ export function PresentationItem({ presentation, className, ...props }: Presenta
const router = useNavigation<NativeStackNavigationProp<{ 'presentation-details': { id: string } }>>();
const isPast = isPresentationPast(presentation);
const isFavorite = isFavoritePresentation(presentation.slug);
const startTime = ConferenceService.getFormattedTimestamp(presentation.startTime);
const endTime = ConferenceService.getFormattedTimestamp(presentation.endTime);
const onPress = () => {
router.navigate('presentation-details', { id: presentation.slug });
};
Expand All @@ -46,7 +49,7 @@ export function PresentationItem({ presentation, className, ...props }: Presenta
</StyledText>
<StyledText className='text-slate-500' numberOfLines={1}>
{' '}
{presentation.startTime} - {presentation.endTime}
{startTime} - {endTime}
</StyledText>
</View>
</View>
Expand Down
9 changes: 6 additions & 3 deletions components/schedule/layouts/presentation-details-page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { usePresentation } from '../../../hooks/use-presentation';
import { ConferenceService } from '../../../services/conference.service';
import { Screen } from '../../base/screen';
import { ScrollContent } from '../../base/scroll-content';
import { StyledText } from '../../base/text';
Expand All @@ -15,24 +16,26 @@ interface ScheduleDetailsPageProps {

export function PresentationDetailsPage({ slug }: ScheduleDetailsPageProps) {
const { data, isLoading } = usePresentation(slug);
const startTime = ConferenceService.getFormattedTimestamp(data?.startTime ?? '');
const endTime = ConferenceService.getFormattedTimestamp(data?.endTime ?? '');
return (
<Screen>
<Header>
<Header corner={data ? <FavoriteButton presentation={data} /> : undefined}>
{isLoading && <SkeletonTitle />}
{data && (
<>
<Title>{data?.title}</Title>
<Subtitle>
{data.room}{data.startTime} - {data.endTime}
{data.room}{startTime} - {endTime}
</Subtitle>
<Subtitle>{data.presenter.name}</Subtitle>
</>
)}
</Header>
<ScrollContent>
{isLoading && <SkeletonParagraph />}
{data && <StyledText className='text-xl'>{data.description}</StyledText>}
</ScrollContent>
{data && <FavoriteButton presentation={data} />}
</Screen>
);
}
9 changes: 9 additions & 0 deletions hooks/use-favorite-presentations-list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { useFavoritePresentations } from '../contexts/favorite-presentations.context';
import { useConference } from './use-conference';

export function useFavoritePresentationsList() {
const { data, ...rest } = useConference();
const { isFavoritePresentation } = useFavoritePresentations();
const items = data?.presentations.filter((item) => isFavoritePresentation(item.slug));
return { data: items, ...rest };
}
11 changes: 1 addition & 10 deletions services/conference.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ export class ConferenceService {
static async getConferenceData(): Promise<FullConferenceDto> {
const response = await axiosInstance.get<FullConferenceDto>('/api/conference/index');
response.data.presentations = ConferenceService.sortPresentationsByStartDate(response.data);
response.data.presentations = ConferenceService.formatTimestamps(response.data);
return response.data;
}

Expand All @@ -25,15 +24,7 @@ export class ConferenceService {
});
}

private static formatTimestamps(conference: FullConferenceDto) {
return conference.presentations.map((presentation) => {
presentation.startTime = ConferenceService.getFormattedTimestamp(presentation.startTime);
presentation.endTime = ConferenceService.getFormattedTimestamp(presentation.endTime);
return presentation;
});
}

private static getFormattedTimestamp(timestamp: string) {
static getFormattedTimestamp(timestamp: string) {
try {
return format(new Date(timestamp), 'HH:mm');
} catch {
Expand Down
4 changes: 4 additions & 0 deletions services/notification.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ export class NotificationService {

static async scheduleEventNotification(presentation: PresentationDto) {
await this.registerForPushNotifications();
if (!presentation.startTime) {
console.log('Event start time is not defined, not scheduling notification');
return;
}
if (!this.notificationEnabled) {
console.log('Notification permission not granted, not scheduling notification');
return;
Expand Down

0 comments on commit ed8b2f7

Please sign in to comment.