From 7a1eef95ff0091090966e4f27b19e421b5a1fbc3 Mon Sep 17 00:00:00 2001 From: zlexif <146517988+auradevelopment5m@users.noreply.github.com> Date: Mon, 5 May 2025 19:32:19 +0300 Subject: [PATCH 1/3] refactor(esx_notify): redesign, code improvements. --- [core]/esx_notify/Notify.lua | 55 ++++--- [core]/esx_notify/fxmanifest.lua | 2 +- [core]/esx_notify/nui/css/style.css | 221 +++++++++++++++++++------- [core]/esx_notify/nui/index.html | 24 +-- [core]/esx_notify/nui/js/script.js | 237 ++++++++++++++++++++-------- 5 files changed, 383 insertions(+), 156 deletions(-) diff --git a/[core]/esx_notify/Notify.lua b/[core]/esx_notify/Notify.lua index 26dacd06..a0503f6f 100644 --- a/[core]/esx_notify/Notify.lua +++ b/[core]/esx_notify/Notify.lua @@ -3,11 +3,13 @@ local Debug = ESX.GetConfig().EnableDebug ---@param notificatonType string the notification type ---@param length number the length of the notification ---@param message any the message :D -local function Notify(notificatonType, length, message) +---@param title string optional title for the notification +local function Notify(notificatonType, length, message, title) if Debug then - print(("1 %s"):format(tostring(notificatonType))) - print(("2 %s"):format(tostring(length))) - print(("3 %s"):format(message)) + print("1 ".. tostring(notificatonType)) + print("2 "..tostring(length)) + print("3 "..message) + print("4 "..tostring(title)) end if type(notificatonType) ~= "string" then @@ -19,40 +21,49 @@ local function Notify(notificatonType, length, message) end if Debug then - print(("4 %s"):format(tostring(notificatonType))) - print(("5 %s"):format(tostring(length))) - print(("6 %s"):format(message)) + print("5 ".. tostring(notificatonType)) + print("6 "..tostring(length)) + print("7 "..message) + print("8 "..tostring(title)) + end + + if type(message) == "string" then + message = message:gsub("~br~", "
") end SendNuiMessage(json.encode({ - type = notificatonType, - length = length, + type = notificatonType or "info", + length = length or 5000, message = message or "ESX-Notify", + title = title or "New Notification" })) end -exports("Notify", Notify) -ESX.SecureNetEvent("ESX:Notify", Notify) +exports('Notify', Notify) +RegisterNetEvent("ESX:Notify", Notify) if Debug then RegisterCommand("oldnotify", function() - ---@diagnostic disable-next-line - ESX.ShowNotification("No Waypoint Set.", true, false, 140) - end, false) + ESX.ShowNotification('No Waypoint Set.', true, false, 140) + end) RegisterCommand("notify", function() - ESX.ShowNotification("You Recived ~br~ 1x ball~s~!", "success", 3000) - end, false) + ESX.ShowNotification("You Received
1x ball!", "success", 3000) + end) RegisterCommand("notify1", function() - ESX.ShowNotification("Well ~g~Done~s~!", "success", 3000) - end, false) + ESX.ShowNotification("Well ~g~Done~s~!", "success", 3000, "Achievement") + end) RegisterCommand("notify2", function() - ESX.ShowNotification("Information Recived", "info", 3000) - end, false) + ESX.ShowNotification("Information Received", "info", 3000, "System Info") + end) RegisterCommand("notify3", function() - ESX.ShowNotification("You Did something ~r~WRONG~s~!", "error", 3000) - end, false) + ESX.ShowNotification("You Did something ~r~WRONG~s~!", "error", 3000, "Error") + end) + + RegisterCommand("notify4", function() + ESX.ShowNotification("You Did something ~r~WRONG~s~!", "warning", 3000, "~y~Warning~s~") + end) end diff --git a/[core]/esx_notify/fxmanifest.lua b/[core]/esx_notify/fxmanifest.lua index a7e1b45d..55862551 100644 --- a/[core]/esx_notify/fxmanifest.lua +++ b/[core]/esx_notify/fxmanifest.lua @@ -2,7 +2,7 @@ fx_version 'adamant' lua54 'yes' game 'gta5' -version '1.12.4' +version '1.13.0' author 'ESX-Framework' description 'A beautiful and simple NUI notification system for ESX' diff --git a/[core]/esx_notify/nui/css/style.css b/[core]/esx_notify/nui/css/style.css index 6dc29290..acb1b142 100644 --- a/[core]/esx_notify/nui/css/style.css +++ b/[core]/esx_notify/nui/css/style.css @@ -1,87 +1,196 @@ -@import url("https://fonts.googleapis.com/css2?family=Montserrat:wght@100&family=Poppins:wght@300;400;500;600;800&display=swap"); +@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600&display=swap"); :root { - --color: #919191; + --background: rgba(20, 20, 20, 0.9); + --text-color: #ffffff; + --text-secondary: rgba(255, 255, 255, 0.7); + --success-color: #2ecc71; + --success-border: #1f9c4d; + --error-color: #e74c3c; + --error-border: #c0392b; + --info-color: #3498db; + --info-border: #2980b9; + --warning-color: #f39c12; + --warning-border: #d35400; } * { - border: 0; - margin: 0; - padding: 0; - box-sizing: border-box; + margin: 0; + padding: 0; + box-sizing: border-box; } body { - width: 100vw; - height: 100vh; - color: var(--color); - font-weight: 100; - font-family: "Poppins", sans-serif; - overflow: hidden; + width: 100vw; + height: 100vh; + font-family: "Poppins", sans-serif; + overflow: hidden; } #root { - display: grid; - justify-content: center; + position: fixed; + right: 2rem; + top: 50%; + transform: translateY(-50%); + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 10px; + max-height: 80vh; + z-index: 1000; } -#root .notify { - display: flex; - position: relative; - flex: auto; - min-width: 20rem; - width: fit-content; - height: 3.5rem; - background: rgba(15, 15, 15, 0.9); - border-radius: 0.4rem; - margin-top: 0.5rem; - animation: anim 300ms ease-in-out; - align-items: center; +.notify { + display: flex; + width: 300px; + background: var(--background); + border-radius: 8px; + overflow: hidden; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + position: relative; + padding: 12px; + align-items: center; + border-left: 4px solid #555; } -#root .innerText { - padding-left: 0.4rem; - padding-right: 0.4rem; - width: 100%; +.notify-success { + border-left-color: var(--success-border); } -#root .icon { - float: left; +.notify-error { + border-left-color: var(--error-border); } -#root .text { - display: inline-block; - color: #fff; - margin-left: 0.5rem; +.notify-info { + border-left-color: var(--info-border); } -#root .error { - color: #c0392b; - border: 2px solid #c0392b; +.notify-warning { + border-left-color: var(--warning-border); } -#root .success { - color: #2ecc71; - border: 2px solid #2ecc71; +.notify-icon-container { + margin-right: 12px; + display: flex; + align-items: center; + justify-content: center; } -#root .info { - color: #fb9b04; - border: 2px solid #fb9b04; +.hexagon { + position: relative; + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + clip-path: polygon(50% 0%, 95% 25%, 95% 75%, 50% 100%, 5% 75%, 5% 25%); + background: linear-gradient(145deg, var(--icon-color), rgba(0, 0, 0, 0.7)); + box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); +} + +.hexagon::before { + content: ""; + position: absolute; + top: 3px; + left: 3px; + right: 3px; + bottom: 3px; + background: var(--background); + clip-path: polygon(50% 0%, 95% 25%, 95% 75%, 50% 100%, 5% 75%, 5% 25%); + z-index: 1; +} + +.hexagon::after { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(145deg, var(--icon-color) 10%, transparent 70%); + opacity: 0.4; + clip-path: polygon(50% 0%, 95% 25%, 95% 75%, 50% 100%, 5% 75%, 5% 25%); + z-index: 2; +} + +.hexagon .material-symbols-outlined { + color: var(--icon-color); + font-size: 20px; + position: relative; + z-index: 3; + filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.5)); + display: flex; + align-items: center; + justify-content: center; + height: 24px; + width: 24px; + margin-top: -1px; +} + +.notify-content { + flex: 1; + position: relative; + padding-bottom: 8px; +} + +.notify-title { + color: var(--text-color); + font-size: 14px; + font-weight: 600; + margin-bottom: 2px; +} + +.notify-text { + color: var(--text-secondary); + font-size: 12px; + font-weight: 400; +} + +.notify-progress { + position: absolute; + bottom: 0; + left: 0; + height: 2px; + width: 100%; + border-radius: 1px; } .material-symbols-outlined { - font-variation-settings: "FILL" 0, "wght" 400, "GRAD" 0, "opsz" 48; + font-variation-settings: "FILL" 1, "wght" 400, "GRAD" 0, "opsz" 48; } -@keyframes anim { - 0% { - transform: scaleY(0); - } - 80% { - transform: scaleY(1.1); - } - 100% { - transform: scaleY(1); - } +.notify-warning .material-symbols-outlined, +.notify-error .material-symbols-outlined { + margin-top: -2px; +} + +.fadeIn { + animation: fadeIn 0.3s ease-in-out; +} + +.fadeOut { + animation: fadeOut 0.5s ease-in-out; + opacity: 0; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateX(50px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +@keyframes fadeOut { + from { + opacity: 1; + transform: translateX(0); + } + to { + opacity: 0; + transform: translateX(50px); + } } diff --git a/[core]/esx_notify/nui/index.html b/[core]/esx_notify/nui/index.html index 69800a2d..76a62faa 100644 --- a/[core]/esx_notify/nui/index.html +++ b/[core]/esx_notify/nui/index.html @@ -1,15 +1,15 @@ - - - - - - ESX Notify - - -
- -
- + + + + + ESX Notifications + + +
+ +
+ + diff --git a/[core]/esx_notify/nui/js/script.js b/[core]/esx_notify/nui/js/script.js index 64002521..b2a24bd3 100644 --- a/[core]/esx_notify/nui/js/script.js +++ b/[core]/esx_notify/nui/js/script.js @@ -1,83 +1,190 @@ -const w = window; +const w = window -// Gets the current icon it needs to use. const types = { - ["success"]: { - ["icon"]: "check_circle", - }, - ["error"]: { - ["icon"]: "error", - }, - ["info"]: { - ["icon"]: "info", - }, -}; + ["success"]: { + ["icon"]: "check_circle", + ["frequency"]: 800, + ["duration"]: 100, + ["color"]: "#2ecc71", + ["borderColor"]: "#1f9c4d", + }, + ["error"]: { + ["icon"]: "error", + ["frequency"]: 300, + ["duration"]: 150, + ["color"]: "#e74c3c", + ["borderColor"]: "#c0392b", + }, + ["info"]: { + ["icon"]: "info", + ["frequency"]: 600, + ["duration"]: 100, + ["color"]: "#3498db", + ["borderColor"]: "#2980b9", + }, + ["warning"]: { + ["icon"]: "warning_amber", + ["frequency"]: 450, + ["duration"]: 125, + ["color"]: "#f39c12", + ["borderColor"]: "#d35400", + }, +} // the color codes example `i ~r~love~s~ donuts` const codes = { - "~r~": "#c0392b", - "~b~": "#378cbf", - "~g~": "#2ecc71", - "~y~": "yellow", - "~p~": "purple", - "~c~": "grey", - "~m~": "#212121", - "~u~": "black", - "~o~": "#fb9b04", -}; + "~r~": "#c0392b", + "~b~": "#378cbf", + "~g~": "#2ecc71", + "~y~": "yellow", + "~p~": "purple", + "~c~": "grey", + "~m~": "#212121", + "~u~": "black", + "~o~": "#fb9b04", +} + +// Audio context for sound effects +let audioContext + +// Initialize audio context on user interaction +document.addEventListener("click", initAudioContext, { once: true }) +document.addEventListener("keydown", initAudioContext, { once: true }) + +function initAudioContext() { + if (!audioContext) { + audioContext = new (window.AudioContext || window.webkitAudioContext)() + } +} + +function playNotificationSound(type) { + if (!audioContext) initAudioContext() + + const typeConfig = types[type] || types["info"] + const oscillator = audioContext.createOscillator() + const gainNode = audioContext.createGain() + + oscillator.type = "sine" + oscillator.frequency.setValueAtTime(typeConfig.frequency, audioContext.currentTime) + + gainNode.gain.setValueAtTime(0.3, audioContext.currentTime) + gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + typeConfig.duration / 1000) + + oscillator.connect(gainNode) + gainNode.connect(audioContext.destination) + + oscillator.start() + oscillator.stop(audioContext.currentTime + typeConfig.duration / 1000) +} w.addEventListener("message", (event) => { - notification({ - type: event.data.type, - message: event.data.message, - length: event.data.length, - }); -}); + notification({ + type: event.data.type, + title: event.data.title || "New Notification", + message: event.data.message, + length: event.data.length, + }) +}) const replaceColors = (str, obj) => { - let strToReplace = str; + let strToReplace = str - for (let id in obj) { - strToReplace = strToReplace.replace(new RegExp(id, "g"), obj[id]); + for (const id in obj) { + strToReplace = strToReplace.replace(new RegExp(id, "g"), obj[id]) + } + + return strToReplace +} + +const sanitizeHTML = (str) => { + const temp = document.createElement("div") + temp.textContent = str + return temp.innerHTML +} + +const processLineBreaks = (str) => { + // Replace
tags with actual line breaks + return str.replace(/<br>/g, "
") +} + +const notification = (data) => { + if (typeof $ === "undefined") { + console.error("jQuery is not loaded. Please ensure jQuery is included in your project.") + return + } + + if (data.message) { + // Remove any standalone ~s~ tags (not preceded by a color code) + data.message = data.message.replace(/~s~/g, "") + } + + if (data.title) { + // Remove any standalone ~s~ tags (not preceded by a color code) + data.title = data.title.replace(/~s~/g, "") + } + + let sanitizedTitle = data.title ? sanitizeHTML(data.title) : "" + let sanitizedMessage = data.message ? sanitizeHTML(data.message) : "" + + if (data.title) { + for (const color in codes) { + if (sanitizedTitle.includes(color)) { + const objArr = {} + objArr[color] = `` + objArr["~s~"] = "" + sanitizedTitle = replaceColors(sanitizedTitle, objArr) + } } + } - return strToReplace; -}; - -notification = (data) => { - for (color in codes) { - if (data["message"].includes(color)) { - let objArr = {}; - objArr[color] = ``; - objArr["~s~"] = ""; - - let newStr = replaceColors(data["message"], objArr); - - data["message"] = newStr; - } + for (const color in codes) { + if (sanitizedMessage.includes(color)) { + const objArr = {} + objArr[color] = `` + objArr["~s~"] = "" + sanitizedMessage = replaceColors(sanitizedMessage, objArr) } + } - const id = Math.floor(Math.random() * Math.random()); - const notification = $(` -
-
- ${types[data.type] ? types[data.type]["icon"] : types["info"]["icon"]} -

${data["message"]}

-
+ sanitizedMessage = processLineBreaks(sanitizedMessage) + sanitizedTitle = processLineBreaks(sanitizedTitle) + + const id = Math.floor(Math.random() * 100000) + const typeInfo = types[data.type] || types["info"] + const duration = data.length || 3000 + + const notificationElement = $(` +
+
+
+ ${typeInfo.icon}
- `).appendTo(`#root`); +
+
+

${sanitizedTitle}

+

${sanitizedMessage}

+
+
+
+ `).appendTo(`#root`) + $(`#${id} .notify-progress`).css({ + transition: `width ${duration}ms linear`, + width: "0%", + }) + + playNotificationSound(data.type) + + setTimeout(() => { + $(`#${id} .notify-progress`).css("width", "100%") + }, 10) + + setTimeout(() => { + $(`#${id}`).removeClass("fadeIn").addClass("fadeOut") setTimeout(() => { - document.getElementById(id).classList.remove("fadeIn"); - }, 300); + $(`#${id}`).remove() + }, 500) + }, duration) - setTimeout(() => { - document.getElementById(id).classList.add("fadeOut"); - - setTimeout(() => { - document.getElementById(id).remove(); - }, 400); - },data.length); - - return notification; -}; + return notificationElement +} From 59d2e9a839249f26881f1aa263d3e75343b5996e Mon Sep 17 00:00:00 2001 From: zlexif <146517988+auradevelopment5m@users.noreply.github.com> Date: Mon, 5 May 2025 19:49:07 +0300 Subject: [PATCH 2/3] fix(readme esx_notify): --- [core]/esx_notify/readme.md | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/[core]/esx_notify/readme.md b/[core]/esx_notify/readme.md index f6bc4c9d..430f8a34 100644 --- a/[core]/esx_notify/readme.md +++ b/[core]/esx_notify/readme.md @@ -1,33 +1,48 @@ -

[ESX] Notify

Discord - Website - Documentation +`

`[ESX] Notify```

``

`````Discord`` - ``Website`` - ``Documentation`````` A beautiful and simple NUI notification system for ESX # Example Code -

Change style and time

+`

`Change style and time`

` ```lua ---usage: message/type/length ESX.ShowNotification("message here", "error", 3000) ESX.ShowNotification("message here", "success", 3000) ESX.ShowNotification("message here", "info", 3000) +ESX.ShowNotification("message here", "warning", 3000) ESX.ShowNotification("text here") -- Default will time and type will be info/3000 + +-- With title (optional 4th parameter) +ESX.ShowNotification("message here", "success", 3000, "Achievement") ``` -

Export Usage

+`

`Export Usage`

` ```lua +-- Basic usage exports["esx_notify"]:Notify("info", 3000, "message here") + +-- With title +exports["esx_notify"]:Notify("success", 3000, "Item purchased!", "Shop") + +-- With line break +exports["esx_notify"]:Notify("warning", 4000, "Inventory full!~br~Some items were dropped.", "Warning") ``` -

Event Usage

+`

`Event Usage`

` ```lua +-- Basic usage TriggerEvent("ESX:Notify", "info", 3000, "message here") + +-- With title +TriggerEvent("ESX:Notify", "error", 5000, "You don't have enough money!", "Transaction Failed") ``` -

Color Code Usage

+`

`Color Code Usage`

` ```lua ~r~ = Red @@ -41,15 +56,14 @@ TriggerEvent("ESX:Notify", "info", 3000, "message here") ~o~ = Orange ESX.ShowNotification("I i ~r~love~s~ donuts", "success", 3000) + +-- Line break example +ESX.ShowNotification("You received ~br~1x ~g~ball~s~!", "success", 3000, "Item Received") ``` # Previews -![Preview_1](https://cdn.discordapp.com/attachments/944789399852417096/997890963445927977/unknown.png) - -![Preview_2_zoom](https://cdn.discordapp.com/attachments/944789399852417096/997892214053163148/unknown.png) - -![Preview_2](https://cdn.discordapp.com/attachments/944789399852417096/997891726326898909/unknown.png) +![Preview 1](https://r2.fivemanage.com/gWoWHGuKZdsK8PFzaVuGC/image_2025-05-05_194204916.png) ## Legal @@ -61,4 +75,4 @@ This program Is free software: you can redistribute it And/Or modify it under th This program Is distributed In the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty Of MERCHANTABILITY Or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License For more details. -You should have received a copy Of the GNU General Public License along with this program. If Not, see . +You should have received a copy Of the GNU General Public License along with this program. If Not, see ``. \ No newline at end of file From 7d61f378e2d2520cf4575674e29ca7919ffe0f17 Mon Sep 17 00:00:00 2001 From: zlexif <146517988+auradevelopment5m@users.noreply.github.com> Date: Thu, 8 May 2025 21:53:34 +0300 Subject: [PATCH 3/3] fix:('added title parameter to ESX.ShowNotification. --- [core]/es_extended/client/functions.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/[core]/es_extended/client/functions.lua b/[core]/es_extended/client/functions.lua index 8a577e7e..1ad0edc3 100644 --- a/[core]/es_extended/client/functions.lua +++ b/[core]/es_extended/client/functions.lua @@ -152,9 +152,10 @@ end ---@param message string The message to show ---@param notifyType? string The type of notification to show ---@param length? number The length of the notification +---@param title? string The title of the notification ---@return nil -function ESX.ShowNotification(message, notifyType, length) - return IsResourceFound('esx_notify') and exports['esx_notify']:Notify(notifyType, length, message) +function ESX.ShowNotification(message, notifyType, length, title) + return IsResourceFound('esx_notify') and exports['esx_notify']:Notify(notifyType, length, message, title) end function ESX.TextUI(...)