diff --git a/.env.example b/.env.example index d794d10..922176f 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,4 @@ EXPO_PUBLIC_API_BASE_URL= -EXPO_PUBLIC_DISABLE_ANALYTICS=true \ No newline at end of file +EXPO_PUBLIC_QNA_API_BASE_URL= + +EXPO_PUBLIC_DISABLE_ANALYTICS=true diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index e70b1a9..c6d8f89 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -11,6 +11,7 @@ jobs: runs-on: ubuntu-latest env: EXPO_PUBLIC_API_BASE_URL: ${{ vars.EXPO_PUBLIC_API_BASE_URL }} + EXPO_PUBLIC_QNA_API_BASE_URL: ${{ vars.EXPO_PUBLIC_QNA_API_BASE_URL }} permissions: contents: read pull-requests: write @@ -27,6 +28,10 @@ jobs: echo "EXPO_PUBLIC_API_BASE_URL is not set" exit 1 fi + if [ -z "${{ vars.EXPO_PUBLIC_QNA_API_BASE_URL }}" ]; then + echo "EXPO_PUBLIC_QNA_API_BASE_URL is not set" + exit 1 + fi - name: Checkout repository uses: actions/checkout@v3 diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml index a48bca5..2dd42db 100644 --- a/.github/workflows/update.yml +++ b/.github/workflows/update.yml @@ -11,6 +11,7 @@ jobs: runs-on: ubuntu-latest env: EXPO_PUBLIC_API_BASE_URL: ${{ vars.EXPO_PUBLIC_API_BASE_URL }} + EXPO_PUBLIC_QNA_API_BASE_URL: ${{ vars.EXPO_PUBLIC_QNA_API_BASE_URL }} steps: - name: Check for EXPO_TOKEN run: | @@ -24,6 +25,10 @@ jobs: echo "EXPO_PUBLIC_API_BASE_URL is not set" exit 1 fi + if [ -z "${{ vars.EXPO_PUBLIC_QNA_API_BASE_URL }}" ]; then + echo "EXPO_PUBLIC_QNA_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 1e1adf3..6c3c623 100644 --- a/app.config.ts +++ b/app.config.ts @@ -5,6 +5,7 @@ config(); const EXPO_PUBLIC_API_BASE_URL = env.get('EXPO_PUBLIC_API_BASE_URL').required().asString(); const EXPO_PUBLIC_DISABLE_ANALYTICS = env.get('EXPO_PUBLIC_DISABLE_ANALYTICS').default('false').asBool(); +const EXPO_PUBLIC_QNA_API_BASE_URL = env.get('EXPO_PUBLIC_QNA_API_BASE_URL').required().asString(); export default ({ config }: ConfigContext) => { return { @@ -12,6 +13,7 @@ export default ({ config }: ConfigContext) => { extra: { apiBaseUrl: EXPO_PUBLIC_API_BASE_URL, disableAnalytics: EXPO_PUBLIC_DISABLE_ANALYTICS, + qnaApiBaseUrl: EXPO_PUBLIC_QNA_API_BASE_URL, ...config.extra, }, }; diff --git a/components/qna/input.tsx b/components/qna/input.tsx index c0d9811..e669265 100644 --- a/components/qna/input.tsx +++ b/components/qna/input.tsx @@ -29,7 +29,7 @@ export function Input({ placeholder, onSubmit, disabled = false }: InputProps) { style={{ bottom: Math.max(130, keyboardOffset + 16), }} - className='absolute left-0 right-0 mx-5 flex-row space-x-3 rounded-2xl bg-white dark:bg-slate-800 px-3 py-2 shadow-md max-h-60' + className='absolute left-0 right-0 mx-5 flex-row space-x-3 rounded-xl bg-white dark:bg-slate-800 px-3 py-2 shadow-md max-h-60' > {message.text} + {message.status === 'pending' && ( + Küldés... + )} + {message.status === 'error' && ( + Hiba + )} ); } diff --git a/components/qna/qna-screen.tsx b/components/qna/qna-screen.tsx index 238e136..451a6f1 100644 --- a/components/qna/qna-screen.tsx +++ b/components/qna/qna-screen.tsx @@ -38,6 +38,10 @@ export function QnaScreen() { const remainingQuestions = MAX_QUESTION_COUNT - questionCount; + const onSubmission = (messageText: string) => { + messaging.sendMessageText(messageText, id); + }; + return (
@@ -63,7 +67,7 @@ export function QnaScreen() { ); diff --git a/config/env.config.ts b/config/env.config.ts index 56da28a..d256941 100644 --- a/config/env.config.ts +++ b/config/env.config.ts @@ -2,3 +2,4 @@ import Constants from 'expo-constants'; export const API_BASE_URL = Constants.expoConfig?.extra?.apiBaseUrl; export const DISABLE_ANALYTICS = Constants.expoConfig?.extra?.disableAnalytics; +export const QNA_API_BASE_URL = Constants.expoConfig?.extra?.qnaApiBaseUrl; diff --git a/hooks/use-messaging.ts b/hooks/use-messaging.ts index e957e2e..452d9c6 100644 --- a/hooks/use-messaging.ts +++ b/hooks/use-messaging.ts @@ -1,20 +1,38 @@ -import { useState } from 'react'; +import AsyncStorage from '@react-native-async-storage/async-storage'; +import axios, { isAxiosError } from 'axios'; +import { useEffect, useState } from 'react'; +import { QNA_API_BASE_URL } from '../config/env.config'; import { QnaMessage } from '../types/qna.type'; +import { generateId } from '../utils/common.utils'; export function useMessaging() { const [messages, setMessages] = useState([]); + const [userId, setUserId] = useState(); + + const loadUserId = () => { + AsyncStorage.getItem('userId').then((userId) => { + if (!userId) { + userId = generateId(); + AsyncStorage.setItem('userId', userId); + } + setUserId(userId); + }); + }; const addMessage = (newMessage: QnaMessage) => { setMessages((prevMessages) => [...prevMessages, newMessage]); }; const updateMessage = (message: QnaMessage) => { - setMessages((prevMessages) => prevMessages.map((prevMessage) => (prevMessage === message ? message : prevMessage))); + setMessages((prevMessages) => + prevMessages.map((prevMessage) => (prevMessage.id === message.id ? message : prevMessage)) + ); }; const addAnswer = (answerText: string) => { const newMessage: QnaMessage = { + id: generateId(), kind: 'answer', text: answerText, isInitial: false, @@ -23,31 +41,41 @@ export function useMessaging() { addMessage(newMessage); }; - const sendMessageText = async (messageText: string) => { + const sendMessageText = (messageText: string, presentationId: string) => { + if (!userId) return; const newMessage: QnaMessage = { + id: generateId(), kind: 'question', text: messageText, isInitial: false, status: 'pending', }; addMessage(newMessage); - sendMessage(newMessage) + sendMessage(messageText, presentationId, userId) .then(() => { updateMessage({ ...newMessage, status: 'sent' }); addAnswer('Kérdésed megkaptuk és moderálás után a felolvasandó kérdések közé kerül. Köszönjük!'); }) - .catch(() => { + .catch((e) => { + if (isAxiosError(e)) { + console.error('Error sending question', e.response?.data); + } else { + console.error('Error sending question', e); + } updateMessage({ ...newMessage, status: 'error' }); }); }; + useEffect(() => { + loadUserId(); + }, []); + return { messages, sendMessageText }; } -async function sendMessage(message: QnaMessage) { - return await new Promise((resolve) => { - setTimeout(() => { - resolve(message); - }, 2000); +async function sendMessage(content: string, presentationId: string, userId: string) { + return await axios.post(`${QNA_API_BASE_URL}/api/presentation/${presentationId}/question`, { + content, + userId, }); } diff --git a/types/qna.type.ts b/types/qna.type.ts index fa22050..2adf03c 100644 --- a/types/qna.type.ts +++ b/types/qna.type.ts @@ -1,4 +1,5 @@ export type QnaMessage = { + id: string; kind: 'question' | 'answer'; text: string; isInitial: boolean; diff --git a/utils/common.utils.ts b/utils/common.utils.ts index ffcf68a..e38dbf5 100644 --- a/utils/common.utils.ts +++ b/utils/common.utils.ts @@ -15,3 +15,10 @@ export function useSafeSlug() { const { slug } = useLocalSearchParams(); return Array.isArray(slug) ? slug[0] : slug ?? ''; } + +export function generateId(length = 16) { + const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + let result = ''; + for (let i = length; i > 0; --i) result += chars[Math.floor(Math.random() * chars.length)]; + return result; +}