From b9c09fb0084fd3dabe356620682513ff795c0e0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Berente?= <30603208+berenteb@users.noreply.github.com> Date: Wed, 31 Jan 2024 23:20:57 +0100 Subject: [PATCH 1/9] feat: added real api endpoint and renamed schedule to presentations --- .env.example | 1 + .gitignore | 2 + app.config.ts | 16 ++++ app/(tabs)/_layout.tsx | 2 +- app/(tabs)/home/index.tsx | 8 +- app/(tabs)/home/presentation-details.tsx | 7 ++ app/(tabs)/home/schedule-details.tsx | 7 -- .../{schedule => presentation}/_layout.tsx | 0 app/(tabs)/presentation/index.tsx | 19 ++++ .../presentation/presentation-details.tsx | 7 ++ app/(tabs)/schedule/index.tsx | 19 ---- app/(tabs)/schedule/schedule-details.tsx | 7 -- app/_layout.tsx | 6 +- components/schedule/favorite-button.tsx | 16 ++-- ...page.tsx => presentation-details-page.tsx} | 16 ++-- ...chedule-item.tsx => presentation-item.tsx} | 36 ++++---- components/schedule/presentation-list.tsx | 36 ++++++++ ....tsx => presentation-status-indicator.tsx} | 14 +-- components/schedule/schedule-list.tsx | 32 ------- config/axios.config.ts | 7 ++ config/env.config.ts | 3 + contexts/favorite-events.context.tsx | 64 ------------- contexts/favorite-presentations.context.tsx | 64 +++++++++++++ hooks/use-conference.ts | 10 +++ hooks/use-presentation.ts | 7 ++ hooks/use-schedule-item.ts | 7 -- hooks/use-schedule.ts | 21 ----- mocks/schedules.ts | 50 ----------- package.json | 3 + services/conference.service.ts | 9 ++ services/favorite-event.service.ts | 12 +-- services/notification.service.ts | 14 +-- types/conference-api.type.ts | 89 +++++++++++++++++++ types/favorite-event.type.ts | 4 +- types/schedule-event.type.ts | 10 --- utils/common.utils.ts | 5 ++ utils/presentation.utils.ts | 22 +++++ utils/schedule.utils.ts | 22 ----- yarn.lock | 38 ++++++++ 39 files changed, 409 insertions(+), 303 deletions(-) create mode 100644 .env.example create mode 100644 app.config.ts create mode 100644 app/(tabs)/home/presentation-details.tsx delete mode 100644 app/(tabs)/home/schedule-details.tsx rename app/(tabs)/{schedule => presentation}/_layout.tsx (100%) create mode 100644 app/(tabs)/presentation/index.tsx create mode 100644 app/(tabs)/presentation/presentation-details.tsx delete mode 100644 app/(tabs)/schedule/index.tsx delete mode 100644 app/(tabs)/schedule/schedule-details.tsx rename components/schedule/{schedule-details-page.tsx => presentation-details-page.tsx} (58%) rename components/schedule/{schedule-item.tsx => presentation-item.tsx} (52%) create mode 100644 components/schedule/presentation-list.tsx rename components/schedule/{schedule-status-indicator.tsx => presentation-status-indicator.tsx} (50%) delete mode 100644 components/schedule/schedule-list.tsx create mode 100644 config/axios.config.ts create mode 100644 config/env.config.ts delete mode 100644 contexts/favorite-events.context.tsx create mode 100644 contexts/favorite-presentations.context.tsx create mode 100644 hooks/use-conference.ts create mode 100644 hooks/use-presentation.ts delete mode 100644 hooks/use-schedule-item.ts delete mode 100644 hooks/use-schedule.ts delete mode 100644 mocks/schedules.ts create mode 100644 services/conference.service.ts create mode 100644 types/conference-api.type.ts delete mode 100644 types/schedule-event.type.ts create mode 100644 utils/presentation.utils.ts delete mode 100644 utils/schedule.utils.ts diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..593846b --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +API_BASE_URL= \ No newline at end of file diff --git a/.gitignore b/.gitignore index f61deaa..35eb96e 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,5 @@ ios .idea .idea/workspace.xml +.env + diff --git a/app.config.ts b/app.config.ts new file mode 100644 index 0000000..0a5576e --- /dev/null +++ b/app.config.ts @@ -0,0 +1,16 @@ +import { ConfigContext } from '@expo/config'; +import { config } from 'dotenv'; +import * as env from 'env-var'; +config(); + +const API_BASE_URL = env.get('API_BASE_URL').required().asString(); + +export default ({ config }: ConfigContext) => { + return { + ...config, + extra: { + apiBaseUrl: API_BASE_URL, + ...config.extra, + }, + }; +}; diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx index 5933916..ed87186 100644 --- a/app/(tabs)/_layout.tsx +++ b/app/(tabs)/_layout.tsx @@ -45,7 +45,7 @@ export default function TabsLayout() { }} /> , diff --git a/app/(tabs)/home/index.tsx b/app/(tabs)/home/index.tsx index e39c24b..50cedbe 100644 --- a/app/(tabs)/home/index.tsx +++ b/app/(tabs)/home/index.tsx @@ -6,14 +6,14 @@ import { Header } from '../../../components/common/header'; import { SectionTitle } from '../../../components/common/sectiontitle'; import { Title } from '../../../components/common/title'; import { NewsList } from '../../../components/news/news-list'; -import { ScheduleList } from '../../../components/schedule/schedule-list'; -import { useSchedule } from '../../../hooks/use-schedule'; +import { PresentationList } from '../../../components/schedule/presentation-list'; +import { useConference } from '../../../hooks/use-conference'; import { news } from '../../../mocks/news'; interface HomePageProps {} export default function HomePage({}: HomePageProps) { - const { data } = useSchedule(); + const { data } = useConference(); return ( @@ -21,7 +21,7 @@ export default function HomePage({}: HomePageProps) { Simonyi Konferencia Előadások - + Hírek diff --git a/app/(tabs)/home/presentation-details.tsx b/app/(tabs)/home/presentation-details.tsx new file mode 100644 index 0000000..e6e73f6 --- /dev/null +++ b/app/(tabs)/home/presentation-details.tsx @@ -0,0 +1,7 @@ +import { PresentationDetailsPage } from '../../../components/schedule/presentation-details-page'; +import { useSafeId } from '../../../utils/common.utils'; + +export default function PresentationDetails() { + const slug = useSafeId(); + return ; +} diff --git a/app/(tabs)/home/schedule-details.tsx b/app/(tabs)/home/schedule-details.tsx deleted file mode 100644 index 7bb3a5e..0000000 --- a/app/(tabs)/home/schedule-details.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { ScheduleDetailsPage } from '../../../components/schedule/schedule-details-page'; -import { useSafeId } from '../../../utils/common.utils'; - -export default function ScheduleEventDetails() { - const id = useSafeId(); - return ; -} diff --git a/app/(tabs)/schedule/_layout.tsx b/app/(tabs)/presentation/_layout.tsx similarity index 100% rename from app/(tabs)/schedule/_layout.tsx rename to app/(tabs)/presentation/_layout.tsx diff --git a/app/(tabs)/presentation/index.tsx b/app/(tabs)/presentation/index.tsx new file mode 100644 index 0000000..684a2a7 --- /dev/null +++ b/app/(tabs)/presentation/index.tsx @@ -0,0 +1,19 @@ +import { Screen } from '../../../components/base/screen'; +import { Header } from '../../../components/common/header'; +import { Title } from '../../../components/common/title'; +import { PresentationList } from '../../../components/schedule/presentation-list'; +import { useConference } from '../../../hooks/use-conference'; + +interface PresentationListPageProps {} + +export default function PresentationListPage({}: PresentationListPageProps) { + const { data } = useConference(); + return ( + +
+ Programterv +
+ +
+ ); +} diff --git a/app/(tabs)/presentation/presentation-details.tsx b/app/(tabs)/presentation/presentation-details.tsx new file mode 100644 index 0000000..8c80929 --- /dev/null +++ b/app/(tabs)/presentation/presentation-details.tsx @@ -0,0 +1,7 @@ +import { PresentationDetailsPage } from '../../../components/schedule/presentation-details-page'; +import { useSafeId } from '../../../utils/common.utils'; + +export default function ScheduleEventDetails() { + const slug = useSafeId(); + return ; +} diff --git a/app/(tabs)/schedule/index.tsx b/app/(tabs)/schedule/index.tsx deleted file mode 100644 index f49cf79..0000000 --- a/app/(tabs)/schedule/index.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Screen } from '../../../components/base/screen'; -import { Header } from '../../../components/common/header'; -import { Title } from '../../../components/common/title'; -import { ScheduleList } from '../../../components/schedule/schedule-list'; -import { useSchedule } from '../../../hooks/use-schedule'; - -interface SchedulePageProps {} - -export default function SchedulePage({}: SchedulePageProps) { - const { data } = useSchedule(); - return ( - -
- Programterv -
- -
- ); -} diff --git a/app/(tabs)/schedule/schedule-details.tsx b/app/(tabs)/schedule/schedule-details.tsx deleted file mode 100644 index 7bb3a5e..0000000 --- a/app/(tabs)/schedule/schedule-details.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { ScheduleDetailsPage } from '../../../components/schedule/schedule-details-page'; -import { useSafeId } from '../../../utils/common.utils'; - -export default function ScheduleEventDetails() { - const id = useSafeId(); - return ; -} diff --git a/app/_layout.tsx b/app/_layout.tsx index 631508f..939123c 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -5,7 +5,7 @@ import { useEffect } from 'react'; import { View } from 'react-native'; import { queryClient } from '../config/query-client.config'; -import { FavoriteEventsProvider } from '../contexts/favorite-events.context'; +import { FavoritePresentationsProvider } from '../contexts/favorite-presentations.context'; export default function MainLayout() { const [loaded, error] = useFonts({ @@ -26,11 +26,11 @@ export default function MainLayout() { return ( - + - + ); } diff --git a/components/schedule/favorite-button.tsx b/components/schedule/favorite-button.tsx index 096c206..c50769f 100644 --- a/components/schedule/favorite-button.tsx +++ b/components/schedule/favorite-button.tsx @@ -2,23 +2,23 @@ import { AntDesign } from '@expo/vector-icons'; import { useMemo } from 'react'; import { Pressable } from 'react-native'; -import { useFavoriteEvents } from '../../contexts/favorite-events.context'; -import { ScheduleEvent } from '../../types/schedule-event.type'; +import { useFavoritePresentations } from '../../contexts/favorite-presentations.context'; +import { PresentationDto } from '../../types/conference-api.type'; interface FavoriteButtonProps { - event: ScheduleEvent; + presentation: PresentationDto; } -export function FavoriteButton({ event }: FavoriteButtonProps) { - const { isFavoriteEvent, addFavoriteEvent, removeFavoriteEvent } = useFavoriteEvents(); +export function FavoriteButton({ presentation }: FavoriteButtonProps) { + const { isFavoritePresentation, addFavoritePresentation, removeFavoritePresentation } = useFavoritePresentations(); - const isFavorite = useMemo(() => isFavoriteEvent(event.id), [event, isFavoriteEvent]); + const isFavorite = useMemo(() => isFavoritePresentation(presentation.slug), [presentation, isFavoritePresentation]); const onPress = () => { if (isFavorite) { - removeFavoriteEvent(event.id); + removeFavoritePresentation(presentation.slug); } else { - addFavoriteEvent(event); + addFavoritePresentation(presentation); } }; diff --git a/components/schedule/schedule-details-page.tsx b/components/schedule/presentation-details-page.tsx similarity index 58% rename from components/schedule/schedule-details-page.tsx rename to components/schedule/presentation-details-page.tsx index 1e2fd8d..2a849db 100644 --- a/components/schedule/schedule-details-page.tsx +++ b/components/schedule/presentation-details-page.tsx @@ -1,6 +1,6 @@ import { format } from 'date-fns'; -import { useScheduleItem } from '../../hooks/use-schedule-item'; +import { usePresentation } from '../../hooks/use-presentation'; import { Screen } from '../base/screen'; import { StyledText } from '../base/text'; import { Header } from '../common/header'; @@ -9,24 +9,24 @@ import { Title } from '../common/title'; import { FavoriteButton } from './favorite-button'; interface ScheduleDetailsPageProps { - id: string; + slug: string; } -export function ScheduleDetailsPage({ id }: ScheduleDetailsPageProps) { - const { data } = useScheduleItem(id); +export function PresentationDetailsPage({ slug }: ScheduleDetailsPageProps) { + const { data } = usePresentation(slug); if (!data) return <>; - const startDate = format(new Date(data.start), 'HH:mm'); - const endDate = format(new Date(data.end), 'HH:mm'); + const startDate = format(new Date(data.startTime), 'HH:mm'); + const endDate = format(new Date(data.endTime), 'HH:mm'); return (
{data?.title} - {data?.location} • {startDate} - {endDate} + {data?.room} • {startDate} - {endDate}
{data?.description} - +
); } diff --git a/components/schedule/schedule-item.tsx b/components/schedule/presentation-item.tsx similarity index 52% rename from components/schedule/schedule-item.tsx rename to components/schedule/presentation-item.tsx index e3284aa..2326d27 100644 --- a/components/schedule/schedule-item.tsx +++ b/components/schedule/presentation-item.tsx @@ -4,27 +4,27 @@ import { useState } from 'react'; import { Image, Pressable, View } from 'react-native'; import { NativeStackNavigationProp } from 'react-native-screens/native-stack'; -import { useFavoriteEvents } from '../../contexts/favorite-events.context'; -import { ScheduleEvent } from '../../types/schedule-event.type'; +import { useFavoritePresentations } from '../../contexts/favorite-presentations.context'; +import { PresentationDto } from '../../types/conference-api.type'; import { cn } from '../../utils/common.utils'; -import { isScheduleEventPast } from '../../utils/schedule.utils'; +import { isPresentationPast } from '../../utils/presentation.utils'; import { StyledText } from '../base/text'; -import { ScheduleStatusIndicator } from './schedule-status-indicator'; +import { PresentationStatusIndicator } from './presentation-status-indicator'; -interface ScheduleItem { - event: ScheduleEvent; +interface PresentationItemProps { + presentation: PresentationDto; } -export function ScheduleItem({ event }: ScheduleItem) { - const { isFavoriteEvent } = useFavoriteEvents(); - const router = useNavigation>(); +export function PresentationItem({ presentation }: PresentationItemProps) { + const { isFavoritePresentation } = useFavoritePresentations(); + const router = useNavigation>(); const [isPressed, setIsPressed] = useState(false); - const startTime = format(new Date(event.start), 'HH:mm'); - const endTime = format(new Date(event.end), 'HH:mm'); - const isPast = isScheduleEventPast(event); - const isFavorite = isFavoriteEvent(event.id); + const startTime = format(new Date(presentation.startTime), 'HH:mm'); + const endTime = format(new Date(presentation.endTime), 'HH:mm'); + const isPast = isPresentationPast(presentation); + const isFavorite = isFavoritePresentation(presentation.slug); const onPress = () => { - router.navigate('schedule-details', { id: event.id }); + router.navigate('presentation-details', { id: presentation.slug }); }; return ( - + - {event.title} + {presentation.title} - {event.presenter} + {presentation.presenter.name} {' '} @@ -52,7 +52,7 @@ export function ScheduleItem({ event }: ScheduleItem) { - + ); } diff --git a/components/schedule/presentation-list.tsx b/components/schedule/presentation-list.tsx new file mode 100644 index 0000000..91aa63c --- /dev/null +++ b/components/schedule/presentation-list.tsx @@ -0,0 +1,36 @@ +import { useMemo } from 'react'; +import { FlatList } from 'react-native'; + +import { PresentationDto } from '../../types/conference-api.type'; +import { isPresentationCurrent, isPresentationUpcoming } from '../../utils/presentation.utils'; +import { StyledText } from '../base/text'; +import { PresentationItem } from './presentation-item'; + +interface PresentationListProps { + presentations: PresentationDto[]; + filterToCurrent?: boolean; + filterToUpcoming?: boolean; +} + +export function PresentationList({ presentations, filterToCurrent, filterToUpcoming }: PresentationListProps) { + const filteredPresentations = useMemo( + () => + presentations.filter( + (event) => + !(filterToCurrent || filterToUpcoming) || + (filterToUpcoming && isPresentationUpcoming(event)) || + (filterToCurrent && isPresentationCurrent(event)) + ), + [presentations, filterToCurrent, filterToUpcoming] + ); + if (filteredPresentations.length === 0) { + return Nincs megjeleníthető előadás.; + } + return ( + } + /> + ); +} diff --git a/components/schedule/schedule-status-indicator.tsx b/components/schedule/presentation-status-indicator.tsx similarity index 50% rename from components/schedule/schedule-status-indicator.tsx rename to components/schedule/presentation-status-indicator.tsx index 1d18144..6974767 100644 --- a/components/schedule/schedule-status-indicator.tsx +++ b/components/schedule/presentation-status-indicator.tsx @@ -1,16 +1,16 @@ import { View } from 'react-native'; -import { ScheduleEvent } from '../../types/schedule-event.type'; +import { PresentationDto } from '../../types/conference-api.type'; import { cn } from '../../utils/common.utils'; -import { isScheduleEventCurrent, isScheduleEventUpcoming } from '../../utils/schedule.utils'; +import { isPresentationCurrent, isPresentationUpcoming } from '../../utils/presentation.utils'; -interface ScheduleStatusIndicatorProps { - event: ScheduleEvent; +interface PresentationStatusIndicatorProps { + presentation: PresentationDto; } -export function ScheduleStatusIndicator({ event }: ScheduleStatusIndicatorProps) { - const isCurrent = isScheduleEventCurrent(event); - const isUpcoming = isScheduleEventUpcoming(event); +export function PresentationStatusIndicator({ presentation }: PresentationStatusIndicatorProps) { + const isCurrent = isPresentationCurrent(presentation); + const isUpcoming = isPresentationUpcoming(presentation); return ( - schedule.filter( - (event) => - !(filterToCurrent || filterToUpcoming) || - (filterToUpcoming && isScheduleEventUpcoming(event)) || - (filterToCurrent && isScheduleEventCurrent(event)) - ), - [schedule, filterToCurrent, filterToUpcoming] - ); - return ( - } - /> - ); -} diff --git a/config/axios.config.ts b/config/axios.config.ts new file mode 100644 index 0000000..2fee5da --- /dev/null +++ b/config/axios.config.ts @@ -0,0 +1,7 @@ +import axios from 'axios'; + +import { API_BASE_URL } from './env.config'; + +export const axiosInstance = axios.create({ + baseURL: API_BASE_URL, +}); diff --git a/config/env.config.ts b/config/env.config.ts new file mode 100644 index 0000000..1801f34 --- /dev/null +++ b/config/env.config.ts @@ -0,0 +1,3 @@ +import Constants from 'expo-constants'; + +export const API_BASE_URL = Constants.expoConfig?.extra?.apiBaseUrl; diff --git a/contexts/favorite-events.context.tsx b/contexts/favorite-events.context.tsx deleted file mode 100644 index b67b84e..0000000 --- a/contexts/favorite-events.context.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { createContext, PropsWithChildren, useContext, useEffect, useState } from 'react'; - -import { FavoriteEventStorageService } from '../services/favorite-event.service'; -import { NotificationService } from '../services/notification.service'; -import { FavoriteEvent } from '../types/favorite-event.type'; -import { ScheduleEvent } from '../types/schedule-event.type'; - -type FavoriteEventsContextType = - | { - favoriteEvents: FavoriteEvent[]; - addFavoriteEvent: (event: ScheduleEvent) => void; - removeFavoriteEvent: (eventId: string) => void; - isFavoriteEvent: (eventId: string) => boolean; - } - | undefined; - -const FavoriteEventsContext = createContext(undefined); - -export function FavoriteEventsProvider({ children }: PropsWithChildren) { - const [favoriteEvents, setFavoriteEvents] = useState([]); - - async function addFavoriteEvent(event: ScheduleEvent) { - const notificationId = await NotificationService.scheduleEventNotification(event); - const favoriteEvent = { eventId: event.id, notificationId }; - setFavoriteEvents((prev) => [...prev, favoriteEvent]); - FavoriteEventStorageService.addFavoriteEvent(favoriteEvent); - } - - function removeFavoriteEvent(eventId: string) { - const favoriteEvent = favoriteEvents.find((item) => item.eventId === eventId); - setFavoriteEvents((prev) => prev.filter((item) => item.eventId !== eventId)); - FavoriteEventStorageService.removeFavoriteEvent(eventId); - NotificationService.removeScheduledNotification(favoriteEvent?.notificationId); - } - - function isFavoriteEvent(eventId: string) { - return favoriteEvents.some((item) => item.eventId === eventId); - } - - useEffect(() => { - FavoriteEventStorageService.listFavoriteEvents().then(setFavoriteEvents); - }, []); - - return ( - - {children} - - ); -} - -export function useFavoriteEvents() { - const context = useContext(FavoriteEventsContext); - if (context === undefined) { - throw new Error('useFavoriteEvents must be used within a FavoriteEventsProvider'); - } - return context; -} diff --git a/contexts/favorite-presentations.context.tsx b/contexts/favorite-presentations.context.tsx new file mode 100644 index 0000000..d56f3c8 --- /dev/null +++ b/contexts/favorite-presentations.context.tsx @@ -0,0 +1,64 @@ +import { createContext, PropsWithChildren, useContext, useEffect, useState } from 'react'; + +import { FavoriteEventStorageService } from '../services/favorite-event.service'; +import { NotificationService } from '../services/notification.service'; +import { PresentationDto } from '../types/conference-api.type'; +import { FavoritePresentation } from '../types/favorite-event.type'; + +type FavoritePresentationsContextType = + | { + favoritePresentations: FavoritePresentation[]; + addFavoritePresentation: (presentation: PresentationDto) => void; + removeFavoritePresentation: (presentationSlug: string) => void; + isFavoritePresentation: (presentationSlug: string) => boolean; + } + | undefined; + +const FavoritePresentationsContext = createContext(undefined); + +export function FavoritePresentationsProvider({ children }: PropsWithChildren) { + const [favoritePresentations, setFavoritePresentations] = useState([]); + + async function addFavoritePresentation(presentation: PresentationDto) { + const notificationId = await NotificationService.scheduleEventNotification(presentation); + const favoritePresentation: FavoritePresentation = { slug: presentation.slug, notificationId }; + setFavoritePresentations((prev) => [...prev, favoritePresentation]); + FavoriteEventStorageService.addFavoriteEvent(favoritePresentation); + } + + function removeFavoritePresentation(eventId: string) { + const favoriteEvent = favoritePresentations.find((item) => item.slug === eventId); + setFavoritePresentations((prev) => prev.filter((item) => item.slug !== eventId)); + FavoriteEventStorageService.removeFavoriteEvent(eventId); + NotificationService.removeScheduledNotification(favoriteEvent?.notificationId); + } + + function isFavoritePresentation(eventId: string) { + return favoritePresentations.some((item) => item.slug === eventId); + } + + useEffect(() => { + FavoriteEventStorageService.listFavoriteEvents().then(setFavoritePresentations); + }, []); + + return ( + + {children} + + ); +} + +export function useFavoritePresentations() { + const context = useContext(FavoritePresentationsContext); + if (context === undefined) { + throw new Error('useFavoritePresentations must be used within a FavoriteEventsProvider'); + } + return context; +} diff --git a/hooks/use-conference.ts b/hooks/use-conference.ts new file mode 100644 index 0000000..7d97abb --- /dev/null +++ b/hooks/use-conference.ts @@ -0,0 +1,10 @@ +import { useQuery } from '@tanstack/react-query'; + +import { ConferenceService } from '../services/conference.service'; + +export function useConference() { + return useQuery({ + queryKey: ['conference'], + queryFn: ConferenceService.getConferenceData, + }); +} diff --git a/hooks/use-presentation.ts b/hooks/use-presentation.ts new file mode 100644 index 0000000..5b81211 --- /dev/null +++ b/hooks/use-presentation.ts @@ -0,0 +1,7 @@ +import { useConference } from './use-conference'; + +export function usePresentation(slug: string) { + const { data, ...rest } = useConference(); + const item = data?.presentations.find((item) => item.slug === slug); + return { data: item, ...rest }; +} diff --git a/hooks/use-schedule-item.ts b/hooks/use-schedule-item.ts deleted file mode 100644 index bd5fc03..0000000 --- a/hooks/use-schedule-item.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { useSchedule } from './use-schedule'; - -export function useScheduleItem(id: string) { - const { data, ...rest } = useSchedule(); - const item = data?.find((item) => item.id === id); - return { data: item, ...rest }; -} diff --git a/hooks/use-schedule.ts b/hooks/use-schedule.ts deleted file mode 100644 index 538bf5e..0000000 --- a/hooks/use-schedule.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; - -import { schedule } from '../mocks/schedules'; -import { ScheduleEvent } from '../types/schedule-event.type'; - -export function useSchedule() { - return useQuery({ - queryKey: ['schedule'], - queryFn: getSchedule, - }); -} - -const delay = 0; - -function getSchedule(): Promise { - return new Promise((resolve) => { - setTimeout(() => { - resolve(schedule); - }, delay); - }); -} diff --git a/mocks/schedules.ts b/mocks/schedules.ts deleted file mode 100644 index f4f6a12..0000000 --- a/mocks/schedules.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { addMinutes, subMinutes } from 'date-fns'; - -import { ScheduleEvent } from '../types/schedule-event.type'; - -export const schedule: ScheduleEvent[] = [ - { - id: '1', - title: 'Korábbi esemény valamilyen hosszú címmel', - presenter: 'Valamilyen Előadó Hosszú Névvel Ráadásul', - presenterImage: 'https://picsum.photos/200', - location: 'IB028', - start: subMinutes(new Date(), 15).toISOString(), - end: subMinutes(new Date(), 0).toISOString(), - description: - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla euismod, nisl eget aliquam ultrices, nunc nisl aliquet nunc, vitae aliquam nisl', - }, - { - id: '2', - title: 'Jelenlegi esemény valamilyen hosszú címmel', - presenter: 'Valamilyen Előadó Hosszú Névvel Ráadásul', - presenterImage: 'https://picsum.photos/200', - location: 'IB028', - start: new Date().toISOString(), - end: addMinutes(new Date(), 15).toISOString(), - description: - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla euismod, nisl eget aliquam ultrices, nunc nisl aliquet nunc, vitae aliquam nisl', - }, - { - id: '3', - title: 'Következő esemény valamilyen hosszú címmel', - presenter: 'Valamilyen Előadó Hosszú Névvel Ráadásul', - presenterImage: 'https://picsum.photos/200', - location: 'IB028', - start: addMinutes(new Date(), 15).toISOString(), - end: addMinutes(new Date(), 30).toISOString(), - description: - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla euismod, nisl eget aliquam ultrices, nunc nisl aliquet nunc, vitae aliquam nisl', - }, - { - id: '4', - title: 'Későbbi esemény valamilyen hosszú címmel', - presenter: 'Valamilyen Előadó Hosszú Névvel Ráadásul', - presenterImage: 'https://picsum.photos/200', - location: 'IB028', - start: addMinutes(new Date(), 30).toISOString(), - end: addMinutes(new Date(), 45).toISOString(), - description: - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla euismod, nisl eget aliquam ultrices, nunc nisl aliquet nunc, vitae aliquam nisl', - }, -]; diff --git a/package.json b/package.json index c67aa52..d7bd1d0 100644 --- a/package.json +++ b/package.json @@ -17,8 +17,11 @@ "@react-native-async-storage/async-storage": "1.18.2", "@react-navigation/elements": "^1.3.21", "@tanstack/react-query": "^5.17.15", + "axios": "^1.6.7", "clsx": "^2.1.0", "date-fns": "^3.2.0", + "dotenv": "^16.4.1", + "env-var": "^7.4.1", "expo": "~49.0.21", "expo-constants": "~14.4.2", "expo-device": "~5.4.0", diff --git a/services/conference.service.ts b/services/conference.service.ts new file mode 100644 index 0000000..8f6ef1b --- /dev/null +++ b/services/conference.service.ts @@ -0,0 +1,9 @@ +import { axiosInstance } from '../config/axios.config'; +import { FullConferenceDto } from '../types/conference-api.type'; + +export class ConferenceService { + static async getConferenceData(): Promise { + const response = await axiosInstance.get('/api/conference/index'); + return response.data; + } +} diff --git a/services/favorite-event.service.ts b/services/favorite-event.service.ts index dbbca5a..19650e0 100644 --- a/services/favorite-event.service.ts +++ b/services/favorite-event.service.ts @@ -1,9 +1,9 @@ import AsyncStorage from '@react-native-async-storage/async-storage'; -import { FavoriteEvent } from '../types/favorite-event.type'; +import { FavoritePresentation } from '../types/favorite-event.type'; export class FavoriteEventStorageService { - static async listFavoriteEvents(): Promise { + static async listFavoriteEvents(): Promise { try { const favoriteEvents = await AsyncStorage.getItem('favoriteEvents'); if (!favoriteEvents) { @@ -17,7 +17,7 @@ export class FavoriteEventStorageService { } } - static async addFavoriteEvent(favorite: FavoriteEvent): Promise { + static async addFavoriteEvent(favorite: FavoritePresentation): Promise { try { const favoriteEvents = await this.listFavoriteEvents(); const newFavoriteEvents = [...favoriteEvents, favorite]; @@ -30,7 +30,7 @@ export class FavoriteEventStorageService { static async removeFavoriteEvent(eventId: string): Promise { try { const favoriteEvents = await this.listFavoriteEvents(); - const newFavoriteEvents = favoriteEvents.filter((favoriteEvent) => favoriteEvent.eventId !== eventId); + const newFavoriteEvents = favoriteEvents.filter((favoriteEvent) => favoriteEvent.slug !== eventId); await this.saveFavoriteEvents(newFavoriteEvents); } catch (error) { console.log(error); @@ -40,14 +40,14 @@ export class FavoriteEventStorageService { static async isFavoriteEvent(eventId: string): Promise { try { const favoriteEvents = await this.listFavoriteEvents(); - return favoriteEvents.some((favoriteEvent) => favoriteEvent.eventId === eventId); + return favoriteEvents.some((favoriteEvent) => favoriteEvent.slug === eventId); } catch (error) { console.log(error); return false; } } - private static async saveFavoriteEvents(favoriteEvents: FavoriteEvent[]): Promise { + private static async saveFavoriteEvents(favoriteEvents: FavoritePresentation[]): Promise { try { await AsyncStorage.setItem('favoriteEvents', JSON.stringify(favoriteEvents)); } catch (error) { diff --git a/services/notification.service.ts b/services/notification.service.ts index 6327227..e2efbdf 100644 --- a/services/notification.service.ts +++ b/services/notification.service.ts @@ -2,7 +2,7 @@ import { isBefore } from 'date-fns'; import * as Notifications from 'expo-notifications'; import { Platform } from 'react-native'; -import { ScheduleEvent } from '../types/schedule-event.type'; +import { PresentationDto } from '../types/conference-api.type'; export class NotificationService { static notificationEnabled = false; @@ -23,24 +23,24 @@ export class NotificationService { } } - static async scheduleEventNotification(event: ScheduleEvent) { + static async scheduleEventNotification(presentation: PresentationDto) { await this.registerForPushNotifications(); if (!this.notificationEnabled) { console.log('Notification permission not granted, not scheduling notification'); return; } - if (isBefore(new Date(event.start), new Date())) { + if (isBefore(new Date(presentation.startTime), new Date())) { console.log('Event is in the past, not scheduling notification'); return; } - const triggerDate = new Date(event.start); + const triggerDate = new Date(presentation.startTime); return await Notifications.scheduleNotificationAsync({ content: { - title: event.title, - body: `Hamarosan kezdődik a(z) ${event.location} teremben!`, - data: { tab: 'schedule', screen: 'schedule-details', id: event.id }, + title: presentation.title, + body: `Hamarosan kezdődik a(z) ${presentation.room} teremben!`, + data: { tab: 'presentation', screen: 'presentation-details', slug: presentation.slug }, }, trigger: triggerDate, }); diff --git a/types/conference-api.type.ts b/types/conference-api.type.ts new file mode 100644 index 0000000..fb54010 --- /dev/null +++ b/types/conference-api.type.ts @@ -0,0 +1,89 @@ +export type FullConferenceDto = { + previousConferences: PreviousConferencesDto; + registration: RegistrationDto; + mobilApp: MobilAppDto; + giveaway: GiveawayDto; + promoVideo: PromoVideoDto; + sponsors: SponsorsDto; + organisers: OrganiserDto[]; + featuredPresentation: FeaturedPresentationDto; + presentations: PresentationDto[]; +}; + +export type FeaturedPresentationDto = { + sectionTitle: string; + description: string; +}; + +export type GiveawayDto = { + sectionTitle: string; + description: string; + pictureUrl: string; + rules: string; +}; + +export type MobilAppDto = { + description: string; + androidUrl: string; + iosUrl: string; +}; + +export type OrganiserDto = { + name: string; + rank: string; + emailAddress: string; + pictureUrl: string; + priority: number; +}; + +export type PresentationDto = { + slug: string; + title: string; + room: string; + language: string; + startTime: Date; + endTime: Date; + description: string; + questionsUrl: string; + presenter: PresenterDto; +}; + +export type PresenterDto = { + name: string; + rank: string; + pictureUrl: string; +}; + +export type PreviousConferencesDto = { + sectionTitle: string; + conferences: PreviousConferenceDto[]; +}; + +export type PreviousConferenceDto = { + title: string; + priority: number; + imageUrls: string[]; +}; + +export type PromoVideoDto = { + sectionTitle: string; + youtubeUrl: string; + description: string; +}; + +export type RegistrationDto = { + buttonText: string; + cooltixEventId: string; +}; + +export type SponsorsDto = { + sectionTitle: string; + companies: CompanyDto[]; +}; + +export type CompanyDto = { + name: string; + logoUrl: string; + url: string; + category: string; +}; diff --git a/types/favorite-event.type.ts b/types/favorite-event.type.ts index 8d94897..b44d9a0 100644 --- a/types/favorite-event.type.ts +++ b/types/favorite-event.type.ts @@ -1,4 +1,4 @@ -export type FavoriteEvent = { - eventId: string; +export type FavoritePresentation = { + slug: string; notificationId: string | undefined; }; diff --git a/types/schedule-event.type.ts b/types/schedule-event.type.ts deleted file mode 100644 index e22c332..0000000 --- a/types/schedule-event.type.ts +++ /dev/null @@ -1,10 +0,0 @@ -export type ScheduleEvent = { - id: string; - title: string; - description: string; - location: string; - start: string; - end: string; - presenter: string; - presenterImage: string; -}; diff --git a/utils/common.utils.ts b/utils/common.utils.ts index 831ff24..ffcf68a 100644 --- a/utils/common.utils.ts +++ b/utils/common.utils.ts @@ -10,3 +10,8 @@ export function useSafeId() { const { id } = useLocalSearchParams(); return Array.isArray(id) ? id[0] : id ?? ''; } + +export function useSafeSlug() { + const { slug } = useLocalSearchParams(); + return Array.isArray(slug) ? slug[0] : slug ?? ''; +} diff --git a/utils/presentation.utils.ts b/utils/presentation.utils.ts new file mode 100644 index 0000000..9ba661b --- /dev/null +++ b/utils/presentation.utils.ts @@ -0,0 +1,22 @@ +import { differenceInMinutes, isAfter, isBefore } from 'date-fns'; + +import { PresentationDto } from '../types/conference-api.type'; + +export function isPresentationPast(presentation: PresentationDto) { + const now = new Date(); + const end = new Date(presentation.endTime); + return isBefore(end, now); +} + +export function isPresentationCurrent(presentation: PresentationDto) { + const now = new Date(); + const start = new Date(presentation.startTime); + const end = new Date(presentation.endTime); + return isBefore(start, now) && isAfter(end, now); +} + +export function isPresentationUpcoming(presentation: PresentationDto) { + const now = new Date(); + const start = new Date(presentation.startTime); + return isAfter(start, now) && differenceInMinutes(start, now) < 15; +} diff --git a/utils/schedule.utils.ts b/utils/schedule.utils.ts deleted file mode 100644 index db81a38..0000000 --- a/utils/schedule.utils.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { differenceInMinutes, isAfter, isBefore } from 'date-fns'; - -import { ScheduleEvent } from '../types/schedule-event.type'; - -export function isScheduleEventPast(schedule: ScheduleEvent) { - const now = new Date(); - const end = new Date(schedule.end); - return isBefore(end, now); -} - -export function isScheduleEventCurrent(schedule: ScheduleEvent) { - const now = new Date(); - const start = new Date(schedule.start); - const end = new Date(schedule.end); - return isBefore(start, now) && isAfter(end, now); -} - -export function isScheduleEventUpcoming(schedule: ScheduleEvent) { - const now = new Date(); - const start = new Date(schedule.start); - return isAfter(start, now) && differenceInMinutes(start, now) < 15; -} diff --git a/yarn.lock b/yarn.lock index 54f400f..c92e9ef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2623,6 +2623,15 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== +axios@^1.6.7: + version "1.6.7" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.7.tgz#7b48c2e27c96f9c68a2f8f31e2ab19f59b06b0a7" + integrity sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA== + dependencies: + follow-redirects "^1.15.4" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + babel-core@^7.0.0-bridge.0: version "7.0.0-bridge.0" resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" @@ -3604,6 +3613,11 @@ dotenv-expand@~10.0.0: resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-10.0.0.tgz#12605d00fb0af6d0a592e6558585784032e4ef37" integrity sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A== +dotenv@^16.4.1: + version "16.4.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.1.tgz#1d9931f1d3e5d2959350d1250efab299561f7f11" + integrity sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ== + dotenv@~16.0.3: version "16.0.3" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" @@ -3656,6 +3670,11 @@ env-editor@^0.4.1: resolved "https://registry.yarnpkg.com/env-editor/-/env-editor-0.4.2.tgz#4e76568d0bd8f5c2b6d314a9412c8fe9aa3ae861" integrity sha512-ObFo8v4rQJAE59M69QzwloxPZtd33TpYEIjtKD1rrFDcM1Gd7IkDxEBU+HriziN6HSHQnBJi8Dmy+JWkav5HKA== +env-var@^7.4.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/env-var/-/env-var-7.4.1.tgz#88af75fe16cc11031000f88d4777802c6958a01c" + integrity sha512-H8Ga2SbXTQwt6MKEawWSvmxoH1+J6bnAXkuyE7eDvbGmrhIL2i+XGjzGM3DFHcJu8GY1zY9/AnBJY8uGQYPHiw== + envinfo@^7.7.2: version "7.11.0" resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.11.0.tgz#c3793f44284a55ff8c82faf1ffd91bc6478ea01f" @@ -4240,6 +4259,11 @@ flow-parser@^0.206.0: resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.206.0.tgz#f4f794f8026535278393308e01ea72f31000bfef" integrity sha512-HVzoK3r6Vsg+lKvlIZzaWNBVai+FXTX1wdYhz/wVlH13tb/gOdLXmlTqy6odmTBhT5UoWUbq0k8263Qhr9d88w== +follow-redirects@^1.15.4: + version "1.15.5" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" + integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== + fontfaceobserver@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/fontfaceobserver/-/fontfaceobserver-2.3.0.tgz#5fb392116e75d5024b7ec8e4f2ce92106d1488c8" @@ -4269,6 +4293,15 @@ form-data@^3.0.1: combined-stream "^1.0.8" mime-types "^2.1.12" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + freeport-async@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/freeport-async/-/freeport-async-2.0.0.tgz#6adf2ec0c629d11abff92836acd04b399135bab4" @@ -6568,6 +6601,11 @@ prop-types@*, prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.13.1" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" From d2fffbb0f0c5427f7d84e5d72d0a790c7e70196f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Berente?= <30603208+berenteb@users.noreply.github.com> Date: Wed, 31 Jan 2024 23:35:39 +0100 Subject: [PATCH 2/9] feat: scroll content with padding added --- components/base/scroll-content.tsx | 11 +++++++++++ components/schedule/presentation-details-page.tsx | 5 ++++- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 components/base/scroll-content.tsx diff --git a/components/base/scroll-content.tsx b/components/base/scroll-content.tsx new file mode 100644 index 0000000..70bcc58 --- /dev/null +++ b/components/base/scroll-content.tsx @@ -0,0 +1,11 @@ +import { ScrollView, View, ViewProps } from 'react-native'; + +import { cn } from '../../utils/common.utils'; + +export function ScrollContent({ className, ...props }: ViewProps) { + return ( + + + + ); +} diff --git a/components/schedule/presentation-details-page.tsx b/components/schedule/presentation-details-page.tsx index 2a849db..6fa0e2a 100644 --- a/components/schedule/presentation-details-page.tsx +++ b/components/schedule/presentation-details-page.tsx @@ -2,6 +2,7 @@ import { format } from 'date-fns'; import { usePresentation } from '../../hooks/use-presentation'; import { Screen } from '../base/screen'; +import { ScrollContent } from '../base/scroll-content'; import { StyledText } from '../base/text'; import { Header } from '../common/header'; import { Subtitle } from '../common/subtitle'; @@ -25,7 +26,9 @@ export function PresentationDetailsPage({ slug }: ScheduleDetailsPageProps) { {data?.room} • {startDate} - {endDate} - {data?.description} + + {data?.description} +
); From 5b2a71848b6fc631d0743634389b482b9efe7962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Berente?= <30603208+berenteb@users.noreply.github.com> Date: Thu, 1 Feb 2024 19:53:54 +0100 Subject: [PATCH 3/9] fix: sort presentation by start time --- services/conference.service.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/services/conference.service.ts b/services/conference.service.ts index 8f6ef1b..e48843f 100644 --- a/services/conference.service.ts +++ b/services/conference.service.ts @@ -1,9 +1,26 @@ +import { isAfter, isBefore } from 'date-fns'; + import { axiosInstance } from '../config/axios.config'; import { FullConferenceDto } from '../types/conference-api.type'; export class ConferenceService { static async getConferenceData(): Promise { const response = await axiosInstance.get('/api/conference/index'); + response.data.presentations = ConferenceService.sortPresentationsByStartDate(response.data); return response.data; } + + private static sortPresentationsByStartDate(conference: FullConferenceDto) { + return conference.presentations.sort((a, b) => { + const aStartDate = new Date(a.startTime); + const bStartDate = new Date(b.startTime); + if (isBefore(aStartDate, bStartDate)) { + return 1; + } + if (isAfter(aStartDate, bStartDate)) { + return -1; + } + return 0; + }); + } } From 96a84266c9ac0d4ee2b0532beca6ed0c593542cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Berente?= <30603208+berenteb@users.noreply.github.com> Date: Fri, 2 Feb 2024 19:23:17 +0100 Subject: [PATCH 4/9] feat: api error and loading state handling --- app/(tabs)/home/index.tsx | 14 +++++-- app/(tabs)/presentation/index.tsx | 10 ++++- components/common/error-message.tsx | 14 +++++++ components/common/header.tsx | 3 +- components/common/sectiontitle.tsx | 2 +- components/common/separator.tsx | 8 ++++ components/common/skeleton.tsx | 9 +++++ .../schedule/presentation-item-skeleton.tsx | 9 +++++ utils/animation.utils.ts | 40 +++++++++++++++++++ 9 files changed, 101 insertions(+), 8 deletions(-) create mode 100644 components/common/error-message.tsx create mode 100644 components/common/separator.tsx create mode 100644 components/common/skeleton.tsx create mode 100644 components/schedule/presentation-item-skeleton.tsx create mode 100644 utils/animation.utils.ts diff --git a/app/(tabs)/home/index.tsx b/app/(tabs)/home/index.tsx index 50cedbe..d855e33 100644 --- a/app/(tabs)/home/index.tsx +++ b/app/(tabs)/home/index.tsx @@ -1,11 +1,13 @@ import React from 'react'; -import { View } from 'react-native'; import { Screen } from '../../../components/base/screen'; +import { ErrorMessage } from '../../../components/common/error-message'; import { Header } from '../../../components/common/header'; import { SectionTitle } from '../../../components/common/sectiontitle'; +import { Separator } from '../../../components/common/separator'; import { Title } from '../../../components/common/title'; import { NewsList } from '../../../components/news/news-list'; +import { PresentationItemSkeleton } from '../../../components/schedule/presentation-item-skeleton'; import { PresentationList } from '../../../components/schedule/presentation-list'; import { useConference } from '../../../hooks/use-conference'; import { news } from '../../../mocks/news'; @@ -13,7 +15,7 @@ import { news } from '../../../mocks/news'; interface HomePageProps {} export default function HomePage({}: HomePageProps) { - const { data } = useConference(); + const conference = useConference(); return ( @@ -21,8 +23,12 @@ export default function HomePage({}: HomePageProps) { Simonyi Konferencia Előadások - - + {conference.isLoading && [0, 1].map((i) => )} + {conference.isError && Nem sikerült betölteni az előadásokat} + {!conference.isError && !conference.isLoading && ( + + )} + Hírek diff --git a/app/(tabs)/presentation/index.tsx b/app/(tabs)/presentation/index.tsx index 684a2a7..8d38b24 100644 --- a/app/(tabs)/presentation/index.tsx +++ b/app/(tabs)/presentation/index.tsx @@ -1,19 +1,25 @@ +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 { PresentationItemSkeleton } from '../../../components/schedule/presentation-item-skeleton'; import { PresentationList } from '../../../components/schedule/presentation-list'; import { useConference } from '../../../hooks/use-conference'; interface PresentationListPageProps {} export default function PresentationListPage({}: PresentationListPageProps) { - const { data } = useConference(); + const { data, isError, isLoading } = useConference(); return (
Programterv
- + {isLoading && [0, 1, 2, 3].map((i) => )} + {!isError && !isLoading && } + {isError && Nem sikerült betölteni az előadásokat}
); } diff --git a/components/common/error-message.tsx b/components/common/error-message.tsx new file mode 100644 index 0000000..229df20 --- /dev/null +++ b/components/common/error-message.tsx @@ -0,0 +1,14 @@ +import { Feather } from '@expo/vector-icons'; +import { View, ViewProps } from 'react-native'; + +import { cn } from '../../utils/common.utils'; +import { StyledText } from '../base/text'; + +export function ErrorMessage({ className, children, ...props }: ViewProps) { + return ( + + + {children} + + ); +} diff --git a/components/common/header.tsx b/components/common/header.tsx index f512453..9ad3d2f 100644 --- a/components/common/header.tsx +++ b/components/common/header.tsx @@ -3,6 +3,7 @@ import { useNavigation } from 'expo-router'; import { Pressable, View, ViewProps } from 'react-native'; import { cn } from '../../utils/common.utils'; +import { Separator } from './separator'; interface HeaderProps extends ViewProps {} @@ -17,7 +18,7 @@ export function Header({ children, className, ...props }: HeaderProps) { )} {children} - + ); } diff --git a/components/common/sectiontitle.tsx b/components/common/sectiontitle.tsx index 6f20a20..c501074 100644 --- a/components/common/sectiontitle.tsx +++ b/components/common/sectiontitle.tsx @@ -5,7 +5,7 @@ import { StyledText } from '../base/text'; export function SectionTitle({ children, className, ...props }: TextProps) { return ( - + {children} ); diff --git a/components/common/separator.tsx b/components/common/separator.tsx new file mode 100644 index 0000000..b995038 --- /dev/null +++ b/components/common/separator.tsx @@ -0,0 +1,8 @@ +import React from 'react'; +import { View, ViewProps } from 'react-native'; + +import { cn } from '../../utils/common.utils'; + +export function Separator({ className, ...props }: ViewProps) { + return ; +} diff --git a/components/common/skeleton.tsx b/components/common/skeleton.tsx new file mode 100644 index 0000000..9f426e5 --- /dev/null +++ b/components/common/skeleton.tsx @@ -0,0 +1,9 @@ +import { Animated, ViewProps } from 'react-native'; + +import { usePulseAnimation } from '../../utils/animation.utils'; +import { cn } from '../../utils/common.utils'; + +export function SkeletonRectangle({ className, style, ...props }: ViewProps) { + const { opacity } = usePulseAnimation(); + return ; +} diff --git a/components/schedule/presentation-item-skeleton.tsx b/components/schedule/presentation-item-skeleton.tsx new file mode 100644 index 0000000..a251755 --- /dev/null +++ b/components/schedule/presentation-item-skeleton.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import { ViewProps } from 'react-native'; + +import { cn } from '../../utils/common.utils'; +import { SkeletonRectangle } from '../common/skeleton'; + +export function PresentationItemSkeleton({ className, ...props }: ViewProps) { + return ; +} diff --git a/utils/animation.utils.ts b/utils/animation.utils.ts new file mode 100644 index 0000000..b2443df --- /dev/null +++ b/utils/animation.utils.ts @@ -0,0 +1,40 @@ +import { useEffect, useState } from 'react'; +import { Animated, Easing } from 'react-native'; + +export function usePulseAnimation() { + const [pulseValue] = useState(new Animated.Value(0)); + + useEffect(() => { + const startAnimation = () => { + Animated.loop( + Animated.sequence([ + Animated.timing(pulseValue, { + toValue: 1, + duration: 1000, + easing: Easing.inOut(Easing.ease), + useNativeDriver: true, + }), + Animated.timing(pulseValue, { + toValue: 0, + duration: 1000, + easing: Easing.inOut(Easing.ease), + useNativeDriver: true, + }), + ]) + ).start(); + }; + + startAnimation(); + + return () => { + pulseValue.stopAnimation(); + }; + }, [pulseValue]); + + return { + opacity: pulseValue.interpolate({ + inputRange: [0, 0.5, 1], + outputRange: [1, 0.5, 1], + }), + }; +} From 8a9b8ab8fbfde0b84d1c589cfaf2a44402ec91cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Berente?= <30603208+berenteb@users.noreply.github.com> Date: Sat, 3 Feb 2024 10:47:01 +0100 Subject: [PATCH 5/9] feat: news api --- app/(tabs)/home/index.tsx | 6 +++--- components/news/news-details-page.tsx | 14 ++++++++++---- components/news/news-item.tsx | 12 ++++++------ components/news/news-list.tsx | 8 +++----- hooks/use-news-item.ts | 11 +++++++---- hooks/use-news.ts | 18 ++++-------------- services/news.service.ts | 19 +++++++++++++++++++ types/news-api.type.ts | 22 ++++++++++++++++++++++ types/news-event.type.ts | 5 ----- 9 files changed, 74 insertions(+), 41 deletions(-) create mode 100644 services/news.service.ts create mode 100644 types/news-api.type.ts delete mode 100644 types/news-event.type.ts diff --git a/app/(tabs)/home/index.tsx b/app/(tabs)/home/index.tsx index d855e33..febb4cf 100644 --- a/app/(tabs)/home/index.tsx +++ b/app/(tabs)/home/index.tsx @@ -10,13 +10,13 @@ import { NewsList } from '../../../components/news/news-list'; import { PresentationItemSkeleton } from '../../../components/schedule/presentation-item-skeleton'; import { PresentationList } from '../../../components/schedule/presentation-list'; import { useConference } from '../../../hooks/use-conference'; -import { news } from '../../../mocks/news'; +import { useNews } from '../../../hooks/use-news'; interface HomePageProps {} export default function HomePage({}: HomePageProps) { const conference = useConference(); - + const news = useNews(); return (
@@ -30,7 +30,7 @@ export default function HomePage({}: HomePageProps) { )} Hírek - + {news.data && } ); } diff --git a/components/news/news-details-page.tsx b/components/news/news-details-page.tsx index a4b67c5..272b4d7 100644 --- a/components/news/news-details-page.tsx +++ b/components/news/news-details-page.tsx @@ -1,7 +1,9 @@ import { useNewsItem } from '../../hooks/use-news-item'; import { Screen } from '../base/screen'; import { StyledText } from '../base/text'; +import { ErrorMessage } from '../common/error-message'; import { Header } from '../common/header'; +import { SkeletonRectangle } from '../common/skeleton'; import { Title } from '../common/title'; interface NewsDetailsPageProps { @@ -9,14 +11,18 @@ interface NewsDetailsPageProps { } export function NewsDetailsPage({ id }: NewsDetailsPageProps) { - const { data } = useNewsItem(id); - if (!data) return <>; + const { data, error, isLoading } = useNewsItem(id); + if (!data) return ; return (
- {data?.title} + {isLoading && } + {data.title && {data.title}}
- {data?.description} + {(error || true) && ( + Hiba történt a hír betöltése közben. Lehet, hogy ez a hír nem is létezik? + )} + {data?.content}
); } diff --git a/components/news/news-item.tsx b/components/news/news-item.tsx index b434f85..997c20d 100644 --- a/components/news/news-item.tsx +++ b/components/news/news-item.tsx @@ -3,19 +3,19 @@ import { useState } from 'react'; import { Pressable, View } from 'react-native'; import { NativeStackNavigationProp } from 'react-native-screens/native-stack'; -import { NewsEvent } from '../../types/news-event.type'; +import { NewsItemDto } from '../../types/news-api.type'; import { cn } from '../../utils/common.utils'; import { StyledText } from '../base/text'; -interface NewsItem { - event: NewsEvent; +interface NewsItemProps { + newsItem: NewsItemDto; } -export function NewsItem({ event }: NewsItem) { +export function NewsItem({ newsItem }: NewsItemProps) { const router = useNavigation>(); const [isPressed, setIsPressed] = useState(false); const onPress = () => { - router.navigate('news-details', { id: event.id }); + router.navigate('news-details', { id: newsItem.url }); }; return ( - {event.title} + {newsItem.title} diff --git a/components/news/news-list.tsx b/components/news/news-list.tsx index 0d14bee..53976d0 100644 --- a/components/news/news-list.tsx +++ b/components/news/news-list.tsx @@ -1,14 +1,12 @@ import { FlatList } from 'react-native'; -import { NewsEvent } from '../../types/news-event.type'; +import { NewsItemDto } from '../../types/news-api.type'; import { NewsItem } from './news-item'; interface NewsListProps { - news: NewsEvent[]; + news: NewsItemDto[]; } export function NewsList({ news }: NewsListProps) { - return ( - } /> - ); + return } />; } diff --git a/hooks/use-news-item.ts b/hooks/use-news-item.ts index f7551c2..0513524 100644 --- a/hooks/use-news-item.ts +++ b/hooks/use-news-item.ts @@ -1,7 +1,10 @@ -import { useNews } from './use-news'; +import { useQuery } from '@tanstack/react-query'; + +import { NewsService } from '../services/news.service'; export function useNewsItem(id: string) { - const { data, ...rest } = useNews(); - const item = data?.find((item) => item.id === id); - return { data: item, ...rest }; + return useQuery({ + queryKey: ['news', id], + queryFn: () => NewsService.getNewsItemData(id), + }); } diff --git a/hooks/use-news.ts b/hooks/use-news.ts index 0388369..9f1605c 100644 --- a/hooks/use-news.ts +++ b/hooks/use-news.ts @@ -1,21 +1,11 @@ import { useQuery } from '@tanstack/react-query'; -import { news } from '../mocks/news'; -import { NewsEvent } from '../types/news-event.type'; +import { NewsService } from '../services/news.service'; +import { NewsDto } from '../types/news-api.type'; export function useNews() { - return useQuery({ + return useQuery({ queryKey: ['news'], - queryFn: getNews, - }); -} - -const delay = 0; - -function getNews(): Promise { - return new Promise((resolve) => { - setTimeout(() => { - resolve(news); - }, delay); + queryFn: NewsService.getNewsData, }); } diff --git a/services/news.service.ts b/services/news.service.ts new file mode 100644 index 0000000..7707a10 --- /dev/null +++ b/services/news.service.ts @@ -0,0 +1,19 @@ +import { axiosInstance } from '../config/axios.config'; +import { NewsDto, NewsItemDetailsDto, NewsItemDto } from '../types/news-api.type'; + +export class NewsService { + static async getNewsData(): Promise { + const response = await axiosInstance.get('/api/news'); + response.data.news = NewsService.sortNewsByTimestamp(response.data.news); + return response.data; + } + + static async getNewsItemData(id: string): Promise { + const response = await axiosInstance.get(`/api/news/${id}`); + return response.data; + } + + private static sortNewsByTimestamp(news: NewsItemDto[]) { + return news.sort((a, b) => b.timestamp - a.timestamp); + } +} diff --git a/types/news-api.type.ts b/types/news-api.type.ts new file mode 100644 index 0000000..791e305 --- /dev/null +++ b/types/news-api.type.ts @@ -0,0 +1,22 @@ +export type NewsDto = { + news: NewsItemDto[]; +}; + +export type NewsItemDto = { + url: string; + title: string; + briefContent: string; + imageUrl: string; + highlighted: boolean; + timestamp: number; +}; + +export type NewsItemDetailsDto = { + title: string; + content: string; + imageUrl: string; + timestamp: number; + ogTitle: string; + ogImage: string; + ogDescription: string; +}; diff --git a/types/news-event.type.ts b/types/news-event.type.ts deleted file mode 100644 index eebeac0..0000000 --- a/types/news-event.type.ts +++ /dev/null @@ -1,5 +0,0 @@ -export type NewsEvent = { - id: string; - title: string; - description: string; -}; From 0378e3c27adc279a907ecbe64448dc79f514a365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Berente?= <30603208+berenteb@users.noreply.github.com> Date: Sat, 3 Feb 2024 10:52:32 +0100 Subject: [PATCH 6/9] feat: splash display until data is loaded --- app/_layout.tsx | 33 +++++++++------------------------ components/common/splash.tsx | 31 +++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 24 deletions(-) create mode 100644 components/common/splash.tsx diff --git a/app/_layout.tsx b/app/_layout.tsx index 939123c..8ceacf6 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -1,36 +1,21 @@ import { QueryClientProvider } from '@tanstack/react-query'; -import { useFonts } from 'expo-font'; -import { Slot, SplashScreen } from 'expo-router'; -import { useEffect } from 'react'; +import { Slot } from 'expo-router'; import { View } from 'react-native'; +import { Splash } from '../components/common/splash'; import { queryClient } from '../config/query-client.config'; import { FavoritePresentationsProvider } from '../contexts/favorite-presentations.context'; export default function MainLayout() { - const [loaded, error] = useFonts({ - Raleway: require('../assets/fonts/Raleway-Regular.ttf'), - RalewayBold: require('../assets/fonts/Raleway-Bold.ttf'), - RalewayLight: require('../assets/fonts/Raleway-Light.ttf'), - }); - - useEffect(() => { - if (error) throw error; - }, [error]); - - useEffect(() => { - if (loaded) SplashScreen.hideAsync(); - }, [loaded]); - - if (!loaded) return null; - return ( - - - - - + + + + + + + ); } diff --git a/components/common/splash.tsx b/components/common/splash.tsx new file mode 100644 index 0000000..649b59d --- /dev/null +++ b/components/common/splash.tsx @@ -0,0 +1,31 @@ +import { useFonts } from 'expo-font'; +import { SplashScreen } from 'expo-router'; +import { PropsWithChildren, useEffect } from 'react'; + +import { useConference } from '../../hooks/use-conference'; +import { useNews } from '../../hooks/use-news'; + +export function Splash({ children }: PropsWithChildren) { + const conference = useConference(); + const news = useNews(); + + const [loaded, error] = useFonts({ + Raleway: require('../../assets/fonts/Raleway-Regular.ttf'), + RalewayBold: require('../../assets/fonts/Raleway-Bold.ttf'), + RalewayLight: require('../../assets/fonts/Raleway-Light.ttf'), + }); + + const dataReady = conference.status !== 'pending' && news.status !== 'pending'; + + useEffect(() => { + if (error) throw error; + }, [error]); + + useEffect(() => { + if (loaded && dataReady) SplashScreen.hideAsync(); + }, [loaded, dataReady]); + + if (!loaded || !dataReady) return null; + + return children; +} From c56ad81c656925e008670bb93fab8cd2ae0059a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Berente?= <30603208+berenteb@users.noreply.github.com> Date: Sat, 3 Feb 2024 16:34:35 +0100 Subject: [PATCH 7/9] feat: layout improvements --- app/(tabs)/home/index.tsx | 27 +++++++++++-------- components/common/sectiontitle.tsx | 2 +- components/news/home-news-list.tsx | 14 ++++++++++ components/news/news-item.tsx | 17 +++++++----- .../schedule/home-presentation-list.tsx | 25 +++++++++++++++++ components/schedule/presentation-list.tsx | 20 +++----------- 6 files changed, 70 insertions(+), 35 deletions(-) create mode 100644 components/news/home-news-list.tsx create mode 100644 components/schedule/home-presentation-list.tsx diff --git a/app/(tabs)/home/index.tsx b/app/(tabs)/home/index.tsx index febb4cf..071f5bd 100644 --- a/app/(tabs)/home/index.tsx +++ b/app/(tabs)/home/index.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { ScrollView, View } from 'react-native'; import { Screen } from '../../../components/base/screen'; import { ErrorMessage } from '../../../components/common/error-message'; @@ -6,9 +7,9 @@ import { Header } from '../../../components/common/header'; import { SectionTitle } from '../../../components/common/sectiontitle'; import { Separator } from '../../../components/common/separator'; import { Title } from '../../../components/common/title'; -import { NewsList } from '../../../components/news/news-list'; +import { HomeNewsList } from '../../../components/news/home-news-list'; +import { HomePresentationList } from '../../../components/schedule/home-presentation-list'; import { PresentationItemSkeleton } from '../../../components/schedule/presentation-item-skeleton'; -import { PresentationList } from '../../../components/schedule/presentation-list'; import { useConference } from '../../../hooks/use-conference'; import { useNews } from '../../../hooks/use-news'; @@ -22,15 +23,19 @@ export default function HomePage({}: HomePageProps) {
Simonyi Konferencia
- Előadások - {conference.isLoading && [0, 1].map((i) => )} - {conference.isError && Nem sikerült betölteni az előadásokat} - {!conference.isError && !conference.isLoading && ( - - )} - - Hírek - {news.data && } + + + Előadások + {conference.isLoading && [0, 1].map((i) => )} + {conference.isError && Nem sikerült betölteni az előadásokat} + {!conference.isError && !conference.isLoading && ( + + )} + + Hírek + {news.data && } + +
); } diff --git a/components/common/sectiontitle.tsx b/components/common/sectiontitle.tsx index c501074..136f5fd 100644 --- a/components/common/sectiontitle.tsx +++ b/components/common/sectiontitle.tsx @@ -5,7 +5,7 @@ import { StyledText } from '../base/text'; export function SectionTitle({ children, className, ...props }: TextProps) { return ( - + {children} ); diff --git a/components/news/home-news-list.tsx b/components/news/home-news-list.tsx new file mode 100644 index 0000000..7b76887 --- /dev/null +++ b/components/news/home-news-list.tsx @@ -0,0 +1,14 @@ +import { NewsItemDto } from '../../types/news-api.type'; +import { StyledText } from '../base/text'; +import { NewsItem } from './news-item'; + +interface HomeNewsListProps { + news: NewsItemDto[]; +} + +export function HomeNewsList({ news }: HomeNewsListProps) { + if (news.length === 0) { + return Nincs megjeleníthető hír.; + } + return news.map((newsItem, index) => ); +} diff --git a/components/news/news-item.tsx b/components/news/news-item.tsx index 997c20d..7caf92d 100644 --- a/components/news/news-item.tsx +++ b/components/news/news-item.tsx @@ -1,17 +1,17 @@ import { useNavigation } from 'expo-router'; import { useState } from 'react'; -import { Pressable, View } from 'react-native'; +import { Pressable, PressableProps, View } from 'react-native'; import { NativeStackNavigationProp } from 'react-native-screens/native-stack'; import { NewsItemDto } from '../../types/news-api.type'; import { cn } from '../../utils/common.utils'; import { StyledText } from '../base/text'; -interface NewsItemProps { +interface NewsItemProps extends Omit { newsItem: NewsItemDto; } -export function NewsItem({ newsItem }: NewsItemProps) { +export function NewsItem({ newsItem, className, ...props }: NewsItemProps) { const router = useNavigation>(); const [isPressed, setIsPressed] = useState(false); const onPress = () => { @@ -22,9 +22,14 @@ export function NewsItem({ newsItem }: NewsItemProps) { onPress={onPress} onPressIn={() => setIsPressed(true)} onPressOut={() => setIsPressed(false)} - className={cn('mb-5 rounded-xl bg-white flex-row p-3 items-center shadow-md shadow-slate-500/10', { - 'bg-slate-50': isPressed, - })} + className={cn( + 'mb-5 rounded-xl bg-white flex-row p-3 items-center shadow-md shadow-slate-500/10', + { + 'bg-slate-50': isPressed, + }, + className + )} + {...props} > diff --git a/components/schedule/home-presentation-list.tsx b/components/schedule/home-presentation-list.tsx new file mode 100644 index 0000000..093e051 --- /dev/null +++ b/components/schedule/home-presentation-list.tsx @@ -0,0 +1,25 @@ +import { useMemo } from 'react'; + +import { PresentationDto } from '../../types/conference-api.type'; +import { isPresentationCurrent, isPresentationUpcoming } from '../../utils/presentation.utils'; +import { StyledText } from '../base/text'; +import { PresentationItem } from './presentation-item'; + +interface HomePresentationListProps { + presentations: PresentationDto[]; +} + +export function HomePresentationList({ presentations }: HomePresentationListProps) { + const filteredPresentations = useMemo( + () => presentations.filter((event) => isPresentationUpcoming(event) || isPresentationCurrent(event)), + [presentations] + ); + + if (filteredPresentations.length === 0) { + return Nincs megjeleníthető előadás.; + } + + return filteredPresentations.map((presentation, index) => ( + + )); +} diff --git a/components/schedule/presentation-list.tsx b/components/schedule/presentation-list.tsx index 91aa63c..d3a0673 100644 --- a/components/schedule/presentation-list.tsx +++ b/components/schedule/presentation-list.tsx @@ -1,34 +1,20 @@ -import { useMemo } from 'react'; import { FlatList } from 'react-native'; import { PresentationDto } from '../../types/conference-api.type'; -import { isPresentationCurrent, isPresentationUpcoming } from '../../utils/presentation.utils'; import { StyledText } from '../base/text'; import { PresentationItem } from './presentation-item'; interface PresentationListProps { presentations: PresentationDto[]; - filterToCurrent?: boolean; - filterToUpcoming?: boolean; } -export function PresentationList({ presentations, filterToCurrent, filterToUpcoming }: PresentationListProps) { - const filteredPresentations = useMemo( - () => - presentations.filter( - (event) => - !(filterToCurrent || filterToUpcoming) || - (filterToUpcoming && isPresentationUpcoming(event)) || - (filterToCurrent && isPresentationCurrent(event)) - ), - [presentations, filterToCurrent, filterToUpcoming] - ); - if (filteredPresentations.length === 0) { +export function PresentationList({ presentations }: PresentationListProps) { + if (presentations.length === 0) { return Nincs megjeleníthető előadás.; } return ( } /> From a7783e156c008b5c845515ff2f820cc7a6afd2df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Berente?= <30603208+berenteb@users.noreply.github.com> Date: Sat, 3 Feb 2024 16:41:50 +0100 Subject: [PATCH 8/9] fix: EXPO_PUBLIC_API_BASE_URL --- .env.example | 2 +- .github/workflows/preview.yml | 16 ++++++++-------- app.config.ts | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.env.example b/.env.example index 593846b..8c52f08 100644 --- a/.env.example +++ b/.env.example @@ -1 +1 @@ -API_BASE_URL= \ No newline at end of file +EXPO_PUBLIC_API_BASE_URL= \ No newline at end of file diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 39de7b6..e70b1a9 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -9,8 +9,8 @@ jobs: update: name: EAS Update runs-on: ubuntu-latest -# env: -# EXPO_PUBLIC_API_URL: ${{ vars.EXPO_PUBLIC_API_URL }} + env: + EXPO_PUBLIC_API_BASE_URL: ${{ vars.EXPO_PUBLIC_API_BASE_URL }} permissions: contents: read pull-requests: write @@ -21,12 +21,12 @@ jobs: echo "You must provide an EXPO_TOKEN secret linked to this project's Expo account in this repo's secrets." exit 1 fi -# - name: Check for ENV variables -# run: | -# if [ -z "${{ vars.EXPO_PUBLIC_API_URL }}" ]; then -# echo "API_URL is not set" -# exit 1 -# fi + - name: Check for ENV variables + run: | + if [ -z "${{ vars.EXPO_PUBLIC_API_BASE_URL }}" ]; then + echo "EXPO_PUBLIC_API_BASE_URL is not set" + exit 1 + fi - name: Checkout repository uses: actions/checkout@v3 diff --git a/app.config.ts b/app.config.ts index 0a5576e..c1a7693 100644 --- a/app.config.ts +++ b/app.config.ts @@ -3,13 +3,13 @@ import { config } from 'dotenv'; import * as env from 'env-var'; config(); -const API_BASE_URL = env.get('API_BASE_URL').required().asString(); +const EXPO_PUBLIC_API_BASE_URL = env.get('EXPO_PUBLIC_API_BASE_URL').required().asString(); export default ({ config }: ConfigContext) => { return { ...config, extra: { - apiBaseUrl: API_BASE_URL, + apiBaseUrl: EXPO_PUBLIC_API_BASE_URL, ...config.extra, }, }; From a2b59fc67b45553bae3437b639cb740cd90c1da0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Berente?= <30603208+berenteb@users.noreply.github.com> Date: Sat, 3 Feb 2024 16:46:43 +0100 Subject: [PATCH 9/9] fix: expo update --- app.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app.json b/app.json index bf14dad..0085698 100644 --- a/app.json +++ b/app.json @@ -30,6 +30,12 @@ "projectId": "922ba2d9-369f-4940-aba6-9f2331f1f1c1" } }, + "updates": { + "url": "https://u.expo.dev/922ba2d9-369f-4940-aba6-9f2331f1f1c1" + }, + "runtimeVersion": { + "policy": "sdkVersion" + }, "owner": "kir-dev", "plugins": ["expo-router"], "notification": {