mirror of
https://github.com/qbcore-fivem/qb-core.git
synced 2026-08-28 17:01:16 +00:00
refactor(ui/notify): Cleanup internal code & new config options
This commit is contained in:
@@ -58,6 +58,10 @@ function QBCore.Functions.DrawText3D(x, y, z, text) -- Use local function instea
|
||||
ClearDrawOrigin()
|
||||
end
|
||||
|
||||
RegisterNUICallback('getNotifyConfig', function(_, cb)
|
||||
cb(QBCore.Config.Notify)
|
||||
end)
|
||||
|
||||
function QBCore.Functions.Notify(text, textype, length)
|
||||
if type(text) == "table" then
|
||||
local ttext = text.text or 'Placeholder'
|
||||
@@ -65,6 +69,7 @@ function QBCore.Functions.Notify(text, textype, length)
|
||||
local ttype = textype or 'primary'
|
||||
local length = length or 5000
|
||||
SendNUIMessage({
|
||||
action = 'notify',
|
||||
type = ttype,
|
||||
length = length,
|
||||
text = ttext,
|
||||
@@ -73,7 +78,8 @@ function QBCore.Functions.Notify(text, textype, length)
|
||||
else
|
||||
local ttype = textype or 'primary'
|
||||
local length = length or 5000
|
||||
SendNUIMessage({
|
||||
SendNUIMessage({
|
||||
action = 'notify',
|
||||
type = ttype,
|
||||
length = length,
|
||||
text = text
|
||||
|
||||
+34
@@ -33,3 +33,37 @@ QBConfig.Server.uptime = 0 -- Time the server has been up.
|
||||
QBConfig.Server.whitelist = false -- Enable or disable whitelist on the server
|
||||
QBConfig.Server.discord = "" -- Discord invite link
|
||||
QBConfig.Server.PermissionList = {} -- permission list
|
||||
|
||||
QBConfig.Notify = {}
|
||||
|
||||
QBConfig.Notify.NotificationStyling = {
|
||||
group = false, -- Allow notifications to stack with a badge instead of repeating
|
||||
position = "right", -- top-left | top-right | bottom-left | bottom-right | top | bottom | left | right | center
|
||||
progress = true -- Display Progress Bar
|
||||
}
|
||||
|
||||
-- These are how you define different notification variants
|
||||
-- The "color" key is background of the notification
|
||||
-- The "icon" key is the css-icon code, this project uses `Material Icons` & `Font Awesome`
|
||||
QBconfig.Notify.VariantDefinitions = {
|
||||
success = {
|
||||
color = 'green',
|
||||
icon = 'done'
|
||||
},
|
||||
primary = {
|
||||
color = 'blue',
|
||||
icon = 'info'
|
||||
},
|
||||
error = {
|
||||
color = 'red',
|
||||
icon = 'dangerous'
|
||||
},
|
||||
police = {
|
||||
color = 'blue',
|
||||
icon = 'local_police'
|
||||
},
|
||||
ambulance = {
|
||||
color = 'red',
|
||||
icon = 'fas fa-ambulance'
|
||||
}
|
||||
}
|
||||
+44
-41
@@ -1,63 +1,66 @@
|
||||
import { registerWindowMethods } from "./testing.js";
|
||||
import { isEnvBrowser } from "./utils.js";
|
||||
import {
|
||||
determineStyleFromVariant,
|
||||
DEV_MODE,
|
||||
fetchNotifyConfig,
|
||||
NOTIFY_CONFIG,
|
||||
} from "./config.js";
|
||||
|
||||
const { useQuasar } = Quasar;
|
||||
|
||||
const { onMounted, onUnmounted } = Vue;
|
||||
|
||||
const app = Vue.createApp({
|
||||
setup() {
|
||||
const $q = useQuasar();
|
||||
const showNotif = (e) => {
|
||||
const text = e.data.text;
|
||||
const length = e.data.length;
|
||||
const type = e.data.type;
|
||||
const caption = e.data.caption;
|
||||
|
||||
switch (type) {
|
||||
case 'success':
|
||||
color = 'green';
|
||||
icon = 'done';
|
||||
break;
|
||||
case 'primary':
|
||||
color = 'blue';
|
||||
icon = 'info';
|
||||
break;
|
||||
case 'error':
|
||||
color = 'red';
|
||||
icon = 'dangerous';
|
||||
break;
|
||||
case 'police':
|
||||
color = 'blue';
|
||||
icon = 'local_police';
|
||||
break;
|
||||
case 'ambulance':
|
||||
color = 'red';
|
||||
icon = 'fas fa-ambulance';
|
||||
break;
|
||||
}
|
||||
const showNotif = async ({ data }) => {
|
||||
// Otherwise we process any old MessageEvent with a data property
|
||||
if (data?.action !== "notify") return;
|
||||
|
||||
if (text.length > 100) {
|
||||
multiline = true;
|
||||
} else {
|
||||
multiline = false;
|
||||
const { text, length, type, caption } = data;
|
||||
const { color, icon } = determineStyleFromVariant(type);
|
||||
|
||||
// Make sure we have sucessfully fetched out config properly
|
||||
if (!NOTIFY_CONFIG) {
|
||||
console.error(
|
||||
"The notification config did not load properly, trying again for next time"
|
||||
);
|
||||
// Lets check again to see if it exists
|
||||
await fetchNotifyConfig();
|
||||
// If we have a config lets re-run notification with same data, this
|
||||
// isn't recursive though.
|
||||
if (NOTIFY_CONFIG) return showNotif({ data });
|
||||
}
|
||||
|
||||
$q.notify({
|
||||
message: text,
|
||||
caption: caption,
|
||||
multiLine: multiline,
|
||||
color: color,
|
||||
group: false,
|
||||
progress: true,
|
||||
position: 'right',
|
||||
multiLine: text.length > 100,
|
||||
// If our text is larger than a 100 characters,
|
||||
// we should use multiline notifications
|
||||
group: NOTIFY_CONFIG.NotificationStyling.group ?? false,
|
||||
progress: NOTIFY_CONFIG.NotificationStyling.progress ?? true,
|
||||
position: NOTIFY_CONFIG.NotificationStyling.position ?? "right",
|
||||
timeout: length,
|
||||
icon: icon,
|
||||
caption,
|
||||
color,
|
||||
icon,
|
||||
});
|
||||
};
|
||||
onMounted(() => {
|
||||
window.addEventListener('message', showNotif);
|
||||
window.addEventListener("message", showNotif);
|
||||
});
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('message', showNotif);
|
||||
window.removeEventListener("message", showNotif);
|
||||
});
|
||||
return {};
|
||||
},
|
||||
});
|
||||
|
||||
app.use(Quasar, { config: {} });
|
||||
app.mount('#q-app');
|
||||
app.mount("#q-app");
|
||||
|
||||
if (DEV_MODE || isEnvBrowser()) {
|
||||
registerWindowMethods();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { fetchNui, isEnvBrowser } from "./utils.js";
|
||||
import { BrowserMockConfigData } from "./testing.js";
|
||||
|
||||
export const DEV_MODE = false;
|
||||
|
||||
/**
|
||||
* @typedef NotiVariantData
|
||||
* @property {string} icon
|
||||
* @property {string} color
|
||||
**/
|
||||
|
||||
/**
|
||||
* Will hold config statically outside of Vue state
|
||||
* @property {Record<string, NotiVariantData>} VariantDefinitions
|
||||
* @property {Record<string, any>} NotificationStyling
|
||||
* */
|
||||
export let NOTIFY_CONFIG = null;
|
||||
|
||||
/**
|
||||
* Pure function taking a notification type and returning an object
|
||||
* with style details
|
||||
* @param variant {string}
|
||||
* @returns NotiVariantData
|
||||
**/
|
||||
export const determineStyleFromVariant = (variant) => {
|
||||
const variantData = NOTIFY_CONFIG.VariantDefinitions[variant];
|
||||
if (!variantData)
|
||||
throw new Error(`Style of type: ${variant}, does not exist in the config`);
|
||||
return variantData;
|
||||
};
|
||||
|
||||
// Fetch and set NOTIFY_CONFIG from client script callback
|
||||
export const fetchNotifyConfig = async () => {
|
||||
NOTIFY_CONFIG = await fetchNui("getNotifyConfig", {}, BrowserMockConfigData);
|
||||
if (isEnvBrowser() || DEV_MODE) {
|
||||
console.log("Fetched Config:");
|
||||
console.dir(NOTIFY_CONFIG);
|
||||
}
|
||||
};
|
||||
|
||||
// We specifically wait for all other files to load
|
||||
// just in case of a race condition between client handlers
|
||||
// and NUI fetch call
|
||||
window.addEventListener("load", async () => {
|
||||
await fetchNotifyConfig();
|
||||
});
|
||||
+1
-1
@@ -26,7 +26,7 @@
|
||||
src="https://cdn.jsdelivr.net/npm/quasar@2.1.0/dist/quasar.umd.prod.js"
|
||||
defer
|
||||
></script>
|
||||
<script src="app.js" defer></script>
|
||||
<script type="module" src="app.js"></script>
|
||||
</head>
|
||||
<body style="font-family: 'Poppins', sans-serif">
|
||||
<div
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// Will register dev utilities on window
|
||||
export const registerWindowMethods = () => {
|
||||
window.SendNotification = (data) => {
|
||||
window.dispatchEvent(
|
||||
new MessageEvent("message", {
|
||||
data: {
|
||||
action: "notify",
|
||||
...data,
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
// Used for browser env handling
|
||||
export const BrowserMockConfigData = {
|
||||
NotificationStyling: {
|
||||
group: true,
|
||||
position: "top-right",
|
||||
progress: true,
|
||||
},
|
||||
VariantDefinitions: {
|
||||
success: {
|
||||
color: "green",
|
||||
icon: "done",
|
||||
},
|
||||
primary: {
|
||||
color: "blue",
|
||||
icon: "info",
|
||||
},
|
||||
error: {
|
||||
color: "red",
|
||||
icon: "dangerous",
|
||||
},
|
||||
police: {
|
||||
color: "blue",
|
||||
icon: "local_police",
|
||||
},
|
||||
ambulance: {
|
||||
color: "red",
|
||||
icon: "fas fa-ambulance",
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
// Returns whether we are in browser or not
|
||||
export const isEnvBrowser = () => !window.invokeNative;
|
||||
|
||||
/**
|
||||
* Real simple wrapper around fetch api for NUI focus
|
||||
* it will return the mockData param if we are in browser. So we don't
|
||||
* make a useless request to a hostname that doesn't exist.
|
||||
* @param evName {string} - The callback event name/type
|
||||
* @param data {any} - Optional `body` data JSON stringified & passed with the request
|
||||
* @param mockData {any} - Mock data to return if this is running in browser
|
||||
* @return Promise
|
||||
**/
|
||||
export const fetchNui = async (evName, data, mockData = null) => {
|
||||
if (isEnvBrowser()) return mockData;
|
||||
|
||||
const resourceName = window.GetParentResourceName();
|
||||
|
||||
const rawResp = await fetch(`https://cfx-nui-${resourceName}/${evName}`, {
|
||||
body: JSON.stringify(data),
|
||||
headers: {
|
||||
"Content-Type": "application/json; charset=UTF8",
|
||||
},
|
||||
});
|
||||
|
||||
return await rawResp.json();
|
||||
};
|
||||
Reference in New Issue
Block a user