mirror of
https://github.com/qbcore-fivem/qb-core.git
synced 2026-09-04 17:23:31 +00:00
Refactor core structure and notification UI
Major refactor to centralize QBCore and replace the web UI stack: - Move QBConfig into a unified QBCore.Config and initialize QBCore.PlayerData; add shared/functions.lua. - Remove legacy client/main.lua and server/main.lua exports and update fxmanifest to include shared/functions and drop those main scripts. - Update client event handling: adapt player data events to QBCore:Client:OnPlayerUpdated and trigger job/gang update events; remove deprecated exploitable handlers. - Web UI rewrite: remove Vue/Quasar dependency and config module; implement a lightweight vanilla JS notification system (html/js/app.js) with updated html and css assets, plus drawtext tweaks. - CSS overhaul for notification styling and animations; HTML head/meta/font cleanup. - Server fixes: use QBCore.Shared.Locations in teleport command and strengthen numeric parsing/validation for coords. - Add .gitattributes for consistent text/eol handling and small code cleanups (thread/comment removal). These changes consolidate config/shared logic under QBCore, simplify the frontend dependencies, and fix a few parsing/event issues.
This commit is contained in:
+104
-49
@@ -1,65 +1,120 @@
|
||||
import { determineStyleFromVariant, fetchNotifyConfig, NOTIFY_CONFIG } from "./config.js";
|
||||
let NOTIFY_CONFIG = null;
|
||||
|
||||
const { useQuasar } = Quasar;
|
||||
const { onMounted, onUnmounted } = Vue;
|
||||
const defaultConfig = {
|
||||
NotificationStyling: {
|
||||
group: false,
|
||||
position: "right",
|
||||
progress: true,
|
||||
},
|
||||
VariantDefinitions: {
|
||||
success: { classes: "success", icon: "check_circle" },
|
||||
primary: { classes: "primary", icon: "notifications" },
|
||||
warning: { classes: "warning", icon: "warning" },
|
||||
error: { classes: "error", icon: "error" },
|
||||
police: { classes: "police", icon: "local_police" },
|
||||
ambulance: { classes: "ambulance", icon: "fas fa-ambulance" },
|
||||
},
|
||||
};
|
||||
|
||||
const fetchNui = async (evName, data) => {
|
||||
const resourceName = window.GetParentResourceName();
|
||||
|
||||
const rawResp = await fetch(`https://${resourceName}/${evName}`, {
|
||||
const resp = await fetch(`https://${resourceName}/${evName}`, {
|
||||
body: JSON.stringify(data),
|
||||
headers: {
|
||||
"Content-Type": "application/json; charset=UTF8",
|
||||
},
|
||||
headers: { "Content-Type": "application/json; charset=UTF8" },
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
return await rawResp.json();
|
||||
return resp.json();
|
||||
};
|
||||
|
||||
window.fetchNui = fetchNui;
|
||||
const determineStyleFromVariant = (variant) => {
|
||||
return NOTIFY_CONFIG.VariantDefinitions[variant] ?? NOTIFY_CONFIG.VariantDefinitions["primary"];
|
||||
};
|
||||
|
||||
const app = Vue.createApp({
|
||||
setup() {
|
||||
const $q = useQuasar();
|
||||
const fetchNotifyConfig = async () => {
|
||||
try {
|
||||
NOTIFY_CONFIG = await fetchNui("getNotifyConfig", {});
|
||||
if (!NOTIFY_CONFIG) NOTIFY_CONFIG = defaultConfig;
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch notification config, using default", error);
|
||||
NOTIFY_CONFIG = defaultConfig;
|
||||
}
|
||||
};
|
||||
|
||||
const showNotif = async ({ data }) => {
|
||||
if (data?.action !== "notify") return;
|
||||
const POSITION_MAP = {
|
||||
"top-left": { top: "16px", left: "16px" },
|
||||
"top-right": { top: "16px", right: "16px" },
|
||||
top: { top: "16px", left: "50%", transform: "translateX(-50%)" },
|
||||
"bottom-left": { bottom: "16px", left: "16px" },
|
||||
"bottom-right": { bottom: "16px", right: "16px" },
|
||||
bottom: { bottom: "16px", left: "50%", transform: "translateX(-50%)" },
|
||||
left: { top: "50%", left: "16px", transform: "translateY(-50%)" },
|
||||
right: { top: "50%", right: "16px", transform: "translateY(-50%)" },
|
||||
center: { top: "50%", left: "50%", transform: "translate(-50%, -50%)" },
|
||||
};
|
||||
|
||||
const { text, length, type, caption, icon: dataIcon } = data;
|
||||
let { classes, icon } = determineStyleFromVariant(type);
|
||||
let container = null;
|
||||
|
||||
if (dataIcon) {
|
||||
icon = dataIcon;
|
||||
}
|
||||
const getContainer = () => {
|
||||
if (container) return container;
|
||||
const pos = NOTIFY_CONFIG.NotificationStyling.position ?? "right";
|
||||
const isBottom = pos.startsWith("bottom");
|
||||
container = document.createElement("div");
|
||||
container.id = "notify-container";
|
||||
Object.assign(container.style, {
|
||||
position: "fixed",
|
||||
display: "flex",
|
||||
flexDirection: isBottom ? "column-reverse" : "column",
|
||||
gap: "8px",
|
||||
zIndex: "9999",
|
||||
maxWidth: "400px",
|
||||
pointerEvents: "none",
|
||||
...(POSITION_MAP[pos] ?? POSITION_MAP["right"]),
|
||||
});
|
||||
document.body.appendChild(container);
|
||||
return container;
|
||||
};
|
||||
|
||||
if (!NOTIFY_CONFIG) {
|
||||
console.error("The notification config did not load properly, trying again for next time");
|
||||
await fetchNotifyConfig();
|
||||
if (NOTIFY_CONFIG) return showNotif({ data });
|
||||
}
|
||||
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
$q.notify({
|
||||
message: text,
|
||||
multiLine: text.length > 100,
|
||||
group: NOTIFY_CONFIG.NotificationStyling.group ?? false,
|
||||
progress: NOTIFY_CONFIG.NotificationStyling.progress ?? true,
|
||||
position: NOTIFY_CONFIG.NotificationStyling.position ?? "right",
|
||||
timeout: length,
|
||||
caption,
|
||||
classes,
|
||||
icon,
|
||||
});
|
||||
};
|
||||
onMounted(() => {
|
||||
window.addEventListener("message", showNotif);
|
||||
});
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener("message", showNotif);
|
||||
});
|
||||
return {};
|
||||
},
|
||||
});
|
||||
const showNotif = async ({ data }) => {
|
||||
if (data?.action !== "notify") return;
|
||||
|
||||
app.use(Quasar, { config: {} });
|
||||
app.mount("#q-app");
|
||||
if (!NOTIFY_CONFIG) {
|
||||
await fetchNotifyConfig();
|
||||
}
|
||||
|
||||
const { text: message, type, length: duration = 5000, caption } = data;
|
||||
const style = determineStyleFromVariant(type ?? "primary");
|
||||
const showProgress = NOTIFY_CONFIG.NotificationStyling.progress && duration > 0;
|
||||
|
||||
const isFa = style.icon.startsWith("fa");
|
||||
const iconHtml = isFa ? `<i class="notify-icon ${style.icon}"></i>` : `<span class="notify-icon material-icons">${style.icon}</span>`;
|
||||
|
||||
const item = document.createElement("div");
|
||||
item.className = `notify-item ${style.classes}`;
|
||||
item.innerHTML = `
|
||||
${iconHtml}
|
||||
<div class="notify-content">
|
||||
<div class="notify-message${!caption ? " notify-multiline" : ""}">${message}</div>
|
||||
${caption ? `<div class="notify-caption">${caption}</div>` : ""}
|
||||
</div>
|
||||
${showProgress ? `<div class="notify-progress" style="animation-duration:${duration}ms"></div>` : ""}
|
||||
`;
|
||||
|
||||
const c = getContainer();
|
||||
c.appendChild(item);
|
||||
|
||||
await sleep(10);
|
||||
item.classList.add("notify-show");
|
||||
|
||||
if (duration > 0) {
|
||||
setTimeout(() => {
|
||||
item.classList.remove("notify-show");
|
||||
item.classList.add("notify-hide");
|
||||
setTimeout(() => item.remove(), 350);
|
||||
}, duration);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("message", showNotif);
|
||||
window.addEventListener("load", fetchNotifyConfig);
|
||||
|
||||
Reference in New Issue
Block a user