Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: basic chat implementation #3

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 74 additions & 19 deletions api/server.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { Server } from 'socket.io'
import express from 'express'
import { createServer } from 'http'
import path from 'path'
import uuid from 'react-native-uuid'
import { Server } from 'socket.io'

import buyers from '../src/data/buyers'
import { categories } from '../src/data/categories'
import deals from '../src/data/deals'
import offers from '../src/data/offers'
import tenderRequests from '../src/data/tenderRequests'
import suppliers from '../src/data/suppliers'
import { categories } from '../src/data/categories'
import tenderRequests from '../src/data/tenderRequests'
import { sendPushNotification as push } from './notifications'
const port = process.env.PORT || 3000
const app = express()
Expand Down Expand Up @@ -258,22 +258,22 @@ io.on('connection', (socket) => {
},
})

if (tenderRequest && otherOffersForTender)
otherOffersForTender.forEach(o =>
sendPushNotification({
to: [o.supplier.token],
title: 'Anbud förkastat',
body: `Ditt bud på ${tenderRequest.title} har förkastats. ${offer.supplier.name}s bud har godkänts av följande skäl: ${offer.acceptanceMotivation}`,
data: {
date: new Date(),
type: 'offer',
to: [o.supplier.id],
id: offer.id,
tenderRequestId: tenderRequest?.id,
},
})
)
}
if (tenderRequest && otherOffersForTender)
otherOffersForTender.forEach((o) =>
sendPushNotification({
to: [o.supplier.token],
title: 'Anbud förkastat',
body: `Ditt bud på ${tenderRequest.title} har förkastats. ${offer.supplier.name}s bud har godkänts av följande skäl: ${offer.acceptanceMotivation}`,
data: {
date: new Date(),
type: 'offer',
to: [o.supplier.id],
id: offer.id,
tenderRequestId: tenderRequest?.id,
},
})
)
}
state.offers[index] = offer
io.emit('offers', state.offers)
})
Expand Down Expand Up @@ -308,6 +308,61 @@ io.on('connection', (socket) => {
io.emit('tenderRequests', state.tenderRequests)
})

// CHAT

socket.on('messages', (tenderRequestId) => {
const tenderRequestIndex = state.tenderRequests.findIndex(
(d) => d.id === tenderRequestId
)
const tenderRequest = state.tenderRequests[tenderRequestIndex]

io.emit('messages', tenderRequest.messages)
})

socket.on('sendMessage', (message) => {
const tenderRequestIndex = state.tenderRequests.findIndex(
(d) => d.id === message.tenderRequestId
)
const tenderRequest = state.tenderRequests[tenderRequestIndex]

console.log(tenderRequest.messages)

state.tenderRequests[tenderRequestIndex].messages = [
...tenderRequest.messages,
message,
]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to be sure I'm getting it, if we add the messages to the tender can we somehow filter them to only show the messages sent between the buyer (who published the tender) and each producer separately? Or do we show all the messages that are involved in the tender to every user?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently we show every message to everyone that is in the tender. I was not sure how the implementation was supposed to work, but since its a POC i think its fine?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! I guess for a POC is enough, just wanted to make sure I was understanding correctly, it looks good then💃🏽


// Send notification to everyone involved in the conversation
// Currently also sends notification to the user who sent the message for easy demo purposes

const toUsers = unique(
tenderRequest.messages.map((message) => message.from)
// .filter(
// (user) => user?.id != message.from?.id
// ) /* <- uncomment this to disable notification to sender */
)

const tokens = unique(
tenderRequest.messages
.map((message) => message.from?.token)
.filter((t) => t && t)
)

sendPushNotification({
to: tokens,
title: `Nytt meddelande i ${tenderRequest.title} från ${message.from.name}`,
body: message.text,
data: {
date: message.date,
type: 'message',
to: toUsers.map((user) => user.id),
id: tenderRequest.id,
},
})

io.emit('messages', tenderRequest.messages)
})

// NOTIFICATIONS

