refactor(ui/notify): Cleanup internal code & new config options

This commit is contained in:
Taso
2021-10-13 19:39:59 -04:00
parent 57e895de19
commit e719bc6569
7 changed files with 202 additions and 43 deletions
+7 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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();
}
+46
View File
@@ -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
View File
@@ -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
+44
View File
@@ -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",
},
},
};
+26
View File
@@ -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();
};