diff --git a/app/(tabs)/settings.tsx b/app/(tabs)/settings.tsx index 45faac2..8859a40 100644 --- a/app/(tabs)/settings.tsx +++ b/app/(tabs)/settings.tsx @@ -7,6 +7,7 @@ import { ScrollContent } from '../../components/base/scroll-content'; import { Header } from '../../components/common/header'; import { Setting } from '../../components/common/settings/setting'; import { Title } from '../../components/common/title'; +import { AppDetails } from '../../components/settings/app-details'; import i18n from '../../services/i18-next'; import { SettingsStorageService } from '../../services/settings-storage.service'; import { SettingsType } from '../../types/settings.type'; @@ -89,6 +90,7 @@ export default function SettingsPage() { {/* currentValue={settings?.notifications ?? false}*/} {/* onChange={toggleNotifications}*/} {/*/>*/} + ); diff --git a/components/common/error-boundary.tsx b/components/common/error-boundary.tsx index 45fa8c7..3a7162a 100644 --- a/components/common/error-boundary.tsx +++ b/components/common/error-boundary.tsx @@ -14,7 +14,7 @@ export function ErrorBoundary(props: ErrorBoundaryProps) { usePageView('error'); return ( {props.error.message} {t('errBoundary.sub')} - + {t('errBoundary.retry')} diff --git a/components/map/resource-sheet.tsx b/components/map/resource-sheet.tsx index 8ee78dc..50b937c 100644 --- a/components/map/resource-sheet.tsx +++ b/components/map/resource-sheet.tsx @@ -6,6 +6,8 @@ import { useAnimated } from '../../utils/animation.utils'; import { cn } from '../../utils/common.utils'; import { StyledText } from '../base/text'; +const DISPLAY_ID = false; + interface ResourceSheetProps extends ViewProps { resource: MapResource | undefined; } @@ -34,7 +36,8 @@ export function ResourceSheet({ resource, className, ...props }: ResourceSheetPr {...props} > - {savedResource?.title} ({savedResource?.id}) + {savedResource?.title} + {DISPLAY_ID && ` (${savedResource?.id})`} {savedResource?.description.hu} diff --git a/components/schedule/layouts/home-presentation-list.tsx b/components/schedule/layouts/home-presentation-list.tsx index 804b00f..a492834 100644 --- a/components/schedule/layouts/home-presentation-list.tsx +++ b/components/schedule/layouts/home-presentation-list.tsx @@ -1,4 +1,5 @@ -import { useMemo } from 'react'; +import { useFocusEffect } from 'expo-router'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { PresentationDto } from '../../../types/conference-api.type'; @@ -11,12 +12,26 @@ interface HomePresentationListProps { } export function HomePresentationList({ presentations }: HomePresentationListProps) { + const [date, setDate] = useState(new Date()); const filteredPresentations = useMemo( () => presentations.filter((event) => isPresentationUpcoming(event) || isPresentationCurrent(event)), - [presentations] + [presentations, date] ); const { t } = useTranslation(); + useEffect(() => { + const interval = setInterval(() => { + setDate(new Date()); + }, 1000 * 15); + return () => clearInterval(interval); + }, []); + + useFocusEffect( + useCallback(() => { + setDate(new Date()); + }, []) + ); + if (filteredPresentations.length === 0) { return {t('home.empty')}; } diff --git a/components/schedule/layouts/presentation-list.tsx b/components/schedule/layouts/presentation-list.tsx index 7e241ed..22cb980 100644 --- a/components/schedule/layouts/presentation-list.tsx +++ b/components/schedule/layouts/presentation-list.tsx @@ -1,7 +1,10 @@ +import { useFocusEffect } from 'expo-router'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { FlatList } from 'react-native'; import { PresentationDto } from '../../../types/conference-api.type'; +import { isPresentationPast } from '../../../utils/presentation.utils'; import { StyledText } from '../../base/text'; import { PresentationItem } from '../elements/presentation-item'; @@ -10,12 +13,42 @@ interface PresentationListProps { } export function PresentationList({ presentations }: PresentationListProps) { + const [date, setDate] = useState(new Date()); + const ref = useRef(null); const { t } = useTranslation(); + + useEffect(() => { + if (ref.current && presentations.length > 0) { + const firstUpcomingIndex = presentations.findIndex((presentation) => !isPresentationPast(presentation)); + if (firstUpcomingIndex !== -1) ref.current.scrollToIndex({ index: firstUpcomingIndex, animated: true }); + } + }, [ref.current, presentations, date]); + + useEffect(() => { + const interval = setInterval(() => { + setDate(new Date()); + }, 1000 * 15); + return () => clearInterval(interval); + }, []); + + useFocusEffect( + useCallback(() => { + setDate(new Date()); + }, []) + ); + if (presentations.length === 0) { return {t('presentations.empty')}; } + return ( { + setTimeout(() => { + ref.current?.scrollToIndex({ index: info.index, animated: true }); + }, 100); + }} contentContainerStyle={{ paddingBottom: 130 }} data={presentations} className='px-5 pt-5' diff --git a/components/settings/app-details.tsx b/components/settings/app-details.tsx new file mode 100644 index 0000000..b6a6b21 --- /dev/null +++ b/components/settings/app-details.tsx @@ -0,0 +1,37 @@ +import Constants from 'expo-constants'; +import * as Updates from 'expo-updates'; +import React, { useEffect, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Platform, View } from 'react-native'; + +import { MessagingService } from '../../services/messaging.service'; +import { StyledText } from '../base/text'; + +export function AppDetails() { + const [userId, setUserId] = useState(null); + const appVersion = useMemo(() => { + const update = Updates.updateId ?? '?'; + const appVersion = Constants.expoConfig?.version ?? '?'; + const versionCode = + Platform.OS === 'android' ? Constants.expoConfig?.android?.versionCode : Constants.expoConfig?.ios?.buildNumber; + return `${appVersion} (${versionCode ?? '?'}) - ${update}`; + }, []); + + const appName = Constants.expoConfig?.name ?? 'App'; + + const { t } = useTranslation(); + + useEffect(() => { + MessagingService.getUserId().then(setUserId); + }, []); + + return ( + + {appName} + {appVersion} + UID: {userId} + {t('settings.author')} + {new Date().getFullYear()}. + + ); +} diff --git a/services/i18n_data.ts b/services/i18n_data.ts index 8cc730a..2f1db1c 100644 --- a/services/i18n_data.ts +++ b/services/i18n_data.ts @@ -37,6 +37,8 @@ export const resources = { default: 'System', language: 'Language', notifications: 'Notifications', + userIdLabel: 'Anonymous user ID', + author: 'Made with ❤ by\nKir-Dev & Simonyi Károly College for Advanced Studies', }, unmatched: { main: 'You have reached an unmatched route', @@ -93,6 +95,8 @@ export const resources = { default: 'Rendszer alapján', language: 'Nyelv', notifications: 'Értesítések', + userIdLabel: 'Anonim felhasználói azonosító', + author: 'Készítette ❤-ből\na️ Kir-Dev és a Simonyi Károly Szakkollégium', }, unmatched: { main: 'Ismeretlen képernyőre jutottál', diff --git a/services/messaging.service.ts b/services/messaging.service.ts index 5f6e6a4..8122979 100644 --- a/services/messaging.service.ts +++ b/services/messaging.service.ts @@ -32,7 +32,7 @@ export class MessagingService { }); } - private static async getUserId(): Promise { + static async getUserId(): Promise { if (this.userId) return this.userId; let userIdFromStorage = await AsyncStorage.getItem('userId'); if (!userIdFromStorage) { diff --git a/types/i18next.d.ts b/types/i18next.d.ts new file mode 100644 index 0000000..7961781 --- /dev/null +++ b/types/i18next.d.ts @@ -0,0 +1,11 @@ +import { resources } from '../services/i18n_data'; + +declare module 'i18next' { + interface CustomTypeOptions { + fallbackLng: 'hu'; + defaultNS: 'ns1'; + resources: { + ns1: typeof resources.hu.translation; + }; + } +} diff --git a/utils/presentation.utils.ts b/utils/presentation.utils.ts index a27c643..0bfd077 100644 --- a/utils/presentation.utils.ts +++ b/utils/presentation.utils.ts @@ -2,23 +2,25 @@ import { differenceInMinutes, isAfter, isBefore } from 'date-fns'; import { PresentationDto } from '../types/conference-api.type'; -const currentDate = new Date(); +function getCurrentDate() { + return new Date(); +} export function isPresentationPast(presentation: PresentationDto) { - const now = currentDate; + const now = getCurrentDate(); const end = new Date(presentation.endTime); return isBefore(end, now); } export function isPresentationCurrent(presentation: PresentationDto) { - const now = currentDate; + const now = getCurrentDate(); 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 = currentDate; + const now = getCurrentDate(); const start = new Date(presentation.startTime); return isAfter(start, now) && differenceInMinutes(start, now) < 15; }