socket.on('notifications', (respond) =>
Expand Down
14,337 changes: 101 additions & 14,236 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@react-native-async-storage/async-storage": "^1.18.1",
"@react-navigation/bottom-tabs": "^6.5.7",
"@react-navigation/material-bottom-tabs": "^6.2.15",
"@react-navigation/native": "^6.1.17",
"@react-navigation/native-stack": "^6.9.12",
"@react-navigation/stack": "^6.3.16",
"csv-parser": "^3.0.0",
Expand All @@ -38,6 +39,7 @@
"react-native-reanimated": "~2.14.4",
"react-native-safe-area-context": "^4.5.3",
"react-native-uuid": "^2.0.1",
"react-native-vector-icons": "^10.1.0",
"react-native-web": "~0.18.10",
"react-navigation-header-buttons": "^10.0.0",
"socket.io": "^4.6.2",
Expand All @@ -47,6 +49,7 @@
"@babel/core": "^7.20.0",
"@types/react": "~18.0.14",
"@types/react-native": "^0.72.0",
"@types/react-native-vector-icons": "^6.4.18",
"gh-pages": "^5.0.0",
"nodemon": "^2.0.22",
"ts-node": "^10.9.1",
Expand Down
11 changes: 5 additions & 6 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import React, { useEffect, useState } from 'react'
import { StatusBar } from 'expo-status-bar'
import { SafeAreaProvider } from 'react-native-safe-area-context'
import Navigation from './RootNavigation'
import { NavigationContainer } from '@react-navigation/native'
import React from 'react'
import { useTheme } from 'react-native-paper'
import { SocketProvider } from './context/socketContext'
import { AuthProvider } from './context/authContext'
import { SafeAreaProvider } from 'react-native-safe-area-context'
import Navigation from './RootNavigation'
import { NotificationSnackbar } from './components/NotificationSnackbar'
import { AuthProvider } from './context/authContext'
import { SocketProvider } from './context/socketContext'

export default function App() {
const theme = useTheme()
Expand Down
98 changes: 59 additions & 39 deletions src/components/Chat.tsx
Original file line number Diff line number Diff line change
@@ -1,67 +1,70 @@
import React, { useState } from 'react'
import {
View,
Text,
KeyboardAvoidingView,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
KeyboardAvoidingView,
View,
} from 'react-native'
import { TextInput } from 'react-native-gesture-handler'
import { Caption } from 'react-native-paper'
import { SafeAreaView } from 'react-native-safe-area-context'
import { useAuthContext } from '../context/authContext'
import { Message } from '../data/tenderRequests'
import useMessages from '../hooks/useMessages'

const messages = [
{
id: 1,
type: 'question',
text: 'Vad händer om skörden blir mindre än tänkt?',
date: '2021-05-01T12:00:00',
},
{
id: 2,
type: 'answer',
text: 'Vi för en kontinuerlig dialog och löser det i så fall tillsammans.',
date: '2021-05-01T12:01:00',
},
{
id: 3,
type: 'question',
text: 'Hur exakt är leveransplanen?',
date: '2021-05-02T12:02:00',
},
// ...fler meddelanden
]
const Chat = () => {
const Chat = ({ tenderRequestId }: { tenderRequestId: string }) => {
const [inputText, setInputText] = useState('')
const { user } = useAuthContext()
console.log(user)
const [messages, sendMessage, refresh] = useMessages(tenderRequestId)

const handleSendMessage = () => {
if (!inputText) return
// Logik för att hantera skickade meddelanden
// Exempel: lägga till meddelandet i meddelandelistan
messages.push({
id: (messages.at(-1)?.id || 0) + 1,
type: 'question',

const newMessage: Message = {
id: Math.random().toString(36).slice(-6),
tenderRequestId: tenderRequestId,
text: inputText,
date: new Date().toISOString(),
})
setInputText('') // Rensa svarsfältet efter att ha skickat meddelandet
from: user ?? undefined,
date: new Date(Date.now()),
}

sendMessage(newMessage)
setInputText('')
}

React.useLayoutEffect(() => {
refresh()
}, [])

return (
<SafeAreaView style={{ flex: 1 }}>
<KeyboardAvoidingView>
<ScrollView contentContainerStyle={styles.container}>
{messages.map((message, i) => (
{messages.map((message: Message, i: number) => (
<View key={message.id}>
<View
style={[
styles.messageContainer,
message.type === 'question' ? styles.question : styles.answer,
message.from?.id === user?.id
? styles.question
: styles.answer,
]}
>
<Text style={styles.messageText}>{message.text}</Text>
</View>
{i === messages.length - 1 && (
<Caption style={styles.messageMetadata}>{message.date}</Caption>
<Caption
style={
message.from?.id === user?.id
? styles.questionMetadata
: styles.answerMetadata
}
>
{message.date.toLocaleString()}
</Caption>
)}
</View>
))}
Expand All @@ -74,7 +77,12 @@ const Chat = () => {
/>
<TouchableOpacity
onPress={handleSendMessage}
style={styles.sendButton}
disabled={inputText.length === 0}
style={
inputText.length > 0
? styles.sendButton
: styles.sendButtonDisabled
}
>
<Text style={styles.sendButtonText}>Skicka</Text>
</TouchableOpacity>
Expand Down Expand Up @@ -111,8 +119,13 @@ const styles = StyleSheet.create({
fontSize: 16,
color: '#FFF',
},
messageMetadata: {
alignSelf: 'center',
answerMetadata: {
alignSelf: 'flex-start',
marginTop: -10,
color: '#999',
},
questionMetadata: {
alignSelf: 'flex-end',
marginTop: -10,
color: '#999',
},
Expand All @@ -135,6 +148,13 @@ const styles = StyleSheet.create({
paddingVertical: 8,
borderRadius: 20,
},
sendButtonDisabled: {
marginLeft: 10,
backgroundColor: '#757575', // Morotsfärg
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 20,
},
sendButtonText: {
color: '#FFF',
fontSize: 16,
Expand Down
8 changes: 3 additions & 5 deletions src/components/Deals.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import * as React from 'react'
import { ScrollView, StyleSheet } from 'react-native'
import { TouchableOpacity } from 'react-native-gesture-handler'
import { Card, FAB, List, Searchbar, Text } from 'react-native-paper'
import { ScrollView, StyleSheet, View } from 'react-native'
import { areas } from '../data/categories'
import { TouchableOpacity } from 'react-native-gesture-handler'
import Chat from './Chat'
import useDeals from '../hooks/useDeals'
import { Deal } from '../data/deals'
import { useState } from 'react'
import useAuth from '../hooks/useAuth'
import useDeals from '../hooks/useDeals'

interface Area {
image: string
Expand Down
Loading