Skip to content

Commit

Permalink
Merge pull request #19 from npwd-community/add-vite
Browse files Browse the repository at this point in the history
add vite
  • Loading branch information
itschip committed Jan 19, 2024
2 parents 133e988 + b105449 commit 424edd0
Show file tree
Hide file tree
Showing 13 changed files with 957 additions and 2,673 deletions.
Empty file added .yarn.installed
Empty file.
4 changes: 2 additions & 2 deletions fxmanifest.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ ui_page 'web/dist/index.html'

files {
'web/dist/index.html',
'web/dist/*.js',
}
'web/dist/**/*',
}
13 changes: 13 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<link rel="icon" type="image/svg+xml" href="/vite.svg"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>NPWD QB Garage</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/index.tsx"></script>
</body>
</html>
6 changes: 5 additions & 1 deletion npwd.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import App from './src/App';
import { GarageIcon, NotificationIcon } from './icon';
import { theme } from './src/app.theme';

export const externalAppConfig = () => ({
interface Settings {
language: "en";
}

export const externalAppConfig = (settings: Settings) => ({
id: 'garage',
nameLocale: 'Garage',
color: '#fff',
Expand Down
36 changes: 15 additions & 21 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
{
"name": "garage",
"name": "npwd_qb_garage",
"version": "0.0.0",
"private": true,
"devDependencies": {
"@babel/core": "7.17.5",
"@babel/preset-react": "7.16.7",
"@citizenfx/client": "^2.0.5369-1",
"@citizenfx/server": "^2.0.5369-1",
"@originjs/vite-plugin-federation": "^1.3.3",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.4",
"@types/node": "^18.6.4",
"@types/react": "^17.0.39",
Expand All @@ -15,32 +14,26 @@
"@types/react-router-dom": "^5.3.3",
"@types/styled-components": "^5.1.23",
"@types/webpack-env": "^1.16.3",
"@vitejs/plugin-react": "^4.2.1",
"add": "^2.0.6",
"babel-loader": "8.2.3",
"concurrently": "^7.0.0",
"cross-env": "^7.0.3",
"dotenv": "^16.0.1",
"html-webpack-plugin": "5.5.0",
"react-refresh": "^0.11.0",
"react-refresh-typescript": "^2.0.3",
"serve": "13.0.2",
"ts-loader": "^9.2.6",
"typescript": "^4.5.5",
"webpack": "5.66.0",
"webpack-cli": "4.9.2",
"webpack-dev-server": "4.7.4",
"yarn": "^1.22.17"
"typescript": "^5.2.2",
"vite": "^5.0.9"
},
"scripts": {
"start": "webpack-cli serve",
"build": "cross-env REACT_APP_IN_GAME=1 webpack --mode=production",
"dev": "webpack-dev-server --mode=development",
"serve": "serve dist -p 3002",
"dev": "vite --mode dev",
"dev:browser": "concurrently \"pnpm build:watch --mode dev\" \"pnpm preview:dev\"",
"build:dev": "vite build --mode dev",
"build:watch": "vite build --watch",
"build": "vite build --mode game",
"preview:dev": "vite preview --mode dev",
"clean": "rm -rf dist",
"watch": "concurrently \"yarn watch:client\" \"yarn watch:server\" \"yarn watch:nui\"",
"watch:nui": "esbuild src/index.tsx --outdir=dist/html --watch --target=es6 --bundle --loader:.png=dataurl",
"watch:client": "esbuild client/client.ts --bundle --watch --outfile=dist/client.js",
"watch:server": "esbuild server/server.ts --bundle --sourcemap --platform=node --watch --outfile=dist/server.js"
"watch:nui": "esbuild src/index.tsx --outdir=dist/html --watch --target=es6 --bundle --loader:.png=dataurl"
},
"dependencies": {
"@emotion/react": "^11.7.0",
Expand All @@ -58,7 +51,8 @@
"react-fivem-hooks": "^1.0.2-1",
"react-hot-loader": "^4.13.0",
"react-router-dom": "^5.3.0",
"recoil": "^0.6.1",
"styled-components": "^5.3.3"
"recoil": "^0.7.7",
"styled-components": "^5.3.3",
"vite-plugin-top-level-await": "^1.4.1"
}
}
141 changes: 71 additions & 70 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,93 +1,94 @@
import React, { useEffect, useState } from 'react';
import { NuiProvider } from 'react-fivem-hooks';
import { useHistory } from 'react-router-dom';
import React, {useEffect, useState} from 'react';
import {useHistory} from 'react-router-dom';
import styled from 'styled-components';
import { Header } from './styles/header.styles';
import { IPhoneSettings } from '@project-error/npwd-types';
import { i18n } from 'i18next';
import { IconButton, Theme, StyledEngineProvider, ThemeProvider, Typography } from '@mui/material';
import { ArrowBack } from '@mui/icons-material';
import { GarageItem } from './types/garage';
import { MockGarage } from './utils/constants';
import { buildRespObj } from './utils/misc';
import { VehicleList } from './components/VehicleList';
import {Header} from './styles/header.styles';
import {IPhoneSettings} from '@project-error/npwd-types';
import {i18n} from 'i18next';
import {IconButton, Theme, StyledEngineProvider, Typography} from '@mui/material';
import {ArrowBack} from '@mui/icons-material';
import {GarageItem} from './types/garage';
import {MockGarage} from './utils/constants';
import {buildRespObj} from './utils/misc';
import {VehicleList} from './components/VehicleList';
import fetchNui from './utils/fetchNui';
import { ServerPromiseResp } from './types/common';
import {ServerPromiseResp} from './types/common';
import {RecoilEnv, RecoilRoot} from "recoil";

RecoilEnv.RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED = false;

const Container = styled.div<{ isDarkMode: any }>`
flex: 1;
padding: 1.5rem;
display: flex;
box-sizing: border-box;
flex-direction: column;
overflow: auto;
max-height: 100%;
background-color: #fafafa;
${({ isDarkMode }) =>
isDarkMode &&
`
flex: 1;
padding: 1.5rem;
display: flex;
box-sizing: border-box;
flex-direction: column;
overflow: auto;
max-height: 100%;
background-color: #fafafa;
${({isDarkMode}) =>
isDarkMode &&
`
background-color: #212121;
`}
`;

interface AppProps {
theme: Theme;
i18n: i18n;
settings: IPhoneSettings;
theme: Theme;
i18n: i18n;
settings: IPhoneSettings;
}

const App = (props: AppProps) => {
const history = useHistory();
const [vehicles, setVehicles] = useState<GarageItem[] | undefined>([]);
const [mappedVeh, setMappedVeh] = useState<any>(null);
const history = useHistory();
const [vehicles, setVehicles] = useState<GarageItem[] | undefined>([]);
const [mappedVeh, setMappedVeh] = useState<any>(null);

const isDarkMode = props.theme.palette.mode === 'dark';
const isDarkMode = 'dark'

useEffect(() => {
fetchNui<ServerPromiseResp<GarageItem[]>>(
'npwd:qb-garage:getVehicles',
null,
buildRespObj(MockGarage)
).then((resp) => {
setVehicles(resp.data);
});
}, []);
useEffect(() => {
fetchNui<ServerPromiseResp<GarageItem[]>>(
'npwd:qb-garage:getVehicles',
null,
buildRespObj(MockGarage)
).then((resp) => {
setVehicles(resp.data);
});
}, []);


useEffect(() => {
if (vehicles) {
const mappedVehicles = vehicles?.reduce((vehs: any, vehicle: any) => {
if (!vehs[vehicle.type]) vehs[vehicle.type] = [];
vehs[vehicle.type].push(vehicle);
return vehs;
}, {});
useEffect(() => {
if (vehicles) {
const mappedVehicles = vehicles?.reduce((vehs: any, vehicle: any) => {
if (!vehs[vehicle.type]) vehs[vehicle.type] = [];
vehs[vehicle.type].push(vehicle);
return vehs;
}, {});

setMappedVeh(mappedVehicles);
}
}, [vehicles]);
setMappedVeh(mappedVehicles);
}
}, [vehicles]);

return (
<StyledEngineProvider injectFirst>
<ThemeProvider theme={props.theme}>
<Container isDarkMode={isDarkMode}>
<Header>
<IconButton color="primary" onClick={() => history.goBack()}>
<ArrowBack />
</IconButton>
<Typography fontSize={24} color="primary" fontWeight="bold">
Garage
</Typography>
</Header>
{mappedVeh && <VehicleList isDarkMode={isDarkMode} vehicles={mappedVeh} />}
</Container>
</ThemeProvider>
</StyledEngineProvider>
);
return (
<StyledEngineProvider injectFirst>
<Container isDarkMode={isDarkMode}>
<Header>
<IconButton color="primary" onClick={() => history.goBack()}>
<ArrowBack/>
</IconButton>
<Typography fontSize={24} color="primary" fontWeight="bold">
Garage
</Typography>
</Header>
{mappedVeh && <VehicleList isDarkMode={true} vehicles={mappedVeh}/>}
</Container>
</StyledEngineProvider>
);
};

const WithProviders: React.FC<AppProps> = (props) => (
<NuiProvider>
<App {...props} />
</NuiProvider>
<RecoilRoot override key="npwd_qb_garage">
<App {...props} />
</RecoilRoot>
);

export default WithProviders;
47 changes: 47 additions & 0 deletions src/hooks/useNuiEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { MutableRefObject, useEffect, useRef } from 'react';

interface NuiMessageData<T = unknown> {
method: string;
data: T;
app: string;
}

type NuiHandlerSignature<T> = (data: T) => void;

/**
* A hook that manage events listeners for receiving data from the client scripts
* @param app
* @param action The specific `action` that should be listened for.
* @param handler The callback function that will handle data relayed by this hook
*
* @example
* useNuiEvent<{visibility: true, wasVisible: 'something'}>('setVisible', (data) => {
* // whatever logic you want
* })
*
**/

export const useNuiEvent = <T = any>(app: string, action: string, handler: (data: T) => void) => {
const savedHandler: MutableRefObject<NuiHandlerSignature<T>> = useRef();

// When handler value changes set mutable ref to handler val
useEffect(() => {
savedHandler.current = handler;
}, [handler]);

useEffect(() => {
const eventListener = (event: MessageEvent<NuiMessageData<T>>) => {
const { method: eventAction, app: tgtApp, data } = event.data;

if (savedHandler.current && savedHandler.current.call) {
if (eventAction === action && tgtApp === app) {
savedHandler.current(data);
}
}
};

window.addEventListener('message', eventListener);
// Remove Event Listener on component cleanup
return () => window.removeEventListener('message', eventListener);
}, [action, app]);
};
Loading

0 comments on commit 424edd0

Please sign in to comment.