Skip to content

Commit

Permalink
feat(messages): message notifications & update react-fivem-hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
antonstjernquist committed Sep 20, 2024
1 parent 2446b26 commit 03fce5e
Show file tree
Hide file tree
Showing 23 changed files with 560 additions and 40 deletions.
247 changes: 243 additions & 4 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

3 changes: 0 additions & 3 deletions src/server/database/schemas/Message.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { createDbTable, DATABASE_PREFIX } from '../utils';
import { DBInstance } from '../knex';
import { Message } from '../../../shared/Types';

export type InsertMessage = Pick<Message, 'receiver_id' | 'sender_id' | 'content'>;

export const createMessageTable = () => {
createDbTable('message', (table) => {
Expand Down
2 changes: 1 addition & 1 deletion src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function bootstrap() {
initDB();

const app = new App({
debug: false,
debug: true,
listeners: {
server: SERVER_EVENT_LISTENER,
client: CLIENT_EVENT_LISTENER,
Expand Down
33 changes: 29 additions & 4 deletions src/server/repositories/CallRepository.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Call } from '../../shared/Types';
import { Call, CallWithPhoneNumbers } from '../../shared/Types';
import { DBInstance } from '../database/knex';
import { InsertCall } from '../database/schemas/Call';

Expand Down Expand Up @@ -29,7 +29,21 @@ class CallRepository {

public async createCall(call: InsertCall): Promise<Call> {
const [newId] = await DBInstance(tableName).insert(call);
return await DBInstance(tableName).select('*').where('id', newId).first();
return await DBInstance(tableName)
.select('*')
.where('tmp_phone_call.id', newId)
.leftJoin('tmp_phone_sim_card', 'tmp_phone_sim_card.id', 'tmp_phone_call.receiver_id')
.leftJoin(
'tmp_phone_sim_card as tmp_phone_sim_card2',
'tmp_phone_sim_card2.id',
'tmp_phone_call.caller_id',
)
.select(
'tmp_phone_call.*',
'tmp_phone_sim_card.phone_number as receiver_phone_number',
'tmp_phone_sim_card2.phone_number as caller_phone_number',
)
.first();
}

public async getPendingCalls(simCardId: number): Promise<Call[]> {
Expand All @@ -48,12 +62,23 @@ class CallRepository {
.andWhere('acknowledged_at', null);
}

public async getActiveCalls(simCardId: number): Promise<Call[]> {
public async getActiveCalls(simCardId: number): Promise<CallWithPhoneNumbers[]> {
return await DBInstance(tableName)
.where('ended_at', null)
.andWhere(function () {
this.where('receiver_id', simCardId).orWhere('caller_id', simCardId);
});
})
.leftJoin('tmp_phone_sim_card', 'tmp_phone_sim_card.id', 'tmp_phone_call.receiver_id')
.leftJoin(
'tmp_phone_sim_card as tmp_phone_sim_card2',
'tmp_phone_sim_card2.id',
'tmp_phone_call.caller_id',
)
.select(
'tmp_phone_call.*',
'tmp_phone_sim_card.phone_number as receiver_phone_number',
'tmp_phone_sim_card2.phone_number as caller_phone_number',
);
}

public async updateCall(call: Call): Promise<Call> {
Expand Down
17 changes: 13 additions & 4 deletions src/server/repositories/MessageRepository.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Message, MessageWithPhoneNumbers } from '../../shared/Types';
import { InsertMessage, Message, MessageWithPhoneNumbers } from '../../shared/Types';
import { DBInstance } from '../database/knex';
import { InsertMessage } from '../database/schemas/Message';

const tableName = 'tmp_phone_message';

Expand Down Expand Up @@ -46,9 +45,19 @@ class MessageRepository {
.orderBy('created_at', 'asc');
}

public async createMessage(message: InsertMessage): Promise<Message> {
public async createMessage(message: InsertMessage): Promise<MessageWithPhoneNumbers> {
const [newId] = await DBInstance(tableName).insert(message);
return await DBInstance(tableName).select('*').where('id', newId).first();
return await DBInstance(tableName)
.select('*')
.where(`${tableName}.id`, newId)
.leftJoin('tmp_phone_sim_card as sender', 'sender.id', 'tmp_phone_message.sender_id')
.leftJoin('tmp_phone_sim_card as receiver', 'receiver.id', 'tmp_phone_message.receiver_id')
.select(
'tmp_phone_message.*',
'sender.phone_number as sender_phone_number',
'receiver.phone_number as receiver_phone_number',
)
.first();
}

public async updateMessage(message: Message): Promise<Message> {
Expand Down
2 changes: 1 addition & 1 deletion src/server/router/devices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ devicesRouter.add('/register', async (ctx, next) => {

const device = await DeviceService.createDevice({
sim_card_id: simCard.id,
identifier: ctx.deviceIdentifier,
identifier: ctx.device.identifier,
});

console.log('current-device:updated', device);
Expand Down
1 change: 0 additions & 1 deletion src/server/services/MessageService.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { RouterContext } from 'fivem-router';
import { InsertMessage } from '../database/schemas/Message';
import MessageRepository from '../repositories/MessageRepository';
import SimCardRepository from '../repositories/SimCardRepository';
import {
Expand Down
12 changes: 12 additions & 0 deletions src/shared/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ export interface Call extends Record<string, unknown> {
acknowledged_at?: Date;
}

export interface CallWithPhoneNumbers extends Call {
caller_phone_number: string;
receiver_phone_number: string;
}

export interface Message extends Record<string, unknown> {
id: number;
sender_id: number;
Expand All @@ -45,6 +50,13 @@ export interface Message extends Record<string, unknown> {
updated_at: Date;
}

export type InsertMessage = Pick<Message, 'receiver_id' | 'sender_id' | 'content'>;

export interface MessageWithPhoneNumbers extends Message {
sender_phone_number: string;
receiver_phone_number: string;
}

export interface MessageWithPhoneNumbers extends Message {
sender_phone_number: string;
receiver_phone_number: string;
Expand Down
2 changes: 1 addition & 1 deletion src/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-feather": "^2.0.10",
"react-fivem-hooks": "^1.0.3",
"react-fivem-hooks": "^1.0.4",
"react-router": "^6.26.1",
"react-router-dom": "^6.26.1",
"tailwind-merge": "^2.5.2",
Expand Down
80 changes: 76 additions & 4 deletions src/ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@ import { Outlet, useLocation, useNavigate } from 'react-router-dom';
import { Frame } from './Frame';

import { motion, useMotionValue, useTransform } from 'framer-motion';
import { useEffect } from 'react';
import { Component, ReactNode, useEffect } from 'react';
import { useNuiEvent } from 'react-fivem-hooks';
import { useCurrentDevice } from './api/hooks/useCurrentDevice';
import { closePhone } from './api/phone';
import { Footer } from './components/Main/Footer';
import { Header } from './components/Main/Header';
import { useDisableNavigation } from './contexts/NavigationContext';
import { useBroadcastEvent } from './hooks/useBroadcastEvent';
import { useKeys } from './hooks/useKeys';
import { useThemeType } from './hooks/useTheme';
import { queryClient } from './Providers';
import { isEnvBrowser } from './utils/game';
import { setTheme, Theme } from './utils/theme';
import { useKeys } from './hooks/useKeys';
import { useDisableNavigation } from './contexts/NavigationContext';
import { useActiveCall } from './api/hooks/useActiveCall';
import { useMessagesNotifications } from './api/hooks/useMessagesNotifications';

export const lightTheme: Theme = {
type: 'light',
Expand All @@ -41,6 +43,9 @@ export const darkTheme: Theme = {
};

function App() {
useActiveCall();
useMessagesNotifications();

const location = useLocation();
const navigate = useNavigate();
const currentDevice = useCurrentDevice();
Expand All @@ -52,6 +57,20 @@ function App() {
const borderRadius = useTransform(y, [-150, -40, 0], [40, 0, 0]);
const currentThemeType = useThemeType();

useEffect(() => {
const handleMessage = (event) => {
console.log('----------');
console.log('message received');
console.log(event);
console.log('----------');
};

window.addEventListener('message', handleMessage);
return () => {
window.removeEventListener('message', handleMessage);
};
}, []);

useEffect(() => {
if (currentThemeType === 'dark') {
setTheme(darkTheme);
Expand Down Expand Up @@ -127,4 +146,57 @@ function App() {
);
}

export default App;
interface ErrorBoundaryProps {
fallback: ReactNode;
children: ReactNode;
}
class ErrorBoundary extends Component<ErrorBoundaryProps> {
state: { hasError: boolean };

constructor(props: ErrorBoundaryProps) {
super(props);
this.state = { hasError: false };
}

static getDerivedStateFromError(error: Error) {
console.error('ErrorBoundary caught an error:', error);
// Update state so the next render will show the fallback UI.
return { hasError: true };
}

componentDidCatch(error: Error, info: unknown) {
// Example "componentStack":
// in ComponentThatThrows (created by App)
// in ErrorBoundary (created by App)
// in div (created by App)
// in App
console.error('ErrorBoundary caught an error:', error, info);
// logErrorToMyService(error, info.componentStack);
}

render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return this.props.fallback;
}

return this.props.children;
}
}

const AppWithErrorBoundary = () => {
return (
<ErrorBoundary
fallback={
<div>
<h1>Something went wrong.</h1>
<p>Check the console for more information.</p>
</div>
}
>
<App />
</ErrorBoundary>
);
};

export default AppWithErrorBoundary;
6 changes: 5 additions & 1 deletion src/ui/src/Apps/Calls/Contacts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ import { Button } from '@/components/ui/button';

export const ContactsView = () => {
const device = useCurrentDevice();
const [contacts] = useContacts();
const [contacts, , isLoading] = useContacts();

const [searchParams] = useSearchParams();
const referal = searchParams.get('referal');

if (isLoading) {
return <div>Loading...</div>;
}

return (
<main className="flex flex-col">
<TopNavigation
Expand Down
7 changes: 4 additions & 3 deletions src/ui/src/Apps/Calls/Messages/Conversation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Fragment, useEffect, useRef } from 'react';
import { useConversationMessages } from '../../../../api/hooks/useConversation';
import { Send } from 'react-feather';
import { DateTime } from 'luxon';
import { AlignVerticalSpaceAround } from 'lucide-react';
import { useContactPhoneNumber } from '@/api/hooks/useContactPhoneNumber';

export const Conversation = () => {
const navigate = useNavigate();
Expand All @@ -17,6 +17,7 @@ export const Conversation = () => {
const textAreaRef = useRef<HTMLTextAreaElement>(null);
const device = useCurrentDevice();
const { phoneNumber } = useParams<{ phoneNumber: string }>();
const contact = useContactPhoneNumber(phoneNumber);

const messages = useConversationMessages(phoneNumber);

Expand Down Expand Up @@ -79,7 +80,7 @@ export const Conversation = () => {
return (
<div className="flex flex-col h-full">
<TopNavigation
title={<AlignVerticalSpaceAround />}
title={contact?.name || phoneNumber}
left={
<button onClick={() => navigate(-1)} className="text-primary">
Back
Expand Down Expand Up @@ -128,7 +129,7 @@ export const Conversation = () => {
})}
</ul>

<section className="pt-4 mt-auto shadow-lg">
<section className="pt-4 pb-10 mt-auto shadow-lg">
<form
onSubmit={onSubmit}
className="p-4 flex gap-2 mt-auto bg-secondary mx-4 rounded-lg items-center"
Expand Down
23 changes: 23 additions & 0 deletions src/ui/src/Apps/Calls/Messages/New/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { NewMessageForm } from '@/components/Forms/NewMessageForm';
import { Drawer, DrawerContent, DrawerHeader, DrawerTitle } from '@/components/ui/drawer';
import { useNavigate } from 'react-router-dom';

export const NewMessageView = () => {
const navigate = useNavigate();

const handleBack = () => {
navigate(-1);
};

return (
<Drawer modal open onClose={handleBack}>
<DrawerContent>
<DrawerHeader>
<DrawerTitle className="text-primary">Compose a new message</DrawerTitle>
</DrawerHeader>

<NewMessageForm onMessageSent={handleBack} />
</DrawerContent>
</Drawer>
);
};
14 changes: 12 additions & 2 deletions src/ui/src/Apps/Calls/Messages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
import { TopNavigation } from '../../../components/Navigation/TopNavigation';
import { Link } from 'react-router-dom';
import { Link, Outlet } from 'react-router-dom';
import { useConversations } from '../../../api/hooks/useConversations';
import { useContacts } from '@/api/hooks/useContacts';
import { Button } from '@/components/ui/button';

export const MessagesApp = () => {
const [contacts] = useContacts();
const conversations = useConversations();

return (
<div>
<TopNavigation title="Messages" />
<TopNavigation
title="Messages"
right={
<Link to="/apps/messages/new">
<Button>New</Button>
</Link>
}
/>

<ul className="flex flex-col gap-1 p-4">
{conversations.map((phoneNumber) => {
Expand All @@ -25,6 +33,8 @@ export const MessagesApp = () => {
);
})}
</ul>

<Outlet />
</div>
);
};
2 changes: 1 addition & 1 deletion src/ui/src/Providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const queryClient = new QueryClient({
export const Providers = () => {
return (
<QueryClientProvider client={queryClient}>
<NuiProvider>
<NuiProvider debug>
<NotificationsProvider>
<NavigationProvider>
<RouterProvider initialRoutes={routes}>
Expand Down
Loading

0 comments on commit 03fce5e

Please sign in to comment.