From 41a185e8f1ba2cd420f47f354eef7e65e0d6a325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Tue, 1 Aug 2017 11:00:23 +0200 Subject: [PATCH 01/76] Initial commit --- README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..2a2878fd --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# fxserver-esx_phone +FXServer esx_phone From d90aecf2b5d0a28b054b9fe2bd0b060d659a41fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Tue, 1 Aug 2017 11:07:23 +0200 Subject: [PATCH 02/76] Add files --- README.md | 14 ++ __resource.lua | 35 +++ client/main.lua | 265 +++++++++++++++++++++ esx_phone.sql | 11 + html/bankgothic.ttf | Bin 0 -> 36272 bytes html/css/app.css | 392 +++++++++++++++++++++++++++++++ html/img/cursor.png | Bin 0 -> 272 bytes html/img/icons/add.png | Bin 0 -> 995 bytes html/img/icons/back.png | Bin 0 -> 1105 bytes html/img/icons/edit.png | Bin 0 -> 236 bytes html/img/icons/location.png | Bin 0 -> 345 bytes html/img/icons/msg.png | Bin 0 -> 1205 bytes html/img/icons/new-msg.png | Bin 0 -> 1196 bytes html/img/icons/ok.png | Bin 0 -> 241 bytes html/img/icons/rep.png | Bin 0 -> 1504 bytes html/img/icons/reply.png | Bin 0 -> 260 bytes html/img/icons/signal.png | Bin 0 -> 1102 bytes html/img/icons/write.png | Bin 0 -> 250 bytes html/img/keys/enter.png | Bin 0 -> 941 bytes html/img/keys/return.png | Bin 0 -> 376 bytes html/img/phone.png | Bin 0 -> 18906 bytes html/img/pointer.png | Bin 0 -> 2639 bytes html/pdown.ttf | Bin 0 -> 151512 bytes html/scripts/app.js | 434 +++++++++++++++++++++++++++++++++++ html/scripts/mustache.min.js | 1 + html/ui.html | 82 +++++++ server/main.lua | 309 +++++++++++++++++++++++++ 27 files changed, 1543 insertions(+) create mode 100644 __resource.lua create mode 100644 client/main.lua create mode 100644 esx_phone.sql create mode 100644 html/bankgothic.ttf create mode 100644 html/css/app.css create mode 100644 html/img/cursor.png create mode 100644 html/img/icons/add.png create mode 100644 html/img/icons/back.png create mode 100644 html/img/icons/edit.png create mode 100644 html/img/icons/location.png create mode 100644 html/img/icons/msg.png create mode 100644 html/img/icons/new-msg.png create mode 100644 html/img/icons/ok.png create mode 100644 html/img/icons/rep.png create mode 100644 html/img/icons/reply.png create mode 100644 html/img/icons/signal.png create mode 100644 html/img/icons/write.png create mode 100644 html/img/keys/enter.png create mode 100644 html/img/keys/return.png create mode 100644 html/img/phone.png create mode 100644 html/img/pointer.png create mode 100644 html/pdown.ttf create mode 100644 html/scripts/app.js create mode 100644 html/scripts/mustache.min.js create mode 100644 html/ui.html create mode 100644 server/main.lua diff --git a/README.md b/README.md index 2a2878fd..185d67a3 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,16 @@ # fxserver-esx_phone FXServer esx_phone + +[INSTALLATION] + +1) CD in your resources folder + +2) +``` +git clone https://github.com/FXServer-ESX/fxserver-esx_phone +``` +3) Add this in your server.cfg : + +``` +start esx_phone +``` \ No newline at end of file diff --git a/__resource.lua b/__resource.lua new file mode 100644 index 00000000..c7e09045 --- /dev/null +++ b/__resource.lua @@ -0,0 +1,35 @@ +resource_manifest_version '44febabe-d386-4d18-afbe-5e627f4af937' + +description 'ESX Phone' + +server_scripts { + '@mysql-async/lib/MySQL.lua', + 'server/main.lua' +} + +client_script 'client/main.lua' + +ui_page 'html/ui.html' + +files { + 'html/ui.html', + 'html/bankgothic.ttf', + 'html/pdown.ttf', + 'html/css/app.css', + 'html/scripts/mustache.min.js', + 'html/scripts/app.js', + 'html/img/cursor.png', + 'html/img/keys/enter.png', + 'html/img/keys/return.png', + 'html/img/phone.png', + 'html/img/icons/signal.png', + 'html/img/icons/rep.png', + 'html/img/icons/msg.png', + 'html/img/icons/add.png', + 'html/img/icons/back.png', + 'html/img/icons/new-msg.png', + 'html/img/icons/reply.png', + 'html/img/icons/write.png', + 'html/img/icons/edit.png', + 'html/img/icons/location.png' +} \ No newline at end of file diff --git a/client/main.lua b/client/main.lua new file mode 100644 index 00000000..73dc6121 --- /dev/null +++ b/client/main.lua @@ -0,0 +1,265 @@ +local Keys = { + ["ESC"] = 322, ["F1"] = 288, ["F2"] = 289, ["F3"] = 170, ["F5"] = 166, ["F6"] = 167, ["F7"] = 168, ["F8"] = 169, ["F9"] = 56, ["F10"] = 57, + ["~"] = 243, ["1"] = 157, ["2"] = 158, ["3"] = 160, ["4"] = 164, ["5"] = 165, ["6"] = 159, ["7"] = 161, ["8"] = 162, ["9"] = 163, ["-"] = 84, ["="] = 83, ["BACKSPACE"] = 177, + ["TAB"] = 37, ["Q"] = 44, ["W"] = 32, ["E"] = 38, ["R"] = 45, ["T"] = 245, ["Y"] = 246, ["U"] = 303, ["P"] = 199, ["["] = 39, ["]"] = 40, ["ENTER"] = 18, + ["CAPS"] = 137, ["A"] = 34, ["S"] = 8, ["D"] = 9, ["F"] = 23, ["G"] = 47, ["H"] = 74, ["K"] = 311, ["L"] = 182, + ["LEFTSHIFT"] = 21, ["Z"] = 20, ["X"] = 73, ["C"] = 26, ["V"] = 0, ["B"] = 29, ["N"] = 249, ["M"] = 244, [","] = 82, ["."] = 81, + ["LEFTCTRL"] = 36, ["LEFTALT"] = 19, ["SPACE"] = 22, ["RIGHTCTRL"] = 70, + ["HOME"] = 213, ["PAGEUP"] = 10, ["PAGEDOWN"] = 11, ["DELETE"] = 178, + ["LEFT"] = 174, ["RIGHT"] = 175, ["TOP"] = 27, ["DOWN"] = 173, + ["NENTER"] = 201, ["N4"] = 108, ["N5"] = 60, ["N6"] = 107, ["N+"] = 96, ["N-"] = 97, ["N7"] = 117, ["N8"] = 61, ["N9"] = 118 +} + +ESX = nil +local GUI = {} +GUI.Time = 0 +GUI.PhoneIsShowed = false +GUI.MessagesIsShowed = false +GUI.AddContactIsShowed = false +local PhoneData = {phoneNumber = 0, contacts = {}} +local RegisteredMessageCallbacks = {} +local ContactJustAdded = false +local CurrentAction = nil +local CurrentActionMsg = '' +local CurrentActionData = {} +local CurrentDispatchRequestId = -1 + +Citizen.CreateThread(function() + + while ESX == nil do + TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end) + Citizen.Wait(0) + end + + ESX.UI.Menu.RegisterType('phone', OpenPhone, ClosePhone) + +end) + +function OpenPhone() + + TriggerServerEvent('esx_phone:reload', PhoneData.phoneNumber) + + SendNUIMessage({ + showPhone = true, + phoneData = PhoneData + }) + + GUI.PhoneIsShowed = true + + ESX.SetTimeout(200, function() + SetNuiFocus(true, true) + end) + +end + +function ClosePhone() + + SendNUIMessage({ + showPhone = false + }) + + SetNuiFocus(false) + + GUI.PhoneIsShowed = false + +end + +RegisterNetEvent('esx_phone:loaded') +AddEventHandler('esx_phone:loaded', function(phoneNumber, contacts) + + PhoneData.phoneNumber = phoneNumber + PhoneData.contacts = {} + + for i=1, #contacts, 1 do + table.insert(PhoneData.contacts, contacts[i]) + end + + SendNUIMessage({ + reloadPhone = true, + phoneData = PhoneData + }) + +end) + +RegisterNetEvent('esx_phone:addContact') +AddEventHandler('esx_phone:addContact', function(name, phoneNumber, isOnline) + + table.insert(PhoneData.contacts, { + name = name, + number = phoneNumber, + online = isOnline + }) + -- CALL HERE RELOADCONTACT + SendNUIMessage({ + contactAdded = true, + phoneData = PhoneData + }) +end) + +RegisterNetEvent('esx_phone:addSpecialContact') +AddEventHandler('esx_phone:addSpecialContact', function(name, phoneNumber, base64Icon) + + SendNUIMessage({ + addSpecialContact = true, + name = name, + number = phoneNumber, + base64Icon = base64Icon + }) + +end) + +RegisterNUICallback('add_contact', function(data, cb) + + local phoneNumber = tonumber(data.phoneNumber) + local contactName = tostring(data.contactName) + + if phoneNumber then + TriggerServerEvent('esx_phone:addPlayerContact', phoneNumber, contactName) + end + +end) + + +RegisterNetEvent('esx_phone:onMessage') +AddEventHandler('esx_phone:onMessage', function(phoneNumber, message, position, anon, job, dispatchRequestId) + + ESX.ShowNotification('~b~Nouveau message') + + SendNUIMessage({ + newMessage = true, + phoneNumber = phoneNumber, + message = message, + position = position, + anonyme = anon, + job = job + }) + + if dispatchRequestId then + + CurrentAction = 'dispatch' + CurrentActionMsg = job .. ' - Appuez sur ~INPUT_CONTEXT~ pour prendre l\'appel' + CurrentDispatchRequestId = dispatchRequestId + + CurrentActionData = { + phoneNumber = phoneNumber, + message = message, + position = position, + actions = actions, + anonyme = anon, + job = job + } + + ESX.SetTimeout(15000, function() + CurrentAction = nil + end) + + end + +end) + +RegisterNetEvent('esx_phone:stopDispatch') +AddEventHandler('esx_phone:stopDispatch', function(dispatchRequestId, playerName) + + if CurrentDispatchRequestId == dispatchRequestId and CurrentAction == 'dispatch' then + CurrentAction = nil + ESX.ShowNotification(playerName .. ' a pris l\'appel') + end + +end) + +RegisterNUICallback('setGPS', function(data) + SetNewWaypoint(data.x, data.y) + ESX.ShowNotification('Position entrée dans le GPS') +end) + +RegisterNUICallback('send', function(data) + + local phoneNumber = data.number + + if tonumber(phoneNumber) ~= nil then + phoneNumber = tonumber(phoneNumber) + end + + TriggerServerEvent('esx_phone:send', phoneNumber, data.message, data.anonyme) + + SendNUIMessage({ + showMessageEditor = false + }) + + ESX.ShowNotification('Message envoyé') + +end) + +RegisterNUICallback('escape', function() + ESX.UI.Menu.Close('phone', GetCurrentResourceName(), 'main') +end) + +Citizen.CreateThread(function() + while true do + + Wait(0) + + if GUI.PhoneIsShowed then -- codes here: https://pastebin.com/guYd0ht4 + DisableControlAction(0, 1, true) -- LookLeftRight + DisableControlAction(0, 2, true) -- LookUpDown + DisableControlAction(0, 25, true) -- Input Aim + DisableControlAction(0, 106, true) -- Vehicle Mouse Control Override + + DisableControlAction(0, 24, true) -- Input Attack + DisableControlAction(0, 140, true) -- Melee Attack Alternate + DisableControlAction(0, 141, true) -- Melee Attack Alternate + DisableControlAction(0, 142, true) -- Melee Attack Alternate + DisableControlAction(0, 257, true) -- Input Attack 2 + DisableControlAction(0, 263, true) -- Input Melee Attack + DisableControlAction(0, 264, true) -- Input Melee Attack 2 + + DisableControlAction(0, 12, true) -- Weapon Wheel Up Down + DisableControlAction(0, 14, true) -- Weapon Wheel Next + DisableControlAction(0, 15, true) -- Weapon Wheel Prev + DisableControlAction(0, 16, true) -- Select Next Weapon + DisableControlAction(0, 17, true) -- Select Prev Weapon + else + + if IsControlPressed(0, Keys['F1']) and (GetGameTimer() - GUI.Time) > 150 then + + if not ESX.UI.Menu.IsOpen('phone', GetCurrentResourceName(), 'main') then + ESX.UI.Menu.CloseAll() + ESX.UI.Menu.Open('phone', GetCurrentResourceName(), 'main') + end + + GUI.Time = GetGameTimer() + + end + + end + end +end) + +-- Key controls +Citizen.CreateThread(function() + while true do + + Citizen.Wait(0) + + if CurrentAction ~= nil then + + SetTextComponentFormat('STRING') + AddTextComponentString(CurrentActionMsg) + DisplayHelpTextFromStringLabel(0, 0, 1, -1) + + if IsControlPressed(0, Keys['E']) and (GetGameTimer() - GUI.Time) > 300 then + + if CurrentAction == 'dispatch' then + TriggerServerEvent('esx_phone:stopDispatch', CurrentDispatchRequestId) + SetNewWaypoint(CurrentActionData.position.x, CurrentActionData.position.y) + end + + CurrentAction = nil + GUI.Time = GetGameTimer() + + end + + end + + end +end) \ No newline at end of file diff --git a/esx_phone.sql b/esx_phone.sql new file mode 100644 index 00000000..19d3b255 --- /dev/null +++ b/esx_phone.sql @@ -0,0 +1,11 @@ +CREATE TABLE `user_contacts` ( + `id` int(11) NOT NULL, + `identifier` varchar(255) NOT NULL, + `name` varchar(255) NOT NULL, + `number` int(11) NOT NULL +); + +ALTER TABLE `user_contacts` ADD PRIMARY KEY (`id`); + +ALTER TABLE `users` +ADD COLUMN `phone_number` INT NULL AFTER `position`; diff --git a/html/bankgothic.ttf b/html/bankgothic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..f3d8049b052a1f9cfd27557eb3675bc85ce4b9fc GIT binary patch literal 36272 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 D`#l06 literal 0 HcmV?d00001 diff --git a/html/css/app.css b/html/css/app.css new file mode 100644 index 00000000..d29c0273 --- /dev/null +++ b/html/css/app.css @@ -0,0 +1,392 @@ +html, body { + width: 100%; + height: 100%; + overflow: hidden; +} + +#cursor { + position: absolute; + z-index: 999999; + display: none; +} + +#console { + position: absolute; + left: 0; + bottom: 0; + width: 320px; + height: 250px; + background-color: rgba(0, 0, 0, 0.8); + color: white; + padding: 5px; + overflow-y: auto; + overflow-x: hidden; +} + +#phone { + background-image: url(../img/phone.png); + background-repeat: no-repeat; + font-family: 'Catamaran', sans-serif; + font-weight: 400; + font-size: 12px; + width: 298px; + height: 480px; + color: #212121; + position: absolute; + right: 300px; + bottom: 0; + overflow: hidden; + cursor: default; + display: none; +} + +#phone > .container { + position: absolute; + top: 59px; + left: 46px; + width: 207px; + height: 365px; + background-repeat: no-repeat; + overflow: hidden; +} + +#phone .head { + position: relative; + text-transform: uppercase; + text-align: center; + height: 20px; + line-height: 20px; + background-color: black; + z-index: 5; +} + +#phone-icon { + display: block; + position: absolute; + height: 12px; + line-height: 20px; + top: 4px; + right: 5px; +} + +#phone-number { + display: block; + position: absolute; + height: 20px; + line-height: 20px; + top: 0; + left: 5px; + color: white; +} + +#phone .menu { + max-height: 320px; + overflow-y: scroll; + z-index: 1; +} + +#phone .menu .home { + list-style: none outside none; + margin: 0; + padding: 13px; + color: white; +} + +#phone .menu .phone-icon { + position: relative; + display: block; + float: left; + text-align: center; + width: 60px; + padding-top: 50px; + padding-bottom: 5px; + background-size: 32px 32px; + background-color: transparent; + background-repeat: no-repeat; + background-position: center 10px; + cursor: pointer; +} + +#phone .menu .phone-icon::after { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(255, 255, 255, 0.01); +} + +#phone-icon-rep { + background-image: url("../img/icons/rep.png"); +} + +#phone-icon-msg { + background-image: url("../img/icons/msg.png"); +} + +#phone .menu::-webkit-scrollbar { + display: none; +} + +#phone .menu .phone-icon.selected, #phone .menu .phone-icon:hover { + background-color: rgba(255, 255, 255, 0.1); +} + +.screen { + position: absolute; + top: 0; + left: 207px; + width: 207px; + height: 365px; + background-color: #dde1e8; + z-index: 1; + transition: left 0.25s ease; + overflow: hidden; +} + +.screen.active { + left: 0; +} + +.screen > .container { + position: absolute; + top: 50px; + left: 0; + width: 224px; + height: 315px; + overflow-y: scroll; +} + +.head-screen { + position: absolute; + height: 30px; + line-height: 30px; + text-transform: uppercase; + background-color: white; + text-align: center; + color: #212121; + background-color: white; + font-weight: 600; + border-bottom: 1px solid rgba(0, 0, 0, 0.3); + font-size: 13px; + top: 20px; + left: 0; + right: 0; + z-index: 5; +} + +.btn-head { + display: block; + color: #212121; + width: 30px; + height: 30px; + font-size: 20px; + cursor: pointer; +} + +.btn-head:hover { + background-color: #f1f1f1; +} + +.btn-head-back { + float: left; + background: transparent url("../img/icons/back.png") no-repeat 50%; +} + +.btn-head-new { + float: right; + background: transparent url("../img/icons/add.png") no-repeat 50%; +} + +.message, .contact { + position: relative; + padding: 3px 35px 3px 25px; + background-color: white; + border-bottom: 1px solid rgba(0, 0, 0, 0.3); + color: #212121; + min-height: 30px; +} + +.message.no-item, .contact.no-item { + padding: 3px 5px; +} + +.contact { + opacity: 0.5; +} + +.contact.online, .contact.no-item { + opacity: 1; +} + +.contact::after { + position: absolute; + display: block; + content: ''; + width: 8px; + height: 8px; + background-color: #dcdcdc; + left: 10px; + top: 8px; + border-radius: 50%; +} + +.contact.online::after { + background-color: #6ecc49; +} + +.contact.no-item::after { + display: none; +} + +.no-item { + text-align: center; +} + +.message .sender-info, .message .body, .contact .sender-info, { + margin-right: 30px; +} + +.message .phone-number { + line-height: 10px; + color: #999; +} + +.message .body { + margin-top: 5px; + line-height: 16px; +} + +.sender { + font-weight: 600; +} + +.actions { + position: absolute; + top: 0; + right: 0; + bottom: 0; + width: 24px; +} + +.actions .new-msg { + display: block; + width: 24px; + height: 24px; + background: transparent url("../img/icons/write.png") no-repeat 50%; + cursor: pointer; +} + +.actions .reply-btn { + background: transparent url("../img/icons/reply.png") no-repeat 50%; +} + +.actions .gps { + display: none; + width: 24px; + height: 24px; + background: transparent url(../img/icons/location.png) no-repeat 50%; + cursor: pointer; +} + +.actions .ok-btn { + display: none; + width: 24px; + height: 24px; + background: transparent url(../img/icons/ok.png) no-repeat 50%; + cursor: pointer; +} + +.actions .new-msg.anonyme, .contact .actions .new-msg { + display: none; +} + +.contact.online .actions .new-msg, .actions .gps.active, .actions .ok-btn.showOK { + display: block; +} + +.actions .new-msg:hover, .actions .gps:hover, .actions .ok-btn:hover { + background-color: #f1f1f1; +} + +#writer textarea { + width: 207px; + min-width: 207px; + max-width: 207px; + height: 255px; + min-height: 255px; + max-height: 255px; + background-color: #f1f1f1; + border: none; + box-shadow: 0 2px 3px 1px rgba(0, 0, 0, 0.1) inset; + padding: 5px; + outline: none; +} + +#writer label { + display: block; + width: 100%; + height: 30px; + line-height: 34px; + background-color: #f1f1f1; + border: none; + box-shadow: 0 2px 3px 1px rgba(0, 0, 0, 0.1) inset; + font-size: 14px; + font-weight: 600; + color: #6b6b6b; +} + +#writer label input { + top: 2px; + position: relative; + left: 5px; + margin-right: 10px; +} + +#writer button, #contact button { + display: block; + float: left; + width: 103px; + height: 30px; + line-height: 30px; + background-color: #dcdcdc; + border-color: rgba(0, 0, 0, 0.5); + border: none; + color: white; + font-weight: 600; + cursor: pointer; + outline: none; +} + +#writer button.marged, #contact button.marged { + margin-left: 1px; +} + +#writer #writer_cancel, #contact #contact_cancel { + background-color: #d9534f; +} + +#writer #writer_send:hover, #contact #contact_send:hover { + background-color: #74c374; +} + +#writer #writer_cancel:hover, #contact #contact_cancel:hover { + background-color: #dc6b68; +} + +#writer #writer_send, #contact #contact_send { + background-color: #5cb85c; +} + +#contact input { + display: block; + width: 100%; + height: 30px; + line-height: 30px; + padding: 2px 5px; + background-color: #f1f1f1; + border: none; + border-bottom: 1px solid rgba(0, 0, 0, 0.3); + outline: none; + box-shadow: 0 2px 3px 1px rgba(0, 0, 0, 0.1) inset; +} \ No newline at end of file diff --git a/html/img/cursor.png b/html/img/cursor.png new file mode 100644 index 0000000000000000000000000000000000000000..39536f953d701b36a90dc56084bafd988371a2fb GIT binary patch literal 272 zcmeAS@N?(olHy`uVBq!ia0vp^q9Dw{1|(OCFP#RYSkfJR9T^xl_H+M9WCij$3p^r= z85sBufiR<}hF1enP_o1|q9iy!t)x7$D3zfgF*C13FE6!3!9>qM&(L5k7uNxxqFzrI z#}Etut!FreS`>I(0^ikVp5f1PInc0ibrsXWO3z8Ee_BE>W<&%Ean9H1Y|@_Ma)5tI zP3vrppG@U}o4EJ`H!a-_TKCSz%v`zHX&3lrYRgJzbSuAt9 z@6PvoS_%Jm+&BGp_A+yZ?Fo&SwU@JtJ?kA`YTb#E`^&mC!8qZZWp1Mjt5em&H79@$ OWAJqKb6Mw<&;$S!<6)ox literal 0 HcmV?d00001 diff --git a/html/img/icons/add.png b/html/img/icons/add.png new file mode 100644 index 0000000000000000000000000000000000000000..ad5a13101a1e5748e06d1f79eb5278e9d95c765f GIT binary patch literal 995 zcmaJ=O;6N77%ssOWx=R12JqmdgougTkKJuy8wx_Zg(eosvI%=KcH4oi&`zBWZkLP7 z%_Dd*@nE8!{R0{$5fe>3>5YSlzkmZbL$Xen-Nl1z({?`cJkR^i`#$q@X>oqAf25yb zm_c(vFVgWs@}2FY|7%ALn-0UIP$tW0mDFqunOPTAATWJ<4Hlv8)*pO@Qw-C~dZjWc zTelSl`JA0#xY!RVn_;GA;?Q>1ApsS*<^?Ky`0gVMJXd8`BulVD4OYE{Eew~p7E8|7 zx|4U=nQ1T;D^$RT#0Igy5kyL?vQ1ru&XaAP1x*N9SJ{)K%GMIl5QaeFvS~*UMIg(Z zn3H5#ya_UbnBfKb%4spDNV1Zd0Ii3m(XhL!6m_E&i>_3*N=T^ie63dFYFQ59HD1i; z^NB_#lctDtR1b(9r-Nv;t)Rom!Cpu_6o5q0uAoh#vNY3^BluxQHi%kfq6On)JLE-9 zNRHG7TGs!ezTZJdqzF&*{ikqLs)vv-!U%0*hc<3?Gzk?d8iqDOxP;I~yNXLyL{L;k zA<&j(FuvjiE~-U0nt01n%pf9m;6PJXS*pN!o~vk@AZv!9N!eUp6bloRhDBE4ux$Y@f@5)WA;Dy|l&B(W@kCL<|}unwS_58VSLRQ4Ef^MIQ&gz6U@7V##2^d%bTiC+xp$udx)x@70!cn;MPa|* zF=oT=vCgzQHmHgk)7k<;1&Ih45G$Zy44IBp2s6vN5}CW(90QgicreVYB$Y{afhe*e z2(V3Fl@|mM46=eh5DW_YfsYq_98cb$SMW=LpyX=?s}Dn>*;-CY$??@#WEE!mF}5U* z%jffKzKKP4A18>S=xX?UUV`vCqb61gUenoCQjnpe+PZ~xWCB-F$)XV)W=N(hM=-24 zS<_i96Db&1P%KVhdG|;qU^4lCs9~(39h`!H^8Kfs~=(H^dk#IyMthFk8Wxx?=&vslSwIII#@APn2^H^QDAjllVnntW-%TQ zH2FnAh&8uI+9NSp6!`Wi&j(`%N?aMKBL*~aiK~^k5uPv0b**4nB(e-`{TS5ZHZs8S zktMx+E^=ADRjyVp7cm|y%jHNioZHyH8eQ5V?s2DUwk3--<3p3U-6po4I=-!nT#3gC zIg&2C&Hh+gYNhM17mL$X#oW5#>gw9}wCD29trxC-q=&bpf7WTEyBYrrdOibwt)Kqf z_N;C*q%JNVXn4M7?zQmYW$4mm?$$=2pWjogc|_e<6sHGf7Crkw=ZU*b75Yr>K_hra z!#sVoc4nJ za0`PlBg3pY54nJ za0`PlBg3pY5bl#(9bc}m zbiMn>T0vd4hW-fgq_vZxcAY7=aQdyB^=FMQ&+N42_V0sp{;(|g_dmDcVg93Idd81F@9#?F pyx#dktLXoxeZO+=9J9-wANnh{{*XonJ1`g+JYD@<);T3K0RY4!kemPj literal 0 HcmV?d00001 diff --git a/html/img/icons/msg.png b/html/img/icons/msg.png new file mode 100644 index 0000000000000000000000000000000000000000..aec485a33fdf4c58bbd15c7a6a56c7ca5d9ca9f2 GIT binary patch literal 1205 zcmV;m1WNmfP)`cT+qKTTAST_m8v(5aP)yt+F|l?Ck->~Fdv3IAbKHwxsbtVPFy5+Yk5~?)1iZ;LDbPNvPVdP6 z=WeV@z{_XjWIb@cWbn}Y19Q!vSA*} zz)_9-I2IPm;o@Xw^46-z0p=!Ro^6fgNbA5ztpm;P)*V*FcDk>)o@bTFdDlIj(2zP7t+awy#Q&W44>)CgfMZ@xTKt6nk>r_4sHViqaLyLE`5;HmEPpf9GOHF@aCvLDzlyj^18gz!77qP3#omh( z{4t&8)`J{f7boeyG|6-(&-bwe2QE$W+0gyvoe1OZ!!~MHaV*sGBF}%YgIxR`e}8uz zCs<4Uo_7c|HUr=U*8^azC21@cNaQSGpC$Y7J!gePw(#Y}-*T|l4!MX70`D#mrVi>0 z5O}7AhP_8|LgA9h!a@2}=kE9;ME^W6BiU}K?or2F~L0p=5TQ2obKCbI{T?JGGl7KUds z49{fvb2`nbolg>-%@CcjFg&k25Z9m;^!I~?D-pv~Vj^I}zo;4M1jbXy) zlaAqio5sru0BGxv?L_3)YD9Q}F-$nFdE@&2$WZC{k}!`RXugVQN8!)J8j)Lud_L(| zOkP$1K(xDQG(P9N0``>G+fkMnvKD$%VY|C@+e=Zg5h%n(uE$u8sOmaT#Kt5=<+SGY zF=|ZGaMB1@Lj1Y!GRYMrJ)(#;$DCxlY=%I#$7r*}jvs090 T#=h3V00000NkvXXu0mjf&_6@S literal 0 HcmV?d00001 diff --git a/html/img/icons/new-msg.png b/html/img/icons/new-msg.png new file mode 100644 index 0000000000000000000000000000000000000000..a597048117ec2c2d4b0534f8502f8ed70e2db8b8 GIT binary patch literal 1196 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE;=WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2v2cW~I!Kh>{3jAFJg2T)jk)8oi3#0-$aN1{?c|g2d$P)DnfH z)bz|eTc!8A_bVx6rr0WloBA5~7C5J7WO`H;r3P2|g(O#HCtIc{+1n}DR9FEG$W1Lt zRH(?!$t$+1uvG$^YXxM3g!Ppaz)DK8ZIvL7itr6kaLzAERWQ{v)=f4rG*mD%(=#+N zH8V5RQ7|$vG}1Q!A~Rh>6Dw0QDv55FG|-pw6wGYnPFt43sj+7T$xvrSfQI&tPC^3CAB!YD6^m>Ge1uOWMX1cerbuV z640d(FXR@$jm;~D1`{yA^eYkz^bPe4Kwg3=^!3HBG&dKny0|1L72#g21{a4^7NqJ2 zr55Lx79|5CE=?I^Re_arQEFmIeo;t%ehw@Y12XbU@{2R_3lyA#%@j1kGxJjN%ZoL^ z>V18!JoAc667!N%JzZ>-fF|f=W~NxVnmD^z8W>ud8k<-e8oHW0IvKcG7#q4;S{j)f znwlEG^t$9Hm*%GCmB93-AoM!o)C)=qxdlL*T~doO%TiO^it=+6z+Se>#O)SyoaRCG zrr>sq1x~&CK*#8VA{Hs4VM4&v17gCHEsz6G_^Elo)LsNk)*q@2ZZj}2DtNj$hE&{2 z`t$$4J+mpJF!NN##fugx_2}{(dFF7&ZIR^uh95`vR)4>zC@sNxq_|;5w@-U5>m!u~ z3s*a^c`;j-H_n*rbia;0NmWcCu|4I6pTa%oMUL+r-Y^}RDi{e4mPbW%r_UX zH8H4j&q@&&naq2EcMs2z=N=}9MLM9og!fCaqz7f6vxcsG#z!GcZGTy$zQ~0yX;2As z=H0_~Brrh!0gJ<74zUfZ7kVD=pO6^A9g!8z$iT?PAkgM$x?+<3H&FTE>FVdQ&MBb@ E0A6~DB>(^b literal 0 HcmV?d00001 diff --git a/html/img/icons/ok.png b/html/img/icons/ok.png new file mode 100644 index 0000000000000000000000000000000000000000..c08a8a7ab4c85b2b6ebaca17d1b13dbfd507e158 GIT binary patch literal 241 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ za0`PlBg3pY5Zd2de zoA^$xzjyk~`^rf4_ur}ED}SSzl-Xtr#=*n z;ZeSGQ1MW=fU}q=8>8L6!+Z9n3X2}Ma8HjEUp```%OTL%$yL?~%=bYd99UdZu3W}v!8SgC8CKyyn zv06pEGDRR0$^pQ~uoN{!^*fCAj=}rpx}MIii*SI7q%AL8f?N}mk^6rBq14qG<)n}= zj-;vI>vQ+scwBX_`qojW#o04g1Jc6XYmQ`<h#i&->r^Lh|Qdf@KiXAw$5uO&Tpd3)RV9V)Ts+~>kgM+5i4ctmmxj@55?I3 z{QPXG-DGbMX+W+~r*HgXccWM-eW~KlOwjh4Pfr(7X8Ytx5YNSBZyqYI7AxgyfW)hy zSfPkrdvMEd03;KllMh$H2OEwi7G#$F=P5XOAi&KKkAz~SBAyfph1QNIS0@d~H8B}) z?B99@Jdy}jq!i1sSHB5wB;~;2p<$<~d6|e^nyBx;eZPvO7zYUe`8zkBP^Bd5f+fp^ zGF0Z5;cR;y>iXKTWqAo)UKX}_nBWm4K??aYbIYR(1fro_vO2Gv1BqZPf8Btp&U#o~ z6ubcr-?h}jkdlMUxa6Sukus?`ZyNwPk({l2o8*hc97kd*0Y0LH5|~CElh?d{4&UCn zf{ht^kUZhEnn=kPNu)_dIUD$Knad0uLJc||03N2sWda#^AQ0fD$7X}L>X(Q2uuE40 z0Vx6p5)~^};d1u_Se>qUJp&v_M8(IY5TsZl=QskPXrU-veB6n>dIJQ60B72ous$st z3!A)HJ;+IW_1M3~Iu~z{z@1q0K>qX#j15R&34I;-|ZJVRJDEj03Hf zK~!j0z{&bxbA>iB*w%50BqS19Lcz+-vl*qsGy{tDV!Kw0n5!CkdW8ca`2#DzuF;1oc?emAM_j`QI zKvUx-0&q}IZ#7oUt-rty$@i3Izaw5yp+P?OmX~zM%PmfZuohz=z8yyS{qTXmltHlm)fD4)5UGwX4^POAgffsi|sp04k&B}!2 ztht;*q!=^xbzJ|XvJij=|67^>)B4qs_gKnle^oo7XLM}1@#Nu66XL*gJ0SqD4CSyr zXuG;FuVgbRlFH_@c1@*cY;?Hk_`!9w#nSb>=CtoHJLVX&-D#*+W#+CCDP!VahWMzd zucP7E{?e)9K@Citgt6}TpGl!q7@eG@2N8T(4af07!1tu4>UirnpX~_Hv^|(s*q>8H z?Bb=#WjhaPib}Wfg%WAFd;su!-HfH7_MEZi+%d{N&^uFZSOd=xBiHD2H>j888stmT zwbHo7DSWX^2>^Z%J;vC~eNIz%$3I;yb%RaUE&*^w()C{`jrL0MY6c(x00004nJ za0`PlBg3pY5epC11AL)VX*O4C#Cln$z8afDoO;h*gD zS3&9EFIR^%4+8hw+!ZnHDRg`8>mX@-$ByTxV7X0IT7E*NpCi!444$rjF6*2UngIWO BTLS<9 literal 0 HcmV?d00001 diff --git a/html/img/icons/signal.png b/html/img/icons/signal.png new file mode 100644 index 0000000000000000000000000000000000000000..143301f5e81cd55eed147f7cb7f12e6f2889f639 GIT binary patch literal 1102 zcmaJ=O=#0l9M7zqQ!Jb4gn7_McA03Kq#r4btLsNPt=MgL#Z?$+n!K%HOBPw#9zvtIESZpsDJTy?N|VN521?50#B10?Q7&4| z=5Q{3SdfwCl^l%M)(pa?sGeTikmPZQK^~5&dW8P=@Cgl6B|;Ae(`?#Ez=C>k%7jBx z{aJZxTn;OA?|#r@3q(MJSOT_I)Gfh|&`Y`k**n(^4VEByJVGx!l}itT1TrBAdi@@m z*id2TQ8v7C=#$>2R)KnU`JF9cRTnpiWHQ6VGtt=J-`2wlL~5E!OhE_=&< zFEYm%E*uU!8a|(gAUxKjjwRcpTU~Vp5n8gT8dya-a1^CHD&YuCJY5b!Ggf7FYb8x2 zVT>&q4CiH?km|s6`u|W(TSZ$q1K0fhr?8csG$50K7Al!CDcoq6W6BT`CX_HTvj`RI zSsW}NjI07OKw^jo-NUM`pt7}l37<|2Dc!=7F2j@ABb~4j!TB( zVUZ<6bnn5g!&{R)CrO!kfSPEHC8~YMysJFsZ z8v7D#$YqE#j8oXZ3SGY<-Q#Rm+m;+w8z1VV+fCBeR{z2c@+6+8#8}q;kYD`$yVtel z-PYIAKN$7jzoX8 zz0S73X@!3J{;`?M&2vAl7585UpU-}OR-BpBqPJUWvsW)3iau((72A>;*;y@D0wdJC qCwi*08m`!TvYTH%ZmnHrCn}VSl2LqCKmJ~FUU(|mFW!ltIP(X2lVgYg literal 0 HcmV?d00001 diff --git a/html/img/icons/write.png b/html/img/icons/write.png new file mode 100644 index 0000000000000000000000000000000000000000..28f32bebda0859685419555189e228df512f2c5a GIT binary patch literal 250 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 literal 0 HcmV?d00001 diff --git a/html/pdown.ttf b/html/pdown.ttf new file mode 100644 index 0000000000000000000000000000000000000000..69c76cf3f2a6bbc93f570124554fec06d7bbac25 GIT binary patch literal 151512 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 literal 0 HcmV?d00001 diff --git a/html/scripts/app.js b/html/scripts/app.js new file mode 100644 index 00000000..160d91d1 --- /dev/null +++ b/html/scripts/app.js @@ -0,0 +1,434 @@ +(function(){ + + let ContactTpl = + '
' + + '
' + + '
' + + '{{sender}}
#{{phoneNumber}}' + + '
' + + '
' + + '
' + + '
' + ; + + let MessageTpl = + '
' + + '
' + + '
' + + '{{sender}}
#{{phoneNumber}}' + + '
' + + '
' + + '
{{message}}
' + + '
' + + '
' + ; + + let SpecialContactTpl = '
  • {{name}}
  • '; + + let contacts = []; + let specialContacts = []; + let menu = []; + let currentItem = 0; + let currentVal = null; + let isMessageEditorOpen = false; + let isMessagesOpen = false; + let isPhoneShowed = false; + + let showMain = function() { + $('.screen').removeClass('active'); + } + + let showRepertoire = function() { + $('#repertoire').addClass('active'); + } + + let hideRepertoire = function() { + $('#repertoire').removeClass('active'); + } + + let showMessages = function(){ + $('#messages').addClass('active'); + isMessagesOpen = true; + } + + let hideMessages = function(){ + $('#messages').removeClass('active'); + isMessagesOpen = false; + } + + let showAddContact = function() { + $('#contact').addClass('active'); + } + + let hideAddContact = function() { + $('#contact').removeClass('active'); + $('#contact_name').val(''); + $('#contact_number').val(''); + } + + let showNewMessage = function(cnum, cname) { + $('#writer').addClass('active'); + $('#writer_number').val(cnum); + $('#writer .header-title').html(cname); + } + + let hideNewMessage = function() { + $('#writer').removeClass('active'); + $('#writer_number').val(''); + $('#writer_message').val(''); + $('#writer .header-title').html(''); + } + + let showGPS = function(xPos, yPos) { + $.post('http://esx_phone/setGPS', JSON.stringify({ + x: parseFloat(xPos), + y: parseFloat(yPos) + } + )); + } + + let renderContacts = function(){ + + let contactHTML = ''; + + if(contacts.length > 0) { + + for(let i=0; i 0) { + + for(let i=0; i { + return a.name.localeCompare(b.name); + }); + + renderSpecialContacts(); + + } + + let renderSpecialContacts = function(){ + + $('.phone-icon').unbind('click'); + + $('#phone .menu .home').html( + '
  • Repertoire
  • ' + + '
  • Messages
  • ' + ); + + for(let i=0; i":">",'"':""","'":"'","/":"/","`":"`","=":"="};function escapeHtml(string){return String(string).replace(/[&<>"'`=\/]/g,function fromEntityMap(s){return entityMap[s]})}var whiteRe=/\s*/;var spaceRe=/\s+/;var equalsRe=/\s*=/;var curlyRe=/\s*\}/;var tagRe=/#|\^|\/|>|\{|&|=|!/;function parseTemplate(template,tags){if(!template)return[];var sections=[];var tokens=[];var spaces=[];var hasTag=false;var nonSpace=false;function stripSpace(){if(hasTag&&!nonSpace){while(spaces.length)delete tokens[spaces.pop()]}else{spaces=[]}hasTag=false;nonSpace=false}var openingTagRe,closingTagRe,closingCurlyRe;function compileTags(tagsToCompile){if(typeof tagsToCompile==="string")tagsToCompile=tagsToCompile.split(spaceRe,2);if(!isArray(tagsToCompile)||tagsToCompile.length!==2)throw new Error("Invalid tags: "+tagsToCompile);openingTagRe=new RegExp(escapeRegExp(tagsToCompile[0])+"\\s*");closingTagRe=new RegExp("\\s*"+escapeRegExp(tagsToCompile[1]));closingCurlyRe=new RegExp("\\s*"+escapeRegExp("}"+tagsToCompile[1]))}compileTags(tags||mustache.tags);var scanner=new Scanner(template);var start,type,value,chr,token,openSection;while(!scanner.eos()){start=scanner.pos;value=scanner.scanUntil(openingTagRe);if(value){for(var i=0,valueLength=value.length;i0?sections[sections.length-1][4]:nestedTokens;break;default:collector.push(token)}}return nestedTokens}function Scanner(string){this.string=string;this.tail=string;this.pos=0}Scanner.prototype.eos=function eos(){return this.tail===""};Scanner.prototype.scan=function scan(re){var match=this.tail.match(re);if(!match||match.index!==0)return"";var string=match[0];this.tail=this.tail.substring(string.length);this.pos+=string.length;return string};Scanner.prototype.scanUntil=function scanUntil(re){var index=this.tail.search(re),match;switch(index){case-1:match=this.tail;this.tail="";break;case 0:match="";break;default:match=this.tail.substring(0,index);this.tail=this.tail.substring(index)}this.pos+=match.length;return match};function Context(view,parentContext){this.view=view;this.cache={".":this.view};this.parent=parentContext}Context.prototype.push=function push(view){return new Context(view,this)};Context.prototype.lookup=function lookup(name){var cache=this.cache;var value;if(cache.hasOwnProperty(name)){value=cache[name]}else{var context=this,names,index,lookupHit=false;while(context){if(name.indexOf(".")>0){value=context.view;names=name.split(".");index=0;while(value!=null&&index")value=this.renderPartial(token,context,partials,originalTemplate);else if(symbol==="&")value=this.unescapedValue(token,context);else if(symbol==="name")value=this.escapedValue(token,context);else if(symbol==="text")value=this.rawValue(token);if(value!==undefined)buffer+=value}return buffer};Writer.prototype.renderSection=function renderSection(token,context,partials,originalTemplate){var self=this;var buffer="";var value=context.lookup(token[1]);function subRender(template){return self.render(template,context,partials)}if(!value)return;if(isArray(value)){for(var j=0,valueLength=value.length;j + + + + + + + +
    + +
    + +
    + + #123456 +
    + + + +
    + +
    Répertoire
    +
    +
    +
    +

    Aucun contact

    +
    +
    +
    +
    + +
    +
    Messages
    +
    +
    +
    +

    Aucun message

    +
    +
    +
    +
    + +
    +
    +
    +
    + + + + + +
    +
    +
    + +
    +
    Ajouter un contact
    +
    +
    + + + + +
    +
    +
    + +
    +
    + + + + + + + + \ No newline at end of file diff --git a/server/main.lua b/server/main.lua new file mode 100644 index 00000000..14d1afdb --- /dev/null +++ b/server/main.lua @@ -0,0 +1,309 @@ +ESX = nil + +TriggerEvent('esx:getSharedObject', function(obj) + ESX = obj +end) + +local RegisteredCallbacks = {} +local DisptachRequestId = 0 + +function GenerateUniquePhoneNumber() + + local foundNumber = false + local phoneNumber = nil + + while not foundNumber do + + phoneNumber = math.random(10000, 99999) + + local result = MySQL.Sync.fetchAll( + 'SELECT COUNT(*) as count FROM users WHERE phone_number = @phoneNumber', + { + ['@phoneNumber'] = phoneNumber + } + ) + + local count = tonumber(result[1].count) + + if count == 0 then + foundNumber = true + end + + end + + return phoneNumber +end + +function GetDistpatchRequestId() + + local requestId = DisptachRequestId + + if DisptachRequestId < 65535 then + DisptachRequestId = DisptachRequestId + 1 + else + DisptachRequestId = 0 + end + + return requestId + +end + +AddEventHandler('esx_phone:getDistpatchRequestId', function(cb) + cb(GetDistpatchRequestId()) +end) + +AddEventHandler('onResourceStart', function(ressource) + if ressource == 'esx_phone' then + TriggerEvent('esx_phone:ready') + end +end) + +AddEventHandler('esx:playerLoaded', function(source) + + local xPlayer = ESX.GetPlayerFromId(source) + + MySQL.Async.fetchAll( + 'SELECT * FROM users WHERE identifier = @identifier', + { + ['@identifier'] = xPlayer.identifier + }, + function(result) + + local phoneNumber = result[1].phone_number + + if phoneNumber == nil then + + phoneNumber = GenerateUniquePhoneNumber() + + MySQL.Async.execute( + 'UPDATE users SET phone_number = @phone_number WHERE identifier = @identifier', + { + ['@identifier'] = xPlayer.identifier, + ['@phone_number'] = phoneNumber + } + ) + end + + xPlayer.set('phoneNumber', phoneNumber) + + local contacts = {} + + MySQL.Async.fetchAll( + 'SELECT * FROM user_contacts WHERE identifier = @identifier ORDER BY name ASC', + { + ['@identifier'] = xPlayer.identifier + }, + function(result2) + + for i=1, #result2, 1 do + + table.insert(contacts, { + name = result2[i].name, + number = result2[i].number, + online = false + }) + + local xPlayers = ESX.GetPlayers() + + for k, v in pairs(xPlayers) do + if v.get('phoneNumber') == contacts[i].number then + contacts[i].online = true + end + end + + end + + xPlayer.set('contacts', contacts) + + TriggerClientEvent('esx_phone:loaded', source, phoneNumber, contacts) + + end + ) + + end + ) + +end) + + +RegisterServerEvent('esx_phone:reload') +AddEventHandler('esx_phone:reload', function(phoneNumber) + + local xPlayer = ESX.GetPlayerFromId(source) + local contacts = {} + + MySQL.Async.fetchAll( + 'SELECT * FROM user_contacts WHERE identifier = @identifier ORDER BY name ASC', + { + ['@identifier'] = xPlayer.identifier + }, + function(result2) + + for i=1, #result2, 1 do + + table.insert(contacts, { + name = result2[i].name, + number = result2[i].number, + online = false + }) + + local xPlayers = ESX.GetPlayers() + + for k, v in pairs(xPlayers) do + if v.get('phoneNumber') == contacts[i].number then + contacts[i].online = true + end + end + + end + + xPlayer['contacts'] = contacts + + TriggerClientEvent('esx_phone:loaded', source, phoneNumber, contacts) + + end + ) + +end) + +RegisterServerEvent('esx_phone:registerCallback') +AddEventHandler('esx_phone:registerCallback', function(cb) + table.insert(RegisteredCallbacks, cb) +end) + +RegisterServerEvent('esx_phone:send') +AddEventHandler('esx_phone:send', function(phoneNumber, message, anon) + for i=1, #RegisteredCallbacks, 1 do + RegisteredCallbacks[i](source, phoneNumber, message, anon) + end +end) + +RegisterServerEvent('esx_phone:addPlayerContact') +AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) + + local xPlayers = ESX.GetPlayers() + local foundNumber = false + local foundPlayer = nil + + MySQL.Async.fetchAll( + 'SELECT phone_number FROM users WHERE phone_number = @number', + { + ['@number'] = phoneNumber + }, + function(result) + + if result[1] ~= nil then + foundNumber = true + end + + if foundNumber then + + local xPlayer = ESX.GetPlayerFromId(source) + + if phoneNumber == xPlayer.get('phoneNumber') then + TriggerClientEvent('esx:showNotification', _source, 'Vous ne pouvez pas vous ajouter vous-même') + else + + local hasAlreadyAdded = false + local contacts = xPlayer.get('contacts') + + for i=1, #contacts, 1 do + if contacts[i].number == phoneNumber then + hasAlreadyAdded = true + end + end + + if hasAlreadyAdded then + TriggerClientEvent('esx:showNotification', _source, 'Ce numéro est déja dans votre liste de contacts') + else + + table.insert(contacts, { + name = contactName, + number = phoneNumber, + }) + + xPlayer.set('contacts', contacts) + + MySQL.Async.execute( + 'INSERT INTO user_contacts (identifier, name, number) VALUES (@identifier, @name, @number)', + { + ['@identifier'] = xPlayer.identifier, + ['@name'] = contactName, + ['@number'] = phoneNumber + }, + function(rowsChanged) + + TriggerClientEvent('esx:showNotification', _source, 'Contact ajouté') + + local xPlayers = ESX.GetPlayers() + local isOnline = false + + for k,v in pairs(xPlayers) do + if v.get('phoneNumber') == phoneNumber then + isOnline = true + break + end + end + + TriggerClientEvent('esx_phone:addContact', source, contactName, phoneNumber, isOnline) + end + ) + + end + end + + else + TriggerClientEvent('esx:showNotification', source, 'Ce numéro n\'est pas attribué...') + end + + end + ) + +end) + +AddEventHandler('esx_phone:ready', function() + TriggerEvent('esx_phone:registerCallback', function(source, phoneNumber, message, anon) + + local xPlayer = ESX.GetPlayerFromId(source) + local xPlayers = ESX.GetPlayers() + local job = 'player' + + print('MESSAGE => ' .. xPlayer.name .. '@' .. phoneNumber .. ' : ' .. message) + + for k, v in pairs(xPlayers) do + + if phoneNumber == "police" then + + if v.job.name == 'cop' then + job = "ALERTE POLICE" + TriggerClientEvent('esx_phone:onMessage', v.source, xPlayer.get('phoneNumber'), message, xPlayer.get('coords'), anon, job, GetDistpatchRequestId()) + end + + elseif phoneNumber == "ambulance" then + + if v.job.name == 'ambulance' then + job = "ALERTE AMBULANCE" + TriggerClientEvent('esx_phone:onMessage', v.source, xPlayer.get('phoneNumber'), message, xPlayer.get('coords'), anon, job, GetDistpatchRequestId()) + + end + + elseif phoneNumber == "depanneur" then + + if v.job.name == 'depanneur' then + job = "APPEL DÉPANNEUR" + TriggerClientEvent('esx_phone:onMessage', v.source, xPlayer.get('phoneNumber'), message, xPlayer.get('coords'), anon, job, GetDistpatchRequestId()) + end + + elseif v.get('phoneNumber') == phoneNumber then + TriggerClientEvent('esx_phone:onMessage', v.source, xPlayer.get('phoneNumber'), message, xPlayer.get('coords'), anon, job, false) + end + + end + + end) +end) + +RegisterServerEvent('esx_phone:stopDispatch') +AddEventHandler('esx_phone:stopDispatch', function(dispatchRequestId) + TriggerClientEvent('esx_phone:stopDispatch', -1, dispatchRequestId, GetPlayerName(source)) +end) \ No newline at end of file From edfbb54d03ba56502ecacf02c7b8463c61eca0b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Tue, 1 Aug 2017 11:08:51 +0200 Subject: [PATCH 03/76] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 185d67a3..3521c712 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,12 @@ FXServer esx_phone [INSTALLATION] 1) CD in your resources folder - -2) +2) Clone the repository ``` git clone https://github.com/FXServer-ESX/fxserver-esx_phone ``` -3) Add this in your server.cfg : +3) Import esx_phone.sql in your database +4) Add this in your server.cfg : ``` start esx_phone From 6df8b18357144ed74c907cc1519c5f1f7d7d695b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Tue, 1 Aug 2017 11:10:03 +0200 Subject: [PATCH 04/76] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 3521c712..1a435268 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ # fxserver-esx_phone FXServer esx_phone +[INFO] + +Original script by FiveDev => https://github.com/FiveDev-FR/esx_phone-by-FiveDev + [INSTALLATION] 1) CD in your resources folder From 463d5899e1a595d3c7100203a1dddddb809d0693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Tue, 1 Aug 2017 11:19:52 +0200 Subject: [PATCH 05/76] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1a435268..244d4fd1 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,11 @@ Original script by FiveDev => https://github.com/FiveDev-FR/esx_phone-by-FiveDev 1) CD in your resources folder 2) Clone the repository ``` -git clone https://github.com/FXServer-ESX/fxserver-esx_phone +git clone https://github.com/FXServer-ESX/fxserver-esx_phone esx_phone ``` 3) Import esx_phone.sql in your database 4) Add this in your server.cfg : ``` start esx_phone -``` \ No newline at end of file +``` From d99586363228edea2384501886231d7eb50c5a66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Tue, 1 Aug 2017 14:07:44 +0200 Subject: [PATCH 06/76] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 244d4fd1..cc324858 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Original script by FiveDev => https://github.com/FiveDev-FR/esx_phone-by-FiveDev [INSTALLATION] -1) CD in your resources folder +1) CD in your resources/[ESX] folder 2) Clone the repository ``` git clone https://github.com/FXServer-ESX/fxserver-esx_phone esx_phone From d216b4d05cf178816881c76439f7de99fd5af929 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Tue, 1 Aug 2017 14:09:48 +0200 Subject: [PATCH 07/76] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cc324858..9a42a9c6 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Original script by FiveDev => https://github.com/FiveDev-FR/esx_phone-by-FiveDev [INSTALLATION] -1) CD in your resources/[ESX] folder +1) CD in your resources/[esx] folder 2) Clone the repository ``` git clone https://github.com/FXServer-ESX/fxserver-esx_phone esx_phone From cc68bf49e5f6b00d806d9155fbe21bafbe9c6b34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Tue, 1 Aug 2017 16:35:27 +0200 Subject: [PATCH 08/76] Update SQL --- esx_phone.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esx_phone.sql b/esx_phone.sql index 19d3b255..8896abdf 100644 --- a/esx_phone.sql +++ b/esx_phone.sql @@ -1,5 +1,5 @@ CREATE TABLE `user_contacts` ( - `id` int(11) NOT NULL, + `id` int(11) NOT NULL AUTO_INCREMENT, `identifier` varchar(255) NOT NULL, `name` varchar(255) NOT NULL, `number` int(11) NOT NULL From 5404250e9ecc0f54fd9e9f3d88bc3fa5a28b7479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Tue, 1 Aug 2017 16:58:40 +0200 Subject: [PATCH 09/76] Update SQL --- esx_phone.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esx_phone.sql b/esx_phone.sql index 8896abdf..0517238e 100644 --- a/esx_phone.sql +++ b/esx_phone.sql @@ -8,4 +8,4 @@ CREATE TABLE `user_contacts` ( ALTER TABLE `user_contacts` ADD PRIMARY KEY (`id`); ALTER TABLE `users` -ADD COLUMN `phone_number` INT NULL AFTER `position`; +ADD COLUMN `phone_number` INT NULL; From b1029ad75f82516b39ea8a5be1372ced3347f7c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Tue, 1 Aug 2017 17:03:36 +0200 Subject: [PATCH 10/76] xPlayer.set('contacts', contacts) --- server/main.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/main.lua b/server/main.lua index 14d1afdb..5681607a 100644 --- a/server/main.lua +++ b/server/main.lua @@ -157,7 +157,7 @@ AddEventHandler('esx_phone:reload', function(phoneNumber) end - xPlayer['contacts'] = contacts + xPlayer.set('contacts', contacts) TriggerClientEvent('esx_phone:loaded', source, phoneNumber, contacts) From fc1ef9dae2e25a3396eb4d42eba4ebffeae02650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Tue, 1 Aug 2017 17:31:17 +0200 Subject: [PATCH 11/76] fix contact click bug --- html/scripts/app.js | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/html/scripts/app.js b/html/scripts/app.js index 160d91d1..4934837c 100644 --- a/html/scripts/app.js +++ b/html/scripts/app.js @@ -361,6 +361,35 @@ showAddContact(); }); + $('.phone-icon').click(function(event) { + + let id = $(this).attr('id'); + + switch(id) { + + case 'phone-icon-rep' : { + showRepertoire(); + break; + } + + case 'phone-icon-msg' : { + showMessages(); + break; + } + + default : { + + let number = $(this).data('number'); + let name = $(this).data('name'); + + showNewMessage(number, name); + + break; + } + } + + }); + window.onData = function(data){ if(data.scroll === true){ @@ -395,7 +424,7 @@ if(data.addSpecialContact === true){ addSpecialContact(data.name, data.number, data.base64Icon); - renderSpecialContacts() + renderSpecialContacts(); } if(data.move && isPhoneShowed){ From 27146a2497645ff6adbc750bf8dc7483baa781a1 Mon Sep 17 00:00:00 2001 From: Aleweadan Date: Wed, 9 Aug 2017 01:42:56 +0200 Subject: [PATCH 12/76] Fix sql --- esx_phone.sql | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/esx_phone.sql b/esx_phone.sql index 0517238e..62842ccc 100644 --- a/esx_phone.sql +++ b/esx_phone.sql @@ -1,11 +1,13 @@ -CREATE TABLE `user_contacts` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `identifier` varchar(255) NOT NULL, - `name` varchar(255) NOT NULL, - `number` int(11) NOT NULL +CREATE TABLE user_contacts ( + id int(11) NOT NULL AUTO_INCREMENT, + identifier varchar(255) NOT NULL, + name varchar(255) NOT NULL, + number int(11) NOT NULL, + PRIMARY KEY (id) ); -ALTER TABLE `user_contacts` ADD PRIMARY KEY (`id`); - +ALTER TABLE `users` +ADD COLUMN `phone_number` INT NULL; + ALTER TABLE `users` ADD COLUMN `phone_number` INT NULL; From 455fe5cb6cb0dab8aec168f92b55074f1a6504ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Wed, 9 Aug 2017 09:59:31 +0200 Subject: [PATCH 13/76] Fix xPlayer is nil --- server/main.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/main.lua b/server/main.lua index 5681607a..0a8d6403 100644 --- a/server/main.lua +++ b/server/main.lua @@ -181,6 +181,7 @@ end) RegisterServerEvent('esx_phone:addPlayerContact') AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) + local xPlayer = ESX.GetPlayerFromId(source) local xPlayers = ESX.GetPlayers() local foundNumber = false local foundPlayer = nil @@ -197,8 +198,6 @@ AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) end if foundNumber then - - local xPlayer = ESX.GetPlayerFromId(source) if phoneNumber == xPlayer.get('phoneNumber') then TriggerClientEvent('esx:showNotification', _source, 'Vous ne pouvez pas vous ajouter vous-même') From 6d6682a3248c8e27f79bda78ef981f72f59401a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Wed, 9 Aug 2017 10:04:00 +0200 Subject: [PATCH 14/76] Add _source --- server/main.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/main.lua b/server/main.lua index 0a8d6403..d2986cc7 100644 --- a/server/main.lua +++ b/server/main.lua @@ -181,7 +181,8 @@ end) RegisterServerEvent('esx_phone:addPlayerContact') AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) - local xPlayer = ESX.GetPlayerFromId(source) + local _source = source + local xPlayer = ESX.GetPlayerFromId(_source) local xPlayers = ESX.GetPlayers() local foundNumber = false local foundPlayer = nil From b0ab7183a89115fea37b5936ed071ab20e57273f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Wed, 9 Aug 2017 10:35:06 +0200 Subject: [PATCH 15/76] Fix source --- server/main.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/main.lua b/server/main.lua index d2986cc7..f6764d03 100644 --- a/server/main.lua +++ b/server/main.lua @@ -245,7 +245,7 @@ AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) end end - TriggerClientEvent('esx_phone:addContact', source, contactName, phoneNumber, isOnline) + TriggerClientEvent('esx_phone:addContact', _source, contactName, phoneNumber, isOnline) end ) From 816d01a80fd8b80e39a0d1dce41807ca295a2690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Wed, 9 Aug 2017 14:13:38 +0200 Subject: [PATCH 16/76] Add LICENSE.txt --- LICENSE.txt | 674 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 674 insertions(+) create mode 100644 LICENSE.txt diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 00000000..30ace6a8 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + {project} Copyright (C) {year} {fullname} + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file From 7acfc3e4fbb2db931d493c15c86c57b89da599fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Fri, 11 Aug 2017 15:46:21 +0200 Subject: [PATCH 17/76] Remove hardcoded phone numbers --- server/main.lua | 37 +------------------------------------ 1 file changed, 1 insertion(+), 36 deletions(-) diff --git a/server/main.lua b/server/main.lua index f6764d03..d0ff5f9a 100644 --- a/server/main.lua +++ b/server/main.lua @@ -263,43 +263,8 @@ end) AddEventHandler('esx_phone:ready', function() TriggerEvent('esx_phone:registerCallback', function(source, phoneNumber, message, anon) - - local xPlayer = ESX.GetPlayerFromId(source) - local xPlayers = ESX.GetPlayers() - local job = 'player' - + local xPlayer = ESX.GetPlayerFromId(source) print('MESSAGE => ' .. xPlayer.name .. '@' .. phoneNumber .. ' : ' .. message) - - for k, v in pairs(xPlayers) do - - if phoneNumber == "police" then - - if v.job.name == 'cop' then - job = "ALERTE POLICE" - TriggerClientEvent('esx_phone:onMessage', v.source, xPlayer.get('phoneNumber'), message, xPlayer.get('coords'), anon, job, GetDistpatchRequestId()) - end - - elseif phoneNumber == "ambulance" then - - if v.job.name == 'ambulance' then - job = "ALERTE AMBULANCE" - TriggerClientEvent('esx_phone:onMessage', v.source, xPlayer.get('phoneNumber'), message, xPlayer.get('coords'), anon, job, GetDistpatchRequestId()) - - end - - elseif phoneNumber == "depanneur" then - - if v.job.name == 'depanneur' then - job = "APPEL DÉPANNEUR" - TriggerClientEvent('esx_phone:onMessage', v.source, xPlayer.get('phoneNumber'), message, xPlayer.get('coords'), anon, job, GetDistpatchRequestId()) - end - - elseif v.get('phoneNumber') == phoneNumber then - TriggerClientEvent('esx_phone:onMessage', v.source, xPlayer.get('phoneNumber'), message, xPlayer.get('coords'), anon, job, false) - end - - end - end) end) From 1f05dc533ccfa1248aea8f9666bcd071a959307f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Tue, 15 Aug 2017 09:58:51 +0200 Subject: [PATCH 18/76] Fix messages not sending to players --- server/main.lua | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/server/main.lua b/server/main.lua index d0ff5f9a..e7b50eac 100644 --- a/server/main.lua +++ b/server/main.lua @@ -263,8 +263,18 @@ end) AddEventHandler('esx_phone:ready', function() TriggerEvent('esx_phone:registerCallback', function(source, phoneNumber, message, anon) - local xPlayer = ESX.GetPlayerFromId(source) + + local xPlayer = ESX.GetPlayerFromId(source) + local xPlayers = ESX.GetPlayers() + print('MESSAGE => ' .. xPlayer.name .. '@' .. phoneNumber .. ' : ' .. message) + + for k, v in pairs(xPlayers) do + if v.get('phoneNumber') == phoneNumber then + TriggerClientEvent('esx_phone:onMessage', v.source, xPlayer.get('phoneNumber'), message, xPlayer.get('coords'), anon, job, false) + end + end + end) end) From 4ff3c0d654e3d26170cb6764be1895491e5ae691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Tue, 15 Aug 2017 14:32:56 +0200 Subject: [PATCH 19/76] Add removeSpecialContact + disable gps option --- client/main.lua | 12 ++++++++++- html/scripts/app.js | 52 +++++++++++++++++++++++++++++++++------------ 2 files changed, 50 insertions(+), 14 deletions(-) diff --git a/client/main.lua b/client/main.lua index 73dc6121..be5ca732 100644 --- a/client/main.lua +++ b/client/main.lua @@ -108,6 +108,16 @@ AddEventHandler('esx_phone:addSpecialContact', function(name, phoneNumber, base6 end) +RegisterNetEvent('esx_phone:removeSpecialContact') +AddEventHandler('esx_phone:removeSpecialContact', function(phoneNumber) + + SendNUIMessage({ + removeSpecialContact = true, + number = phoneNumber + }) + +end) + RegisterNUICallback('add_contact', function(data, cb) local phoneNumber = tonumber(data.phoneNumber) @@ -167,7 +177,7 @@ AddEventHandler('esx_phone:stopDispatch', function(dispatchRequestId, playerName end) -RegisterNUICallback('setGPS', function(data) +RegisterNUICallback('setGPS', function(data) SetNewWaypoint(data.x, data.y) ESX.ShowNotification('Position entrée dans le GPS') end) diff --git a/html/scripts/app.js b/html/scripts/app.js index 4934837c..a4f64f5a 100644 --- a/html/scripts/app.js +++ b/html/scripts/app.js @@ -206,9 +206,9 @@ okNumberData : fromNumber, gpsLocationX : (messages[i].job == 'player') ? '' : messages[i].position.x, gpsLocationY : (messages[i].job == 'player') ? '' : messages[i].position.y, - activeGPS : (messages[i].job == 'player') ? '' : "active", + activeGPS : (messages[i].job == 'player') ? '' : (messages[i].position) ? 'active' : '', jobData : (messages[i].job == 'player') ? '' : messages[i].job, - showOK : (messages[i].job == 'player') ? '' : "showOK" + showOK : (messages[i].job == 'player') ? '' : 'showOK' } let html = Mustache.render(MessageTpl, view); @@ -249,17 +249,40 @@ element.scrollTop += 100; } - window.addSpecialContact = function(name, number, base64Icon){ - - specialContacts.push({ - name : name, - number : number, - base64Icon: base64Icon - }); + let addSpecialContact = function(name, number, base64Icon){ + + let found = false - specialContacts.sort((a,b) => { - return a.name.localeCompare(b.name); - }); + for(let i=0; i { + return a.name.localeCompare(b.name); + }); + + renderSpecialContacts(); + + } + + } + + let removeSpecialContact = function(number){ + + for(let i=0; i Date: Wed, 16 Aug 2017 14:08:47 +0200 Subject: [PATCH 20/76] Try fix phone --- server/main.lua | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/server/main.lua b/server/main.lua index e7b50eac..ccc53a15 100644 --- a/server/main.lua +++ b/server/main.lua @@ -129,7 +129,8 @@ end) RegisterServerEvent('esx_phone:reload') AddEventHandler('esx_phone:reload', function(phoneNumber) - local xPlayer = ESX.GetPlayerFromId(source) + local _source = source + local xPlayer = ESX.GetPlayerFromId(_source) local contacts = {} MySQL.Async.fetchAll( @@ -159,7 +160,7 @@ AddEventHandler('esx_phone:reload', function(phoneNumber) xPlayer.set('contacts', contacts) - TriggerClientEvent('esx_phone:loaded', source, phoneNumber, contacts) + TriggerClientEvent('esx_phone:loaded', _source, phoneNumber, contacts) end ) @@ -173,8 +174,11 @@ end) RegisterServerEvent('esx_phone:send') AddEventHandler('esx_phone:send', function(phoneNumber, message, anon) + + local _source = source + for i=1, #RegisteredCallbacks, 1 do - RegisteredCallbacks[i](source, phoneNumber, message, anon) + RegisteredCallbacks[i](_source, phoneNumber, message, anon) end end) From c7f0dce0c874f7be900d3e95650c18718037e899 Mon Sep 17 00:00:00 2001 From: nearbyplayer Date: Thu, 17 Aug 2017 02:57:06 -0400 Subject: [PATCH 21/76] Multi-language support --- __resource.lua | 12 +++++++-- client/main.lua | 68 ++++++++++++++++++++++++------------------------- config.lua | 2 ++ locales/en.lua | 13 ++++++++++ locales/fr.lua | 13 ++++++++++ server/main.lua | 68 ++++++++++++++++++++++++------------------------- 6 files changed, 106 insertions(+), 70 deletions(-) create mode 100644 config.lua create mode 100644 locales/en.lua create mode 100644 locales/fr.lua diff --git a/__resource.lua b/__resource.lua index c7e09045..b01f9752 100644 --- a/__resource.lua +++ b/__resource.lua @@ -4,10 +4,18 @@ description 'ESX Phone' server_scripts { '@mysql-async/lib/MySQL.lua', + '@es_extended/locale.lua', + 'locales/en.lua', + 'locales/fr.lua', 'server/main.lua' } -client_script 'client/main.lua' +client_scripts { + '@es_extended/locale.lua', + 'locales/en.lua', + 'locales/fr.lua', + 'client/main.lua' +} ui_page 'html/ui.html' @@ -32,4 +40,4 @@ files { 'html/img/icons/write.png', 'html/img/icons/edit.png', 'html/img/icons/location.png' -} \ No newline at end of file +} diff --git a/client/main.lua b/client/main.lua index be5ca732..652a7a17 100644 --- a/client/main.lua +++ b/client/main.lua @@ -1,10 +1,10 @@ local Keys = { - ["ESC"] = 322, ["F1"] = 288, ["F2"] = 289, ["F3"] = 170, ["F5"] = 166, ["F6"] = 167, ["F7"] = 168, ["F8"] = 169, ["F9"] = 56, ["F10"] = 57, - ["~"] = 243, ["1"] = 157, ["2"] = 158, ["3"] = 160, ["4"] = 164, ["5"] = 165, ["6"] = 159, ["7"] = 161, ["8"] = 162, ["9"] = 163, ["-"] = 84, ["="] = 83, ["BACKSPACE"] = 177, + ["ESC"] = 322, ["F1"] = 288, ["F2"] = 289, ["F3"] = 170, ["F5"] = 166, ["F6"] = 167, ["F7"] = 168, ["F8"] = 169, ["F9"] = 56, ["F10"] = 57, + ["~"] = 243, ["1"] = 157, ["2"] = 158, ["3"] = 160, ["4"] = 164, ["5"] = 165, ["6"] = 159, ["7"] = 161, ["8"] = 162, ["9"] = 163, ["-"] = 84, ["="] = 83, ["BACKSPACE"] = 177, ["TAB"] = 37, ["Q"] = 44, ["W"] = 32, ["E"] = 38, ["R"] = 45, ["T"] = 245, ["Y"] = 246, ["U"] = 303, ["P"] = 199, ["["] = 39, ["]"] = 40, ["ENTER"] = 18, ["CAPS"] = 137, ["A"] = 34, ["S"] = 8, ["D"] = 9, ["F"] = 23, ["G"] = 47, ["H"] = 74, ["K"] = 311, ["L"] = 182, ["LEFTSHIFT"] = 21, ["Z"] = 20, ["X"] = 73, ["C"] = 26, ["V"] = 0, ["B"] = 29, ["N"] = 249, ["M"] = 244, [","] = 82, ["."] = 81, - ["LEFTCTRL"] = 36, ["LEFTALT"] = 19, ["SPACE"] = 22, ["RIGHTCTRL"] = 70, + ["LEFTCTRL"] = 36, ["LEFTALT"] = 19, ["SPACE"] = 22, ["RIGHTCTRL"] = 70, ["HOME"] = 213, ["PAGEUP"] = 10, ["PAGEDOWN"] = 11, ["DELETE"] = 178, ["LEFT"] = 174, ["RIGHT"] = 175, ["TOP"] = 27, ["DOWN"] = 173, ["NENTER"] = 201, ["N4"] = 108, ["N5"] = 60, ["N6"] = 107, ["N+"] = 96, ["N-"] = 97, ["N7"] = 117, ["N8"] = 61, ["N9"] = 118 @@ -25,7 +25,7 @@ local CurrentActionData = {} local CurrentDispatchRequestId = -1 Citizen.CreateThread(function() - + while ESX == nil do TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end) Citizen.Wait(0) @@ -38,12 +38,12 @@ end) function OpenPhone() TriggerServerEvent('esx_phone:reload', PhoneData.phoneNumber) - + SendNUIMessage({ showPhone = true, phoneData = PhoneData }) - + GUI.PhoneIsShowed = true ESX.SetTimeout(200, function() @@ -59,21 +59,21 @@ function ClosePhone() }) SetNuiFocus(false) - + GUI.PhoneIsShowed = false end RegisterNetEvent('esx_phone:loaded') AddEventHandler('esx_phone:loaded', function(phoneNumber, contacts) - + PhoneData.phoneNumber = phoneNumber PhoneData.contacts = {} - + for i=1, #contacts, 1 do table.insert(PhoneData.contacts, contacts[i]) end - + SendNUIMessage({ reloadPhone = true, phoneData = PhoneData @@ -83,7 +83,7 @@ end) RegisterNetEvent('esx_phone:addContact') AddEventHandler('esx_phone:addContact', function(name, phoneNumber, isOnline) - + table.insert(PhoneData.contacts, { name = name, number = phoneNumber, @@ -98,7 +98,7 @@ end) RegisterNetEvent('esx_phone:addSpecialContact') AddEventHandler('esx_phone:addSpecialContact', function(name, phoneNumber, base64Icon) - + SendNUIMessage({ addSpecialContact = true, name = name, @@ -110,7 +110,7 @@ end) RegisterNetEvent('esx_phone:removeSpecialContact') AddEventHandler('esx_phone:removeSpecialContact', function(phoneNumber) - + SendNUIMessage({ removeSpecialContact = true, number = phoneNumber @@ -119,10 +119,10 @@ AddEventHandler('esx_phone:removeSpecialContact', function(phoneNumber) end) RegisterNUICallback('add_contact', function(data, cb) - + local phoneNumber = tonumber(data.phoneNumber) local contactName = tostring(data.contactName) - + if phoneNumber then TriggerServerEvent('esx_phone:addPlayerContact', phoneNumber, contactName) end @@ -132,9 +132,9 @@ end) RegisterNetEvent('esx_phone:onMessage') AddEventHandler('esx_phone:onMessage', function(phoneNumber, message, position, anon, job, dispatchRequestId) - - ESX.ShowNotification('~b~Nouveau message') - + + ESX.ShowNotification(_U('new_message')) + SendNUIMessage({ newMessage = true, phoneNumber = phoneNumber, @@ -147,9 +147,9 @@ AddEventHandler('esx_phone:onMessage', function(phoneNumber, message, position, if dispatchRequestId then CurrentAction = 'dispatch' - CurrentActionMsg = job .. ' - Appuez sur ~INPUT_CONTEXT~ pour prendre l\'appel' + CurrentActionMsg = job .. _U('press_take_call') CurrentDispatchRequestId = dispatchRequestId - + CurrentActionData = { phoneNumber = phoneNumber, message = message, @@ -172,31 +172,31 @@ AddEventHandler('esx_phone:stopDispatch', function(dispatchRequestId, playerName if CurrentDispatchRequestId == dispatchRequestId and CurrentAction == 'dispatch' then CurrentAction = nil - ESX.ShowNotification(playerName .. ' a pris l\'appel') + ESX.ShowNotification(playerName .. _U('taken_call')) end end) RegisterNUICallback('setGPS', function(data) SetNewWaypoint(data.x, data.y) - ESX.ShowNotification('Position entrée dans le GPS') + ESX.ShowNotification(_U('gps_position')) end) -RegisterNUICallback('send', function(data) - +RegisterNUICallback('send', function(data) + local phoneNumber = data.number if tonumber(phoneNumber) ~= nil then phoneNumber = tonumber(phoneNumber) end - TriggerServerEvent('esx_phone:send', phoneNumber, data.message, data.anonyme) - + TriggerServerEvent('esx_phone:send', phoneNumber, data.message, data.anonyme) + SendNUIMessage({ showMessageEditor = false }) - ESX.ShowNotification('Message envoyé') + ESX.ShowNotification(_U('message_sent')) end) @@ -206,9 +206,9 @@ end) Citizen.CreateThread(function() while true do - + Wait(0) - + if GUI.PhoneIsShowed then -- codes here: https://pastebin.com/guYd0ht4 DisableControlAction(0, 1, true) -- LookLeftRight DisableControlAction(0, 2, true) -- LookUpDown @@ -221,7 +221,7 @@ Citizen.CreateThread(function() DisableControlAction(0, 142, true) -- Melee Attack Alternate DisableControlAction(0, 257, true) -- Input Attack 2 DisableControlAction(0, 263, true) -- Input Melee Attack - DisableControlAction(0, 264, true) -- Input Melee Attack 2 + DisableControlAction(0, 264, true) -- Input Melee Attack 2 DisableControlAction(0, 12, true) -- Weapon Wheel Up Down DisableControlAction(0, 14, true) -- Weapon Wheel Next @@ -231,7 +231,7 @@ Citizen.CreateThread(function() else if IsControlPressed(0, Keys['F1']) and (GetGameTimer() - GUI.Time) > 150 then - + if not ESX.UI.Menu.IsOpen('phone', GetCurrentResourceName(), 'main') then ESX.UI.Menu.CloseAll() ESX.UI.Menu.Open('phone', GetCurrentResourceName(), 'main') @@ -258,7 +258,7 @@ Citizen.CreateThread(function() DisplayHelpTextFromStringLabel(0, 0, 1, -1) if IsControlPressed(0, Keys['E']) and (GetGameTimer() - GUI.Time) > 300 then - + if CurrentAction == 'dispatch' then TriggerServerEvent('esx_phone:stopDispatch', CurrentDispatchRequestId) SetNewWaypoint(CurrentActionData.position.x, CurrentActionData.position.y) @@ -266,10 +266,10 @@ Citizen.CreateThread(function() CurrentAction = nil GUI.Time = GetGameTimer() - + end end end -end) \ No newline at end of file +end) diff --git a/config.lua b/config.lua new file mode 100644 index 00000000..56a8b5e9 --- /dev/null +++ b/config.lua @@ -0,0 +1,2 @@ +Config = {} +Config.Locale = 'fr' diff --git a/locales/en.lua b/locales/en.lua new file mode 100644 index 00000000..7d7393f8 --- /dev/null +++ b/locales/en.lua @@ -0,0 +1,13 @@ +Locales['en'] = { + + ['new_message'] = '~b~New message', + ['press_take_call'] = ' - Press to take the call', + ['taken_call'] = ' has taken the call', + ['gps_position'] = 'destination entered into the GPS', + ['message_sent'] = 'message sent', + ['cannot_add_self'] = 'you can not add yourself', + ['number_in_contacts'] = 'this number is alraedy in your contact list', + ['contact_added'] = 'contact added', + ['number_not_assigned'] = 'this number is not assigned...', + +} diff --git a/locales/fr.lua b/locales/fr.lua new file mode 100644 index 00000000..4927ec7c --- /dev/null +++ b/locales/fr.lua @@ -0,0 +1,13 @@ +Locales['fr'] = { + + ['new_message'] = '~b~Nouveau message', + ['press_take_call'] = ' - Appuyez sur ~INPUT_CONTEXT~ pour prendre l\'appel', + ['taken_call'] = ' a pris l\'appel', + ['gps_position'] = 'position entrée dans le GPS', + ['message_sent'] = 'message envoyé', + ['cannot_add_self'] = 'vous ne pouvez pas vous ajouter vous-même', + ['number_in_contacts'] = 'ce numéro est déja dans votre liste de contacts', + ['contact_added'] = 'contact ajouté', + ['number_not_assigned'] = 'ce numéro n\'est pas attribué...', + +} diff --git a/server/main.lua b/server/main.lua index ccc53a15..b1555710 100644 --- a/server/main.lua +++ b/server/main.lua @@ -8,14 +8,14 @@ local RegisteredCallbacks = {} local DisptachRequestId = 0 function GenerateUniquePhoneNumber() - + local foundNumber = false local phoneNumber = nil - + while not foundNumber do - + phoneNumber = math.random(10000, 99999) - + local result = MySQL.Sync.fetchAll( 'SELECT COUNT(*) as count FROM users WHERE phone_number = @phoneNumber', { @@ -24,7 +24,7 @@ function GenerateUniquePhoneNumber() ) local count = tonumber(result[1].count) - + if count == 0 then foundNumber = true end @@ -72,9 +72,9 @@ AddEventHandler('esx:playerLoaded', function(source) local phoneNumber = result[1].phone_number if phoneNumber == nil then - + phoneNumber = GenerateUniquePhoneNumber() - + MySQL.Async.execute( 'UPDATE users SET phone_number = @phone_number WHERE identifier = @identifier', { @@ -83,9 +83,9 @@ AddEventHandler('esx:playerLoaded', function(source) } ) end - + xPlayer.set('phoneNumber', phoneNumber) - + local contacts = {} MySQL.Async.fetchAll( @@ -96,13 +96,13 @@ AddEventHandler('esx:playerLoaded', function(source) function(result2) for i=1, #result2, 1 do - + table.insert(contacts, { name = result2[i].name, number = result2[i].number, online = false }) - + local xPlayers = ESX.GetPlayers() for k, v in pairs(xPlayers) do @@ -112,9 +112,9 @@ AddEventHandler('esx:playerLoaded', function(source) end end - + xPlayer.set('contacts', contacts) - + TriggerClientEvent('esx_phone:loaded', source, phoneNumber, contacts) end @@ -141,13 +141,13 @@ AddEventHandler('esx_phone:reload', function(phoneNumber) function(result2) for i=1, #result2, 1 do - + table.insert(contacts, { name = result2[i].name, number = result2[i].number, online = false }) - + local xPlayers = ESX.GetPlayers() for k, v in pairs(xPlayers) do @@ -157,7 +157,7 @@ AddEventHandler('esx_phone:reload', function(phoneNumber) end end - + xPlayer.set('contacts', contacts) TriggerClientEvent('esx_phone:loaded', _source, phoneNumber, contacts) @@ -192,7 +192,7 @@ AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) local foundPlayer = nil MySQL.Async.fetchAll( - 'SELECT phone_number FROM users WHERE phone_number = @number', + 'SELECT phone_number FROM users WHERE phone_number = @number', { ['@number'] = phoneNumber }, @@ -203,14 +203,14 @@ AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) end if foundNumber then - + if phoneNumber == xPlayer.get('phoneNumber') then - TriggerClientEvent('esx:showNotification', _source, 'Vous ne pouvez pas vous ajouter vous-même') + TriggerClientEvent('esx:showNotification', _source, _U('cannot_add_self')) else local hasAlreadyAdded = false local contacts = xPlayer.get('contacts') - + for i=1, #contacts, 1 do if contacts[i].number == phoneNumber then hasAlreadyAdded = true @@ -218,9 +218,9 @@ AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) end if hasAlreadyAdded then - TriggerClientEvent('esx:showNotification', _source, 'Ce numéro est déja dans votre liste de contacts') + TriggerClientEvent('esx:showNotification', _source, _U('number_in_contacts')) else - + table.insert(contacts, { name = contactName, number = phoneNumber, @@ -237,27 +237,27 @@ AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) }, function(rowsChanged) - TriggerClientEvent('esx:showNotification', _source, 'Contact ajouté') - + TriggerClientEvent('esx:showNotification', _source, _U('contact_added')) + local xPlayers = ESX.GetPlayers() local isOnline = false - + for k,v in pairs(xPlayers) do if v.get('phoneNumber') == phoneNumber then isOnline = true break end end - + TriggerClientEvent('esx_phone:addContact', _source, contactName, phoneNumber, isOnline) end ) - + end end else - TriggerClientEvent('esx:showNotification', source, 'Ce numéro n\'est pas attribué...') + TriggerClientEvent('esx:showNotification', source, _U('number_not_assigned')) end end @@ -267,17 +267,17 @@ end) AddEventHandler('esx_phone:ready', function() TriggerEvent('esx_phone:registerCallback', function(source, phoneNumber, message, anon) - + local xPlayer = ESX.GetPlayerFromId(source) local xPlayers = ESX.GetPlayers() print('MESSAGE => ' .. xPlayer.name .. '@' .. phoneNumber .. ' : ' .. message) for k, v in pairs(xPlayers) do - if v.get('phoneNumber') == phoneNumber then - TriggerClientEvent('esx_phone:onMessage', v.source, xPlayer.get('phoneNumber'), message, xPlayer.get('coords'), anon, job, false) - end - end + if v.get('phoneNumber') == phoneNumber then + TriggerClientEvent('esx_phone:onMessage', v.source, xPlayer.get('phoneNumber'), message, xPlayer.get('coords'), anon, job, false) + end + end end) end) @@ -285,4 +285,4 @@ end) RegisterServerEvent('esx_phone:stopDispatch') AddEventHandler('esx_phone:stopDispatch', function(dispatchRequestId) TriggerClientEvent('esx_phone:stopDispatch', -1, dispatchRequestId, GetPlayerName(source)) -end) \ No newline at end of file +end) From 170eb71587c1293458287253587e6cf97b0c15cd Mon Sep 17 00:00:00 2001 From: Don <30905704+ddh3@users.noreply.github.com> Date: Thu, 17 Aug 2017 02:49:58 -0700 Subject: [PATCH 22/76] Update en.lua --- locales/en.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.lua b/locales/en.lua index 7d7393f8..94bf3a54 100644 --- a/locales/en.lua +++ b/locales/en.lua @@ -1,7 +1,7 @@ Locales['en'] = { ['new_message'] = '~b~New message', - ['press_take_call'] = ' - Press to take the call', + ['press_take_call'] = ' - Press ~INPUT_CONTEXT~ to take the call', ['taken_call'] = ' has taken the call', ['gps_position'] = 'destination entered into the GPS', ['message_sent'] = 'message sent', From a99e47718f7ff44b09f01cd50067afb1af8258d9 Mon Sep 17 00:00:00 2001 From: nlmarcio Date: Thu, 17 Aug 2017 23:45:38 +0200 Subject: [PATCH 23/76] Phone image modification --- html/img/phone.png | Bin 18906 -> 19471 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/html/img/phone.png b/html/img/phone.png index 47f6f82fcb850b516b76a137c3a99e6137146760..2c2c8adb63ec344b9fe8b789df021478909de7c7 100644 GIT binary patch literal 19471 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 XPCnY@+C~`#4(+hAu!q%{UrYEmIL@=p literal 18906 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 Date: Fri, 18 Aug 2017 10:23:00 +0200 Subject: [PATCH 24/76] Fix playername not showing + remove gps for players --- server/main.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/main.lua b/server/main.lua index b1555710..9fecf1a7 100644 --- a/server/main.lua +++ b/server/main.lua @@ -275,7 +275,7 @@ AddEventHandler('esx_phone:ready', function() for k, v in pairs(xPlayers) do if v.get('phoneNumber') == phoneNumber then - TriggerClientEvent('esx_phone:onMessage', v.source, xPlayer.get('phoneNumber'), message, xPlayer.get('coords'), anon, job, false) + TriggerClientEvent('esx_phone:onMessage', v.source, xPlayer.get('phoneNumber'), message, false, anon, 'player', false) end end From 2bb542cba22b427db1f86c93942f7abe4a44dd3c Mon Sep 17 00:00:00 2001 From: Allan <31086346+TakumiBR@users.noreply.github.com> Date: Fri, 18 Aug 2017 12:07:23 -0700 Subject: [PATCH 25/76] Multi-language support Brazilian Portuguese translation. [ATTENTION] The ui_br.html file that is in the html folder must be renamed to ui.html to run the translation. --- __resource.lua | 2 ++ html/ui_br.html | 82 +++++++++++++++++++++++++++++++++++++++++++++++++ locales/br.lua | 13 ++++++++ 3 files changed, 97 insertions(+) create mode 100644 html/ui_br.html create mode 100644 locales/br.lua diff --git a/__resource.lua b/__resource.lua index b01f9752..1283b1cc 100644 --- a/__resource.lua +++ b/__resource.lua @@ -5,6 +5,7 @@ description 'ESX Phone' server_scripts { '@mysql-async/lib/MySQL.lua', '@es_extended/locale.lua', + 'locales/br.lua', 'locales/en.lua', 'locales/fr.lua', 'server/main.lua' @@ -12,6 +13,7 @@ server_scripts { client_scripts { '@es_extended/locale.lua', + 'locales/br.lua', 'locales/en.lua', 'locales/fr.lua', 'client/main.lua' diff --git a/html/ui_br.html b/html/ui_br.html new file mode 100644 index 00000000..9a28cd97 --- /dev/null +++ b/html/ui_br.html @@ -0,0 +1,82 @@ + + + + + + + + +
    + +
    + +
    + + #123456 +
    + + + +
    + +
    Agenda
    +
    +
    +
    +

    Nenhum contato

    +
    +
    +
    +
    + +
    +
    Mensagens
    +
    +
    +
    +

    Nenhuma mensagem

    +
    +
    +
    +
    + +
    +
    +
    +
    + + + + + +
    +
    +
    + +
    +
    Adicionar contato
    +
    +
    + + + + +
    +
    +
    + +
    +
    + + + + + + + + \ No newline at end of file diff --git a/locales/br.lua b/locales/br.lua new file mode 100644 index 00000000..3017ac44 --- /dev/null +++ b/locales/br.lua @@ -0,0 +1,13 @@ +Locales['br'] = { + + ['new_message'] = '~b~Nova mensagem', + ['press_take_call'] = ' - Pressione ~INPUT_CONTEXT~ para aceitar a chamada', + ['taken_call'] = ' recebeu uma chamada', + ['gps_position'] = 'Destino inserido no GPS', + ['message_sent'] = 'mensagem enviada', + ['cannot_add_self'] = 'Você não pode se adicionar', + ['number_in_contacts'] = 'Este número já está na sua lista de contatos', + ['contact_added'] = 'Contato adicionado', + ['number_not_assigned'] = 'Este número não foi atribuído...', + +} From 71150e91e469510bb6bd659492a0f9733f854f1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Sat, 19 Aug 2017 11:24:14 +0200 Subject: [PATCH 26/76] Add missing config.lua in __resource.lua --- __resource.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/__resource.lua b/__resource.lua index 1283b1cc..4f737ba0 100644 --- a/__resource.lua +++ b/__resource.lua @@ -4,8 +4,8 @@ description 'ESX Phone' server_scripts { '@mysql-async/lib/MySQL.lua', + 'config.lua', '@es_extended/locale.lua', - 'locales/br.lua', 'locales/en.lua', 'locales/fr.lua', 'server/main.lua' @@ -13,7 +13,7 @@ server_scripts { client_scripts { '@es_extended/locale.lua', - 'locales/br.lua', + 'config.lua', 'locales/en.lua', 'locales/fr.lua', 'client/main.lua' From b415047b1dea4dc87bcff4777b7f7b2ae65ca98a Mon Sep 17 00:00:00 2001 From: nearbyplayer Date: Sat, 19 Aug 2017 19:15:38 -0400 Subject: [PATCH 27/76] Added English HTML / JS variants --- __resource.lua | 6 +- html/scripts/app_en.js | 489 +++++++++++++++++++++++++++++++++++++++++ html/ui_en.html | 82 +++++++ 3 files changed, 575 insertions(+), 2 deletions(-) create mode 100644 html/scripts/app_en.js create mode 100644 html/ui_en.html diff --git a/__resource.lua b/__resource.lua index 4f737ba0..226a9dc4 100644 --- a/__resource.lua +++ b/__resource.lua @@ -22,12 +22,14 @@ client_scripts { ui_page 'html/ui.html' files { - 'html/ui.html', + 'html/ui.html', -- FR + --'html/ui_en.html', -- EN 'html/bankgothic.ttf', 'html/pdown.ttf', 'html/css/app.css', 'html/scripts/mustache.min.js', - 'html/scripts/app.js', + 'html/scripts/app.js', -- FR + --'html/scripts/app_en.js', -- EN 'html/img/cursor.png', 'html/img/keys/enter.png', 'html/img/keys/return.png', diff --git a/html/scripts/app_en.js b/html/scripts/app_en.js new file mode 100644 index 00000000..cd4ef5c2 --- /dev/null +++ b/html/scripts/app_en.js @@ -0,0 +1,489 @@ +(function(){ + + let ContactTpl = + '
    ' + + '
    ' + + '
    ' + + '{{sender}}
    #{{phoneNumber}}' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + ; + + let MessageTpl = + '
    ' + + '
    ' + + '
    ' + + '{{sender}}
    #{{phoneNumber}}' + + '
    ' + + '
    ' + + '
    {{message}}
    ' + + '
    ' + + '
    ' + ; + + let SpecialContactTpl = '
  • {{name}}
  • '; + + let contacts = []; + let specialContacts = []; + let menu = []; + let currentItem = 0; + let currentVal = null; + let isMessageEditorOpen = false; + let isMessagesOpen = false; + let isPhoneShowed = false; + + let showMain = function() { + $('.screen').removeClass('active'); + } + + let showRepertoire = function() { + $('#repertoire').addClass('active'); + } + + let hideRepertoire = function() { + $('#repertoire').removeClass('active'); + } + + let showMessages = function(){ + $('#messages').addClass('active'); + isMessagesOpen = true; + } + + let hideMessages = function(){ + $('#messages').removeClass('active'); + isMessagesOpen = false; + } + + let showAddContact = function() { + $('#contact').addClass('active'); + } + + let hideAddContact = function() { + $('#contact').removeClass('active'); + $('#contact_name').val(''); + $('#contact_number').val(''); + } + + let showNewMessage = function(cnum, cname) { + $('#writer').addClass('active'); + $('#writer_number').val(cnum); + $('#writer .header-title').html(cname); + } + + let hideNewMessage = function() { + $('#writer').removeClass('active'); + $('#writer_number').val(''); + $('#writer_message').val(''); + $('#writer .header-title').html(''); + } + + let showGPS = function(xPos, yPos) { + $.post('http://esx_phone/setGPS', JSON.stringify({ + x: parseFloat(xPos), + y: parseFloat(yPos) + } + )); + } + + let renderContacts = function(){ + + let contactHTML = ''; + + if(contacts.length > 0) { + + for(let i=0; i 0) { + + for(let i=0; i { + return a.name.localeCompare(b.name); + }); + + renderSpecialContacts(); + + } + + } + + let removeSpecialContact = function(number){ + + for(let i=0; iHome' + + '
  • Messages
  • ' + ); + + for(let i=0; i + + + + + + + +
    + +
    + +
    + + #123456 +
    + + + +
    + +
    Home
    +
    +
    +
    +

    No contacts

    +
    +
    +
    +
    + +
    +
    Messages
    +
    +
    +
    +

    No messages

    +
    +
    +
    +
    + +
    +
    +
    +
    + + + + + +
    +
    +
    + +
    +
    Add a contact
    +
    +
    + + + + +
    +
    +
    + +
    +
    + + + + + + + + From b0d8604e96688d3e56405c32e494d8ef25bc062c Mon Sep 17 00:00:00 2001 From: MagnanQC Date: Sun, 20 Aug 2017 00:06:04 -0400 Subject: [PATCH 28/76] Removed duplicate --- esx_phone.sql | 3 --- 1 file changed, 3 deletions(-) diff --git a/esx_phone.sql b/esx_phone.sql index 62842ccc..521f074a 100644 --- a/esx_phone.sql +++ b/esx_phone.sql @@ -8,6 +8,3 @@ CREATE TABLE user_contacts ( ALTER TABLE `users` ADD COLUMN `phone_number` INT NULL; - -ALTER TABLE `users` -ADD COLUMN `phone_number` INT NULL; From 2bdf2f16049d5e7a0f209e398d6e71041da0ab75 Mon Sep 17 00:00:00 2001 From: nearbyplayer Date: Sun, 20 Aug 2017 21:23:40 -0400 Subject: [PATCH 29/76] Update English HTML UI --- __resource.lua | 6 ++---- html/ui_en.html | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/__resource.lua b/__resource.lua index 226a9dc4..4f737ba0 100644 --- a/__resource.lua +++ b/__resource.lua @@ -22,14 +22,12 @@ client_scripts { ui_page 'html/ui.html' files { - 'html/ui.html', -- FR - --'html/ui_en.html', -- EN + 'html/ui.html', 'html/bankgothic.ttf', 'html/pdown.ttf', 'html/css/app.css', 'html/scripts/mustache.min.js', - 'html/scripts/app.js', -- FR - --'html/scripts/app_en.js', -- EN + 'html/scripts/app.js', 'html/img/cursor.png', 'html/img/keys/enter.png', 'html/img/keys/return.png', diff --git a/html/ui_en.html b/html/ui_en.html index cac0462b..b5227c83 100644 --- a/html/ui_en.html +++ b/html/ui_en.html @@ -17,14 +17,14 @@
    -
    Home
    +
    Phone
    From 08e79e8a41922bd5c959eabbdc667d29eaf0de9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Mon, 21 Aug 2017 21:02:14 +0200 Subject: [PATCH 30/76] Update GetPlayers --- server/main.lua | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/server/main.lua b/server/main.lua index 9fecf1a7..3fbeb136 100644 --- a/server/main.lua +++ b/server/main.lua @@ -105,8 +105,11 @@ AddEventHandler('esx:playerLoaded', function(source) local xPlayers = ESX.GetPlayers() - for k, v in pairs(xPlayers) do - if v.get('phoneNumber') == contacts[i].number then + for i=1, #xPlayers, 1 do + + local xPlayer2 = ESX.GetPlayerFromId(xPlayers[i]) + + if xPlayer2.get('phoneNumber') == contacts[i].number then contacts[i].online = true end end @@ -150,8 +153,11 @@ AddEventHandler('esx_phone:reload', function(phoneNumber) local xPlayers = ESX.GetPlayers() - for k, v in pairs(xPlayers) do - if v.get('phoneNumber') == contacts[i].number then + for i=1, #xPlayers, 1 do + + local xPlayer2 = ESX.GetPlayerFromId(xPlayers[i]) + + if xPlayer2.get('phoneNumber') == contacts[i].number then contacts[i].online = true end end @@ -174,7 +180,7 @@ end) RegisterServerEvent('esx_phone:send') AddEventHandler('esx_phone:send', function(phoneNumber, message, anon) - + local _source = source for i=1, #RegisteredCallbacks, 1 do @@ -187,7 +193,6 @@ AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) local _source = source local xPlayer = ESX.GetPlayerFromId(_source) - local xPlayers = ESX.GetPlayers() local foundNumber = false local foundPlayer = nil @@ -242,8 +247,11 @@ AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) local xPlayers = ESX.GetPlayers() local isOnline = false - for k,v in pairs(xPlayers) do - if v.get('phoneNumber') == phoneNumber then + for i=1, #xPlayers, 1 do + + local xPlayer2 = ESX.GetPlayerFromId(xPlayers[i]) + + if xPlayer2.get('phoneNumber') == phoneNumber then isOnline = true break end @@ -273,9 +281,12 @@ AddEventHandler('esx_phone:ready', function() print('MESSAGE => ' .. xPlayer.name .. '@' .. phoneNumber .. ' : ' .. message) - for k, v in pairs(xPlayers) do - if v.get('phoneNumber') == phoneNumber then - TriggerClientEvent('esx_phone:onMessage', v.source, xPlayer.get('phoneNumber'), message, false, anon, 'player', false) + for i=1, #xPlayers, 1 do + + local xPlayer2 = ESX.GetPlayerFromId(xPlayers[i]) + + if xPlayer2.get('phoneNumber') == phoneNumber then + TriggerClientEvent('esx_phone:onMessage', xPlayer2.source, xPlayer.get('phoneNumber'), message, false, anon, 'player', false) end end From d1dd31cf9331aa2be84fef8e0324c23bd38dbb1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Mon, 21 Aug 2017 21:41:46 +0200 Subject: [PATCH 31/76] Fix error --- server/main.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/main.lua b/server/main.lua index 3fbeb136..a453ba2a 100644 --- a/server/main.lua +++ b/server/main.lua @@ -105,9 +105,9 @@ AddEventHandler('esx:playerLoaded', function(source) local xPlayers = ESX.GetPlayers() - for i=1, #xPlayers, 1 do + for j=1, #xPlayers, 1 do - local xPlayer2 = ESX.GetPlayerFromId(xPlayers[i]) + local xPlayer2 = ESX.GetPlayerFromId(xPlayers[j]) if xPlayer2.get('phoneNumber') == contacts[i].number then contacts[i].online = true From 0e33603920a2be97a58ca62d6b99dd4c68a5f5f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Mon, 21 Aug 2017 21:48:40 +0200 Subject: [PATCH 32/76] Fix double i in loop --- server/main.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/main.lua b/server/main.lua index a453ba2a..e4df95f7 100644 --- a/server/main.lua +++ b/server/main.lua @@ -153,9 +153,9 @@ AddEventHandler('esx_phone:reload', function(phoneNumber) local xPlayers = ESX.GetPlayers() - for i=1, #xPlayers, 1 do + for j=1, #xPlayers, 1 do - local xPlayer2 = ESX.GetPlayerFromId(xPlayers[i]) + local xPlayer2 = ESX.GetPlayerFromId(xPlayers[j]) if xPlayer2.get('phoneNumber') == contacts[i].number then contacts[i].online = true From 7a217d7a336e43e5c01a3c320d1b724e511ef8da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Tue, 22 Aug 2017 03:22:46 +0200 Subject: [PATCH 33/76] Increase UI timeout --- client/main.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/main.lua b/client/main.lua index 652a7a17..5ba395b5 100644 --- a/client/main.lua +++ b/client/main.lua @@ -46,7 +46,7 @@ function OpenPhone() GUI.PhoneIsShowed = true - ESX.SetTimeout(200, function() + ESX.SetTimeout(250, function() SetNuiFocus(true, true) end) From 0534ab38bc5200ab733fd6232fe7478052c7d239 Mon Sep 17 00:00:00 2001 From: nearbyplayer Date: Wed, 23 Aug 2017 20:04:34 -0400 Subject: [PATCH 34/76] Update English UI --- html/scripts/app_en.js | 2 +- html/ui_en.html | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/html/scripts/app_en.js b/html/scripts/app_en.js index cd4ef5c2..3b91ca33 100644 --- a/html/scripts/app_en.js +++ b/html/scripts/app_en.js @@ -293,7 +293,7 @@ $('.phone-icon').unbind('click'); $('#phone .menu .home').html( - '
  • Home
  • ' + + '
  • Contacts
  • ' + '
  • Messages
  • ' ); diff --git a/html/ui_en.html b/html/ui_en.html index b5227c83..9aaacf2b 100644 --- a/html/ui_en.html +++ b/html/ui_en.html @@ -17,14 +17,14 @@
    -
    Phone
    +
    Contacts
    From bc8d30e6586ca817c19b2dd790442e32e11f5f13 Mon Sep 17 00:00:00 2001 From: ultratm Date: Sat, 26 Aug 2017 12:25:12 +0200 Subject: [PATCH 35/76] Add German Language --- __resource.lua | 4 ++++ locales/de.lua | 13 +++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 locales/de.lua diff --git a/__resource.lua b/__resource.lua index 4f737ba0..5eb22590 100644 --- a/__resource.lua +++ b/__resource.lua @@ -6,6 +6,8 @@ server_scripts { '@mysql-async/lib/MySQL.lua', 'config.lua', '@es_extended/locale.lua', + 'locales/de.lua', + 'locales/br.lua', 'locales/en.lua', 'locales/fr.lua', 'server/main.lua' @@ -14,6 +16,8 @@ server_scripts { client_scripts { '@es_extended/locale.lua', 'config.lua', + 'locales/de.lua', + 'locales/br.lua', 'locales/en.lua', 'locales/fr.lua', 'client/main.lua' diff --git a/locales/de.lua b/locales/de.lua new file mode 100644 index 00000000..e69dae2e --- /dev/null +++ b/locales/de.lua @@ -0,0 +1,13 @@ +Locales['de'] = { + + ['new_message'] = '~b~Neue Nachricht', + ['press_take_call'] = ' - Drücke ~INPUT_CONTEXT~ um den Anruf anzunehmen', + ['taken_call'] = ' hat den Anruf angenommen', + ['gps_position'] = 'Ziel in GPS eingegeben', + ['message_sent'] = 'nachricht gesendet', + ['cannot_add_self'] = 'du kannst dich nicht selbst hinzufügen', + ['number_in_contacts'] = 'diese Nummer ist bereits in deinen Kontakten', + ['contact_added'] = 'Kontakt hinzugefügt', + ['number_not_assigned'] = 'diese Nummer ist nicht vergeben...', + +} From 32ae7a000ba572fcc38ecf17d926c7e851eb5759 Mon Sep 17 00:00:00 2001 From: ultratm Date: Sat, 26 Aug 2017 12:27:59 +0200 Subject: [PATCH 36/76] Add German ui.html --- html/ui_de.html | 82 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 html/ui_de.html diff --git a/html/ui_de.html b/html/ui_de.html new file mode 100644 index 00000000..55d92593 --- /dev/null +++ b/html/ui_de.html @@ -0,0 +1,82 @@ + + + + + + + + +
    + +
    + +
    + + #123456 +
    + + + +
    + +
    Kontakte
    +
    +
    +
    +

    Keine Kontakte

    +
    +
    +
    +
    + +
    +
    Nachrichten
    +
    +
    +
    +

    Keine Nachrichten

    +
    +
    +
    +
    + +
    +
    +
    +
    + + + + + +
    +
    +
    + +
    +
    Kontakt hinzufügen
    +
    +
    + + + + +
    +
    +
    + +
    +
    + + + + + + + + From bcfb6c2adbd6624269ef889d1b768a6af3555a00 Mon Sep 17 00:00:00 2001 From: ultratm Date: Sat, 26 Aug 2017 12:33:40 +0200 Subject: [PATCH 37/76] Add German app_de.js --- html/scripts/app_de.js | 489 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 489 insertions(+) create mode 100644 html/scripts/app_de.js diff --git a/html/scripts/app_de.js b/html/scripts/app_de.js new file mode 100644 index 00000000..bdc0e71f --- /dev/null +++ b/html/scripts/app_de.js @@ -0,0 +1,489 @@ +(function(){ + + let ContactTpl = + '
    ' + + '
    ' + + '
    ' + + '{{sender}}
    #{{phoneNumber}}' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + ; + + let MessageTpl = + '
    ' + + '
    ' + + '
    ' + + '{{sender}}
    #{{phoneNumber}}' + + '
    ' + + '
    ' + + '
    {{message}}
    ' + + '
    ' + + '
    ' + ; + + let SpecialContactTpl = '
  • {{name}}
  • '; + + let contacts = []; + let specialContacts = []; + let menu = []; + let currentItem = 0; + let currentVal = null; + let isMessageEditorOpen = false; + let isMessagesOpen = false; + let isPhoneShowed = false; + + let showMain = function() { + $('.screen').removeClass('active'); + } + + let showRepertoire = function() { + $('#repertoire').addClass('active'); + } + + let hideRepertoire = function() { + $('#repertoire').removeClass('active'); + } + + let showMessages = function(){ + $('#messages').addClass('active'); + isMessagesOpen = true; + } + + let hideMessages = function(){ + $('#messages').removeClass('active'); + isMessagesOpen = false; + } + + let showAddContact = function() { + $('#contact').addClass('active'); + } + + let hideAddContact = function() { + $('#contact').removeClass('active'); + $('#contact_name').val(''); + $('#contact_number').val(''); + } + + let showNewMessage = function(cnum, cname) { + $('#writer').addClass('active'); + $('#writer_number').val(cnum); + $('#writer .header-title').html(cname); + } + + let hideNewMessage = function() { + $('#writer').removeClass('active'); + $('#writer_number').val(''); + $('#writer_message').val(''); + $('#writer .header-title').html(''); + } + + let showGPS = function(xPos, yPos) { + $.post('http://esx_phone/setGPS', JSON.stringify({ + x: parseFloat(xPos), + y: parseFloat(yPos) + } + )); + } + + let renderContacts = function(){ + + let contactHTML = ''; + + if(contacts.length > 0) { + + for(let i=0; i 0) { + + for(let i=0; i { + return a.name.localeCompare(b.name); + }); + + renderSpecialContacts(); + + } + + } + + let removeSpecialContact = function(number){ + + for(let i=0; iKontakte' + + '
  • Nachrichten
  • ' + ); + + for(let i=0; i Date: Sat, 2 Sep 2017 00:35:35 +0200 Subject: [PATCH 38/76] Rewrite phone => No more server lags --- client/main.lua | 26 ++++- html/scripts/app.js | 6 ++ html/scripts/app_de.js | 104 ++++++++++---------- html/scripts/app_en.js | 104 ++++++++++---------- locales/br.lua | 2 +- locales/en.lua | 2 +- locales/fr.lua | 2 +- server/main.lua | 215 ++++++++++++++++++++++++----------------- 8 files changed, 266 insertions(+), 195 deletions(-) diff --git a/client/main.lua b/client/main.lua index 5ba395b5..ed69f73c 100644 --- a/client/main.lua +++ b/client/main.lua @@ -23,6 +23,7 @@ local CurrentAction = nil local CurrentActionMsg = '' local CurrentActionData = {} local CurrentDispatchRequestId = -1 +local PhoneNumberSources = {} Citizen.CreateThread(function() @@ -71,6 +72,7 @@ AddEventHandler('esx_phone:loaded', function(phoneNumber, contacts) PhoneData.contacts = {} for i=1, #contacts, 1 do + contacts[i].online = (PhoneNumberSources[contacts[i].number] == nil and false or NetworkIsPlayerActive(GetPlayerFromServerId(PhoneNumberSources[contacts[i].number]))), table.insert(PhoneData.contacts, contacts[i]) end @@ -82,12 +84,12 @@ AddEventHandler('esx_phone:loaded', function(phoneNumber, contacts) end) RegisterNetEvent('esx_phone:addContact') -AddEventHandler('esx_phone:addContact', function(name, phoneNumber, isOnline) +AddEventHandler('esx_phone:addContact', function(name, phoneNumber) table.insert(PhoneData.contacts, { name = name, number = phoneNumber, - online = isOnline + online = (PhoneNumberSources[contacts[i].number] == nil and false or NetworkIsPlayerActive(GetPlayerFromServerId(PhoneNumberSources[contacts[i].number]))), }) -- CALL HERE RELOADCONTACT SendNUIMessage({ @@ -133,7 +135,7 @@ end) RegisterNetEvent('esx_phone:onMessage') AddEventHandler('esx_phone:onMessage', function(phoneNumber, message, position, anon, job, dispatchRequestId) - ESX.ShowNotification(_U('new_message')) + ESX.ShowNotification(_U('new_message', message)) SendNUIMessage({ newMessage = true, @@ -177,6 +179,16 @@ AddEventHandler('esx_phone:stopDispatch', function(dispatchRequestId, playerName end) +RegisterNetEvent('esx_phone:setPhoneNumberSource') +AddEventHandler('esx_phone:setPhoneNumberSource', function(phoneNumber, source) + + if source == -1 then + PhoneNumberSources[phoneNumber] = nil + else + PhoneNumberSources[phoneNumber] = source + end +end) + RegisterNUICallback('setGPS', function(data) SetNewWaypoint(data.x, data.y) ESX.ShowNotification(_U('gps_position')) @@ -185,12 +197,18 @@ end) RegisterNUICallback('send', function(data) local phoneNumber = data.number + local playerPed = GetPlayerPed(-1) + local coords = GetEntityCoords(playerPed) if tonumber(phoneNumber) ~= nil then phoneNumber = tonumber(phoneNumber) end - TriggerServerEvent('esx_phone:send', phoneNumber, data.message, data.anonyme) + TriggerServerEvent('esx_phone:send', phoneNumber, data.message, data.anonyme, { + x = coords.x, + y = coords.y, + z = coords.z + }) SendNUIMessage({ showMessageEditor = false diff --git a/html/scripts/app.js b/html/scripts/app.js index a4f64f5a..3afebe53 100644 --- a/html/scripts/app.js +++ b/html/scripts/app.js @@ -36,6 +36,7 @@ let showMain = function() { $('.screen').removeClass('active'); + $('.screen *').attr('disabled', 'disabled'); } let showRepertoire = function() { @@ -58,6 +59,8 @@ let showAddContact = function() { $('#contact').addClass('active'); + $('.screen *').attr('disabled', 'disabled'); + $('.screen.active *').removeAttr('disabled'); } let hideAddContact = function() { @@ -70,6 +73,8 @@ $('#writer').addClass('active'); $('#writer_number').val(cnum); $('#writer .header-title').html(cname); + $('.screen *').attr('disabled', 'disabled'); + $('.screen.active *').removeAttr('disabled'); } let hideNewMessage = function() { @@ -145,6 +150,7 @@ let showPhone = function(phoneData){ reloadPhone(phoneData); $('#phone').show(); + showMain(); isPhoneShowed = true; } diff --git a/html/scripts/app_de.js b/html/scripts/app_de.js index bdc0e71f..fa6bb6a7 100644 --- a/html/scripts/app_de.js +++ b/html/scripts/app_de.js @@ -1,6 +1,6 @@ (function(){ - - let ContactTpl = + + let ContactTpl = '
    ' + '
    ' + '
    ' + @@ -10,15 +10,15 @@ '
    ' + '
    ' ; - - let MessageTpl = + + let MessageTpl = '
    ' + '
    ' + '
    ' + '{{sender}}
    #{{phoneNumber}}' + - '
    ' + - '
    ' + - '
    {{message}}
    ' + + '
    ' + + '
    ' + + '
    {{message}}
    ' + '
    ' + '
    ' ; @@ -33,52 +33,57 @@ let isMessageEditorOpen = false; let isMessagesOpen = false; let isPhoneShowed = false; - + let showMain = function() { $('.screen').removeClass('active'); + $('.screen *').attr('disabled', 'disabled'); } - + let showRepertoire = function() { $('#repertoire').addClass('active'); } - + let hideRepertoire = function() { $('#repertoire').removeClass('active'); } - - let showMessages = function(){ + + let showMessages = function(){ $('#messages').addClass('active'); isMessagesOpen = true; } - let hideMessages = function(){ + let hideMessages = function(){ $('#messages').removeClass('active'); isMessagesOpen = false; } - + let showAddContact = function() { $('#contact').addClass('active'); + $('.screen *').attr('disabled', 'disabled'); + $('.screen.active *').removeAttr('disabled'); } - + let hideAddContact = function() { $('#contact').removeClass('active'); $('#contact_name').val(''); $('#contact_number').val(''); } - + let showNewMessage = function(cnum, cname) { $('#writer').addClass('active'); $('#writer_number').val(cnum); $('#writer .header-title').html(cname); + $('.screen *').attr('disabled', 'disabled'); + $('.screen.active *').removeAttr('disabled'); } - let hideNewMessage = function() { + let hideNewMessage = function() { $('#writer').removeClass('active'); $('#writer_number').val(''); $('#writer_message').val(''); $('#writer .header-title').html(''); } - + let showGPS = function(xPos, yPos) { $.post('http://esx_phone/setGPS', JSON.stringify({ x: parseFloat(xPos), @@ -88,13 +93,13 @@ } let renderContacts = function(){ - + let contactHTML = ''; - + if(contacts.length > 0) { for(let i=0; i 0) { - + for(let i=0; i' + '
    ' + @@ -10,15 +10,15 @@ '
    ' + '
    ' ; - - let MessageTpl = + + let MessageTpl = '
    ' + '
    ' + '
    ' + '{{sender}}
    #{{phoneNumber}}' + - '
    ' + - '
    ' + - '
    {{message}}
    ' + + '
    ' + + '
    ' + + '
    {{message}}
    ' + '
    ' + '
    ' ; @@ -33,52 +33,57 @@ let isMessageEditorOpen = false; let isMessagesOpen = false; let isPhoneShowed = false; - + let showMain = function() { $('.screen').removeClass('active'); + $('.screen *').attr('disabled', 'disabled'); } - + let showRepertoire = function() { $('#repertoire').addClass('active'); } - + let hideRepertoire = function() { $('#repertoire').removeClass('active'); } - - let showMessages = function(){ + + let showMessages = function(){ $('#messages').addClass('active'); isMessagesOpen = true; } - let hideMessages = function(){ + let hideMessages = function(){ $('#messages').removeClass('active'); isMessagesOpen = false; } - + let showAddContact = function() { $('#contact').addClass('active'); + $('.screen *').attr('disabled', 'disabled'); + $('.screen.active *').removeAttr('disabled'); } - + let hideAddContact = function() { $('#contact').removeClass('active'); $('#contact_name').val(''); $('#contact_number').val(''); } - + let showNewMessage = function(cnum, cname) { $('#writer').addClass('active'); $('#writer_number').val(cnum); $('#writer .header-title').html(cname); + $('.screen *').attr('disabled', 'disabled'); + $('.screen.active *').removeAttr('disabled'); } - let hideNewMessage = function() { + let hideNewMessage = function() { $('#writer').removeClass('active'); $('#writer_number').val(''); $('#writer_message').val(''); $('#writer .header-title').html(''); } - + let showGPS = function(xPos, yPos) { $.post('http://esx_phone/setGPS', JSON.stringify({ x: parseFloat(xPos), @@ -88,13 +93,13 @@ } let renderContacts = function(){ - + let contactHTML = ''; - + if(contacts.length > 0) { for(let i=0; i 0) { - + for(let i=0; i ' .. xPlayer.name .. '@' .. phoneNumber .. ' : ' .. message) - local xPlayer2 = ESX.GetPlayerFromId(xPlayers[j]) - - if xPlayer2.get('phoneNumber') == contacts[i].number then - contacts[i].online = true - end + if PhoneNumbers[phoneNumber] ~= nil then + + for i=1, #PhoneNumbers[phoneNumber].sources, 1 do + + if PhoneNumbers[phoneNumber].sources[i] ~= nil then + + local numType = PhoneNumbers[phoneNumber].type + local numHasDispatch = PhoneNumbers[phoneNumber].hasDispatch + local numHide = PhoneNumbers[phoneNumber].hideNumber + local numHidePosIfAnon = PhoneNumbers[phoneNumber].hidePosIfAnon + local numPosition = (PhoneNumbers[phoneNumber].sharePos and position or false) + local numSource = PhoneNumbers[phoneNumber].sources[i] + + if numHidePosIfAnon and anon then + numPosition = false + end + + if numHasDispatch then + TriggerClientEvent('esx_phone:onMessage', numSource, xPlayer.get('phoneNumber'), message, numPosition, (numHide and true or anon), numType, GetDistpatchRequestId()) + else + TriggerClientEvent('esx_phone:onMessage', numSource, xPlayer.get('phoneNumber'), message, numPosition, (numHide and true or anon), numType, false) end end - xPlayer.set('contacts', contacts) - - TriggerClientEvent('esx_phone:loaded', _source, phoneNumber, contacts) - end - ) -end) - -RegisterServerEvent('esx_phone:registerCallback') -AddEventHandler('esx_phone:registerCallback', function(cb) - table.insert(RegisteredCallbacks, cb) -end) - -RegisterServerEvent('esx_phone:send') -AddEventHandler('esx_phone:send', function(phoneNumber, message, anon) - - local _source = source - - for i=1, #RegisteredCallbacks, 1 do - RegisteredCallbacks[i](_source, phoneNumber, message, anon) end + +end) + +AddEventHandler('esx_phone:registerNumber', function(number, type, sharePos, hasDispatch, hideNumber, hidePosIfAnon) + + local hideNumber = hideNumber or false + local hidePosIfAnon = hidePosIfAnon or false + + PhoneNumbers[number] = { + type = type, + sharePos = sharePos, + hasDispatch = (hasDispatch or false), + hideNumber = hideNumber, + hidePosIfAnon = hidePosIfAnon, + sources = {} + } + +end) + +AddEventHandler('esx_phone:addSource', function(number, source) + + local found = false + + for i=1, #PhoneNumbers[number].sources, 1 do + if PhoneNumbers[number].sources[i] == source then + found = true + break + end + end + + if not found then + table.insert(PhoneNumbers[number].sources, source) + end + +end) + +AddEventHandler('esx_phone:removeSource', function(number, source) + + for i=1, #PhoneNumbers[number].sources, 1 do + if PhoneNumbers[number].sources[i] == source then + PhoneNumbers[number].sources[i] = nil + end + end + end) RegisterServerEvent('esx_phone:addPlayerContact') @@ -244,20 +312,7 @@ AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) TriggerClientEvent('esx:showNotification', _source, _U('contact_added')) - local xPlayers = ESX.GetPlayers() - local isOnline = false - - for i=1, #xPlayers, 1 do - - local xPlayer2 = ESX.GetPlayerFromId(xPlayers[i]) - - if xPlayer2.get('phoneNumber') == phoneNumber then - isOnline = true - break - end - end - - TriggerClientEvent('esx_phone:addContact', _source, contactName, phoneNumber, isOnline) + TriggerClientEvent('esx_phone:addContact', _source, contactName, phoneNumber) end ) @@ -273,26 +328,6 @@ AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) end) -AddEventHandler('esx_phone:ready', function() - TriggerEvent('esx_phone:registerCallback', function(source, phoneNumber, message, anon) - - local xPlayer = ESX.GetPlayerFromId(source) - local xPlayers = ESX.GetPlayers() - - print('MESSAGE => ' .. xPlayer.name .. '@' .. phoneNumber .. ' : ' .. message) - - for i=1, #xPlayers, 1 do - - local xPlayer2 = ESX.GetPlayerFromId(xPlayers[i]) - - if xPlayer2.get('phoneNumber') == phoneNumber then - TriggerClientEvent('esx_phone:onMessage', xPlayer2.source, xPlayer.get('phoneNumber'), message, false, anon, 'player', false) - end - end - - end) -end) - RegisterServerEvent('esx_phone:stopDispatch') AddEventHandler('esx_phone:stopDispatch', function(dispatchRequestId) TriggerClientEvent('esx_phone:stopDispatch', -1, dispatchRequestId, GetPlayerName(source)) From f1b94124d126835f3ea17f9f0f895aa58b77afe8 Mon Sep 17 00:00:00 2001 From: nearbyplayer Date: Sun, 3 Sep 2017 15:27:03 -0400 Subject: [PATCH 39/76] Update English UI --- html/scripts/app_en.js | 2 +- locales/en.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/html/scripts/app_en.js b/html/scripts/app_en.js index c0551140..36515331 100644 --- a/html/scripts/app_en.js +++ b/html/scripts/app_en.js @@ -237,7 +237,7 @@ $('.message .ok-btn').click(function() { $.post('http://esx_phone/send', JSON.stringify({ - message: $(this).attr('data-contact-job') + ": Received !", + message: $(this).attr('data-contact-job') + ": Received!", number : $(this).attr('data-contact-number'), anonyme: false })) diff --git a/locales/en.lua b/locales/en.lua index 83ef249f..bcf71598 100644 --- a/locales/en.lua +++ b/locales/en.lua @@ -1,6 +1,6 @@ Locales['en'] = { - ['new_message'] = '~b~New message :~s~ %s', + ['new_message'] = '~b~New message:~s~ %s', ['press_take_call'] = ' - Press ~INPUT_CONTEXT~ to take the call', ['taken_call'] = ' has taken the call', ['gps_position'] = 'destination entered into the GPS', From 89412976d2c5b2cebb04e42657568da4852df13d Mon Sep 17 00:00:00 2001 From: renaiku Date: Tue, 5 Sep 2017 17:34:33 +0200 Subject: [PATCH 40/76] Show 'job .. text' instead of 'new message .. text' if it's a job message --- client/main.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/main.lua b/client/main.lua index ed69f73c..4a16c1af 100644 --- a/client/main.lua +++ b/client/main.lua @@ -135,7 +135,11 @@ end) RegisterNetEvent('esx_phone:onMessage') AddEventHandler('esx_phone:onMessage', function(phoneNumber, message, position, anon, job, dispatchRequestId) - ESX.ShowNotification(_U('new_message', message)) + if job == 'player' then + ESX.ShowNotification(_U('new_message', message)) + else + ESX.ShowNotification('~b~' .. job .. ': ~s~' .. message) + end SendNUIMessage({ newMessage = true, From 6842206a2331499b114a6499ea4beaff5bec6954 Mon Sep 17 00:00:00 2001 From: Coro Veneno Date: Tue, 12 Sep 2017 22:44:02 +0200 Subject: [PATCH 41/76] Create es.lua Added Spanish Language Multi-Language --- locales/es.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 locales/es.lua diff --git a/locales/es.lua b/locales/es.lua new file mode 100644 index 00000000..36983dce --- /dev/null +++ b/locales/es.lua @@ -0,0 +1,12 @@ +Locales['es'] = { + + ['new_message'] = '~b~Nuevo menaje :~s~ %s', + ['press_take_call'] = ' - Presionar ~INPUT_CONTEXT~ para coger la llamada', + ['taken_call'] = ' Has contestado la llamada', + ['gps_position'] = 'Posición GPS', + ['message_sent'] = 'Mensaje enviado', + ['cannot_add_self'] = 'No has podido agregar', + ['number_in_contacts'] = 'Este número ya está en tu lista de contactos', + ['contact_added'] = 'Contacto añadido', + ['number_not_assigned'] = 'El número no se ha añadido todavía...', +} From 17fd773eea6821b29617f2169732c20179086815 Mon Sep 17 00:00:00 2001 From: Coro Veneno Date: Tue, 12 Sep 2017 22:59:14 +0200 Subject: [PATCH 42/76] Update Spanish Language --- __resource.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/__resource.lua b/__resource.lua index 5eb22590..4bf24211 100644 --- a/__resource.lua +++ b/__resource.lua @@ -10,6 +10,7 @@ server_scripts { 'locales/br.lua', 'locales/en.lua', 'locales/fr.lua', + 'locales/es.lua', 'server/main.lua' } @@ -20,6 +21,7 @@ client_scripts { 'locales/br.lua', 'locales/en.lua', 'locales/fr.lua', + 'locales/es.lua', 'client/main.lua' } From 8faec4d095a1b789abd4d492763d94e864edaf91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Wed, 13 Sep 2017 14:52:49 +0200 Subject: [PATCH 43/76] Try fix messages not sending + add phone animation --- client/main.lua | 14 +++++++++-- server/main.lua | 63 ++++++++++++++++--------------------------------- 2 files changed, 32 insertions(+), 45 deletions(-) diff --git a/client/main.lua b/client/main.lua index 4a16c1af..c3273440 100644 --- a/client/main.lua +++ b/client/main.lua @@ -38,6 +38,8 @@ end) function OpenPhone() + local playerPed = GetPlayerPed(-1) + TriggerServerEvent('esx_phone:reload', PhoneData.phoneNumber) SendNUIMessage({ @@ -51,10 +53,16 @@ function OpenPhone() SetNuiFocus(true, true) end) + if not IsPedInAnyVehicle(playerPed, false) then + TaskStartScenarioInPlace(playerPed, "WORLD_HUMAN_STAND_MOBILE", 0, true); + end + end function ClosePhone() + local playerPed = GetPlayerPed(-1) + SendNUIMessage({ showPhone = false }) @@ -63,6 +71,8 @@ function ClosePhone() GUI.PhoneIsShowed = false + ClearPedTasks(playerPed) + end RegisterNetEvent('esx_phone:loaded') @@ -136,9 +146,9 @@ RegisterNetEvent('esx_phone:onMessage') AddEventHandler('esx_phone:onMessage', function(phoneNumber, message, position, anon, job, dispatchRequestId) if job == 'player' then - ESX.ShowNotification(_U('new_message', message)) + ESX.ShowNotification(_U('new_message', message)) else - ESX.ShowNotification('~b~' .. job .. ': ~s~' .. message) + ESX.ShowNotification('~b~' .. job .. ': ~s~' .. message) end SendNUIMessage({ diff --git a/server/main.lua b/server/main.lua index 36e62320..f1dcdf72 100644 --- a/server/main.lua +++ b/server/main.lua @@ -61,10 +61,10 @@ AddEventHandler('esx:playerLoaded', function(source) local xPlayer = ESX.GetPlayerFromId(source) - for k,v in pairs(PhoneNumbers) do + for num,v in pairs(PhoneNumbers) do if tonumber(k) == k then -- If phonenumber is a player phone number - for i=1, #v.sources, 1 do - TriggerClientEvent('esx_phone:setPhoneNumberSource', source, k, v.sources[i]) + for src,_ in pairs(v.sources) do + TriggerClientEvent('esx_phone:setPhoneNumberSource', source, num, tonumber(src)) end end end @@ -99,7 +99,7 @@ AddEventHandler('esx:playerLoaded', function(source) sharePos = false, hideNumber = false, hidePosIfAnon = false, - sources = {source} + sources = {[source] = true} } xPlayer.set('phoneNumber', phoneNumber) @@ -184,27 +184,23 @@ AddEventHandler('esx_phone:send', function(phoneNumber, message, anon, position) if PhoneNumbers[phoneNumber] ~= nil then - for i=1, #PhoneNumbers[phoneNumber].sources, 1 do + for k,v in pairs(PhoneNumbers[phoneNumber].sources) do - if PhoneNumbers[phoneNumber].sources[i] ~= nil then + local numType = PhoneNumbers[phoneNumber].type + local numHasDispatch = PhoneNumbers[phoneNumber].hasDispatch + local numHide = PhoneNumbers[phoneNumber].hideNumber + local numHidePosIfAnon = PhoneNumbers[phoneNumber].hidePosIfAnon + local numPosition = (PhoneNumbers[phoneNumber].sharePos and position or false) + local numSource = tonumber(k) - local numType = PhoneNumbers[phoneNumber].type - local numHasDispatch = PhoneNumbers[phoneNumber].hasDispatch - local numHide = PhoneNumbers[phoneNumber].hideNumber - local numHidePosIfAnon = PhoneNumbers[phoneNumber].hidePosIfAnon - local numPosition = (PhoneNumbers[phoneNumber].sharePos and position or false) - local numSource = PhoneNumbers[phoneNumber].sources[i] - - if numHidePosIfAnon and anon then - numPosition = false - end - - if numHasDispatch then - TriggerClientEvent('esx_phone:onMessage', numSource, xPlayer.get('phoneNumber'), message, numPosition, (numHide and true or anon), numType, GetDistpatchRequestId()) - else - TriggerClientEvent('esx_phone:onMessage', numSource, xPlayer.get('phoneNumber'), message, numPosition, (numHide and true or anon), numType, false) - end + if numHidePosIfAnon and anon then + numPosition = false + end + if numHasDispatch then + TriggerClientEvent('esx_phone:onMessage', numSource, xPlayer.get('phoneNumber'), message, numPosition, (numHide and true or anon), numType, GetDistpatchRequestId()) + else + TriggerClientEvent('esx_phone:onMessage', numSource, xPlayer.get('phoneNumber'), message, numPosition, (numHide and true or anon), numType, false) end end @@ -230,30 +226,11 @@ AddEventHandler('esx_phone:registerNumber', function(number, type, sharePos, has end) AddEventHandler('esx_phone:addSource', function(number, source) - - local found = false - - for i=1, #PhoneNumbers[number].sources, 1 do - if PhoneNumbers[number].sources[i] == source then - found = true - break - end - end - - if not found then - table.insert(PhoneNumbers[number].sources, source) - end - + PhoneNumbers[number].sources[tostring(source)] = true end) AddEventHandler('esx_phone:removeSource', function(number, source) - - for i=1, #PhoneNumbers[number].sources, 1 do - if PhoneNumbers[number].sources[i] == source then - PhoneNumbers[number].sources[i] = nil - end - end - + PhoneNumbers[number].sources[tostring(source)] = nil end) RegisterServerEvent('esx_phone:addPlayerContact') From 3caf7f8282dad0b461f41301c5e2cb523efae77c Mon Sep 17 00:00:00 2001 From: Romain Lanz Date: Wed, 13 Sep 2017 21:20:37 +0200 Subject: [PATCH 44/76] fix(server): fix variable name --- server/main.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/main.lua b/server/main.lua index f1dcdf72..89aa7a01 100644 --- a/server/main.lua +++ b/server/main.lua @@ -62,7 +62,7 @@ AddEventHandler('esx:playerLoaded', function(source) local xPlayer = ESX.GetPlayerFromId(source) for num,v in pairs(PhoneNumbers) do - if tonumber(k) == k then -- If phonenumber is a player phone number + if tonumber(num) == num then -- If phonenumber is a player phone number for src,_ in pairs(v.sources) do TriggerClientEvent('esx_phone:setPhoneNumberSource', source, num, tonumber(src)) end From adf225574c8e2df7f9ce2697fa332b377f393527 Mon Sep 17 00:00:00 2001 From: Romain Lanz Date: Thu, 14 Sep 2017 16:36:56 +0200 Subject: [PATCH 45/76] chore(coding-style): correct indentation --- .editorconfig | 9 ++ __resource.lua | 74 +++++----- client/main.lua | 283 ++++++++++++++++++------------------ esx_phone.sql | 2 +- locales/br.lua | 20 ++- locales/de.lua | 20 ++- locales/en.lua | 20 ++- locales/es.lua | 19 ++- locales/fr.lua | 20 ++- server/main.lua | 380 ++++++++++++++++++++++++------------------------ 10 files changed, 424 insertions(+), 423 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..41c2aaf2 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +indent_size = 2 +indent_style = space +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true \ No newline at end of file diff --git a/__resource.lua b/__resource.lua index 4bf24211..4456627f 100644 --- a/__resource.lua +++ b/__resource.lua @@ -3,49 +3,49 @@ resource_manifest_version '44febabe-d386-4d18-afbe-5e627f4af937' description 'ESX Phone' server_scripts { - '@mysql-async/lib/MySQL.lua', - 'config.lua', - '@es_extended/locale.lua', - 'locales/de.lua', - 'locales/br.lua', - 'locales/en.lua', - 'locales/fr.lua', - 'locales/es.lua', - 'server/main.lua' + '@mysql-async/lib/MySQL.lua', + 'config.lua', + '@es_extended/locale.lua', + 'locales/de.lua', + 'locales/br.lua', + 'locales/en.lua', + 'locales/fr.lua', + 'locales/es.lua', + 'server/main.lua' } client_scripts { - '@es_extended/locale.lua', - 'config.lua', - 'locales/de.lua', - 'locales/br.lua', - 'locales/en.lua', - 'locales/fr.lua', - 'locales/es.lua', - 'client/main.lua' + '@es_extended/locale.lua', + 'config.lua', + 'locales/de.lua', + 'locales/br.lua', + 'locales/en.lua', + 'locales/fr.lua', + 'locales/es.lua', + 'client/main.lua' } ui_page 'html/ui.html' files { - 'html/ui.html', - 'html/bankgothic.ttf', - 'html/pdown.ttf', - 'html/css/app.css', - 'html/scripts/mustache.min.js', - 'html/scripts/app.js', - 'html/img/cursor.png', - 'html/img/keys/enter.png', - 'html/img/keys/return.png', - 'html/img/phone.png', - 'html/img/icons/signal.png', - 'html/img/icons/rep.png', - 'html/img/icons/msg.png', - 'html/img/icons/add.png', - 'html/img/icons/back.png', - 'html/img/icons/new-msg.png', - 'html/img/icons/reply.png', - 'html/img/icons/write.png', - 'html/img/icons/edit.png', - 'html/img/icons/location.png' + 'html/ui.html', + 'html/bankgothic.ttf', + 'html/pdown.ttf', + 'html/css/app.css', + 'html/scripts/mustache.min.js', + 'html/scripts/app.js', + 'html/img/cursor.png', + 'html/img/keys/enter.png', + 'html/img/keys/return.png', + 'html/img/phone.png', + 'html/img/icons/signal.png', + 'html/img/icons/rep.png', + 'html/img/icons/msg.png', + 'html/img/icons/add.png', + 'html/img/icons/back.png', + 'html/img/icons/new-msg.png', + 'html/img/icons/reply.png', + 'html/img/icons/write.png', + 'html/img/icons/edit.png', + 'html/img/icons/location.png' } diff --git a/client/main.lua b/client/main.lua index c3273440..5348f408 100644 --- a/client/main.lua +++ b/client/main.lua @@ -1,22 +1,17 @@ local Keys = { - ["ESC"] = 322, ["F1"] = 288, ["F2"] = 289, ["F3"] = 170, ["F5"] = 166, ["F6"] = 167, ["F7"] = 168, ["F8"] = 169, ["F9"] = 56, ["F10"] = 57, - ["~"] = 243, ["1"] = 157, ["2"] = 158, ["3"] = 160, ["4"] = 164, ["5"] = 165, ["6"] = 159, ["7"] = 161, ["8"] = 162, ["9"] = 163, ["-"] = 84, ["="] = 83, ["BACKSPACE"] = 177, - ["TAB"] = 37, ["Q"] = 44, ["W"] = 32, ["E"] = 38, ["R"] = 45, ["T"] = 245, ["Y"] = 246, ["U"] = 303, ["P"] = 199, ["["] = 39, ["]"] = 40, ["ENTER"] = 18, - ["CAPS"] = 137, ["A"] = 34, ["S"] = 8, ["D"] = 9, ["F"] = 23, ["G"] = 47, ["H"] = 74, ["K"] = 311, ["L"] = 182, - ["LEFTSHIFT"] = 21, ["Z"] = 20, ["X"] = 73, ["C"] = 26, ["V"] = 0, ["B"] = 29, ["N"] = 249, ["M"] = 244, [","] = 82, ["."] = 81, - ["LEFTCTRL"] = 36, ["LEFTALT"] = 19, ["SPACE"] = 22, ["RIGHTCTRL"] = 70, - ["HOME"] = 213, ["PAGEUP"] = 10, ["PAGEDOWN"] = 11, ["DELETE"] = 178, - ["LEFT"] = 174, ["RIGHT"] = 175, ["TOP"] = 27, ["DOWN"] = 173, - ["NENTER"] = 201, ["N4"] = 108, ["N5"] = 60, ["N6"] = 107, ["N+"] = 96, ["N-"] = 97, ["N7"] = 117, ["N8"] = 61, ["N9"] = 118 + ["ESC"] = 322, ["F1"] = 288, ["F2"] = 289, ["F3"] = 170, ["F5"] = 166, ["F6"] = 167, ["F7"] = 168, ["F8"] = 169, ["F9"] = 56, ["F10"] = 57, + ["~"] = 243, ["1"] = 157, ["2"] = 158, ["3"] = 160, ["4"] = 164, ["5"] = 165, ["6"] = 159, ["7"] = 161, ["8"] = 162, ["9"] = 163, ["-"] = 84, ["="] = 83, ["BACKSPACE"] = 177, + ["TAB"] = 37, ["Q"] = 44, ["W"] = 32, ["E"] = 38, ["R"] = 45, ["T"] = 245, ["Y"] = 246, ["U"] = 303, ["P"] = 199, ["["] = 39, ["]"] = 40, ["ENTER"] = 18, + ["CAPS"] = 137, ["A"] = 34, ["S"] = 8, ["D"] = 9, ["F"] = 23, ["G"] = 47, ["H"] = 74, ["K"] = 311, ["L"] = 182, + ["LEFTSHIFT"] = 21, ["Z"] = 20, ["X"] = 73, ["C"] = 26, ["V"] = 0, ["B"] = 29, ["N"] = 249, ["M"] = 244, [","] = 82, ["."] = 81, + ["LEFTCTRL"] = 36, ["LEFTALT"] = 19, ["SPACE"] = 22, ["RIGHTCTRL"] = 70, + ["HOME"] = 213, ["PAGEUP"] = 10, ["PAGEDOWN"] = 11, ["DELETE"] = 178, + ["LEFT"] = 174, ["RIGHT"] = 175, ["TOP"] = 27, ["DOWN"] = 173, + ["NENTER"] = 201, ["N4"] = 108, ["N5"] = 60, ["N6"] = 107, ["N+"] = 96, ["N-"] = 97, ["N7"] = 117, ["N8"] = 61, ["N9"] = 118 } -ESX = nil local GUI = {} -GUI.Time = 0 -GUI.PhoneIsShowed = false -GUI.MessagesIsShowed = false -GUI.AddContactIsShowed = false -local PhoneData = {phoneNumber = 0, contacts = {}} +local PhoneData = { phoneNumber = 0, contacts = {} } local RegisteredMessageCallbacks = {} local ContactJustAdded = false local CurrentAction = nil @@ -25,22 +20,28 @@ local CurrentActionData = {} local CurrentDispatchRequestId = -1 local PhoneNumberSources = {} +ESX = nil +GUI.Time = 0 +GUI.PhoneIsShowed = false +GUI.MessagesIsShowed = false +GUI.AddContactIsShowed = false + Citizen.CreateThread(function() - while ESX == nil do - TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end) - Citizen.Wait(0) - end + while ESX == nil do + TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end) + Citizen.Wait(0) + end - ESX.UI.Menu.RegisterType('phone', OpenPhone, ClosePhone) + ESX.UI.Menu.RegisterType('phone', OpenPhone, ClosePhone) end) function OpenPhone() - local playerPed = GetPlayerPed(-1) + local playerPed = GetPlayerPed(-1) - TriggerServerEvent('esx_phone:reload', PhoneData.phoneNumber) + TriggerServerEvent('esx_phone:reload', PhoneData.phoneNumber) SendNUIMessage({ showPhone = true, @@ -50,94 +51,94 @@ function OpenPhone() GUI.PhoneIsShowed = true ESX.SetTimeout(250, function() - SetNuiFocus(true, true) + SetNuiFocus(true, true) end) - if not IsPedInAnyVehicle(playerPed, false) then - TaskStartScenarioInPlace(playerPed, "WORLD_HUMAN_STAND_MOBILE", 0, true); - end + if not IsPedInAnyVehicle(playerPed, false) then + TaskStartScenarioInPlace(playerPed, "WORLD_HUMAN_STAND_MOBILE", 0, true); + end end function ClosePhone() - local playerPed = GetPlayerPed(-1) + local playerPed = GetPlayerPed(-1) SendNUIMessage({ - showPhone = false + showPhone = false }) SetNuiFocus(false) - GUI.PhoneIsShowed = false + GUI.PhoneIsShowed = false - ClearPedTasks(playerPed) + ClearPedTasks(playerPed) end RegisterNetEvent('esx_phone:loaded') AddEventHandler('esx_phone:loaded', function(phoneNumber, contacts) - PhoneData.phoneNumber = phoneNumber - PhoneData.contacts = {} + PhoneData.phoneNumber = phoneNumber + PhoneData.contacts = {} - for i=1, #contacts, 1 do - contacts[i].online = (PhoneNumberSources[contacts[i].number] == nil and false or NetworkIsPlayerActive(GetPlayerFromServerId(PhoneNumberSources[contacts[i].number]))), - table.insert(PhoneData.contacts, contacts[i]) - end + for i=1, #contacts, 1 do + contacts[i].online = (PhoneNumberSources[contacts[i].number] == nil and false or NetworkIsPlayerActive(GetPlayerFromServerId(PhoneNumberSources[contacts[i].number]))), + table.insert(PhoneData.contacts, contacts[i]) + end - SendNUIMessage({ - reloadPhone = true, - phoneData = PhoneData - }) + SendNUIMessage({ + reloadPhone = true, + phoneData = PhoneData + }) end) RegisterNetEvent('esx_phone:addContact') AddEventHandler('esx_phone:addContact', function(name, phoneNumber) - table.insert(PhoneData.contacts, { - name = name, - number = phoneNumber, - online = (PhoneNumberSources[contacts[i].number] == nil and false or NetworkIsPlayerActive(GetPlayerFromServerId(PhoneNumberSources[contacts[i].number]))), - }) - -- CALL HERE RELOADCONTACT - SendNUIMessage({ - contactAdded = true, - phoneData = PhoneData - }) + table.insert(PhoneData.contacts, { + name = name, + number = phoneNumber, + online = (PhoneNumberSources[contacts[i].number] == nil and false or NetworkIsPlayerActive(GetPlayerFromServerId(PhoneNumberSources[contacts[i].number]))), + }) + -- CALL HERE RELOADCONTACT + SendNUIMessage({ + contactAdded = true, + phoneData = PhoneData + }) end) RegisterNetEvent('esx_phone:addSpecialContact') AddEventHandler('esx_phone:addSpecialContact', function(name, phoneNumber, base64Icon) - SendNUIMessage({ - addSpecialContact = true, - name = name, - number = phoneNumber, - base64Icon = base64Icon - }) + SendNUIMessage({ + addSpecialContact = true, + name = name, + number = phoneNumber, + base64Icon = base64Icon + }) end) RegisterNetEvent('esx_phone:removeSpecialContact') AddEventHandler('esx_phone:removeSpecialContact', function(phoneNumber) - SendNUIMessage({ - removeSpecialContact = true, - number = phoneNumber - }) + SendNUIMessage({ + removeSpecialContact = true, + number = phoneNumber + }) end) RegisterNUICallback('add_contact', function(data, cb) - local phoneNumber = tonumber(data.phoneNumber) + local phoneNumber = tonumber(data.phoneNumber) local contactName = tostring(data.contactName) - if phoneNumber then - TriggerServerEvent('esx_phone:addPlayerContact', phoneNumber, contactName) - end + if phoneNumber then + TriggerServerEvent('esx_phone:addPlayerContact', phoneNumber, contactName) + end end) @@ -145,107 +146,107 @@ end) RegisterNetEvent('esx_phone:onMessage') AddEventHandler('esx_phone:onMessage', function(phoneNumber, message, position, anon, job, dispatchRequestId) - if job == 'player' then - ESX.ShowNotification(_U('new_message', message)) - else - ESX.ShowNotification('~b~' .. job .. ': ~s~' .. message) - end + if job == 'player' then + ESX.ShowNotification(_U('new_message', message)) + else + ESX.ShowNotification('~b~' .. job .. ': ~s~' .. message) + end - SendNUIMessage({ - newMessage = true, - phoneNumber = phoneNumber, - message = message, - position = position, - anonyme = anon, - job = job - }) + SendNUIMessage({ + newMessage = true, + phoneNumber = phoneNumber, + message = message, + position = position, + anonyme = anon, + job = job + }) - if dispatchRequestId then + if dispatchRequestId then - CurrentAction = 'dispatch' - CurrentActionMsg = job .. _U('press_take_call') - CurrentDispatchRequestId = dispatchRequestId + CurrentAction = 'dispatch' + CurrentActionMsg = job .. _U('press_take_call') + CurrentDispatchRequestId = dispatchRequestId - CurrentActionData = { - phoneNumber = phoneNumber, - message = message, - position = position, - actions = actions, - anonyme = anon, - job = job - } + CurrentActionData = { + phoneNumber = phoneNumber, + message = message, + position = position, + actions = actions, + anonyme = anon, + job = job + } - ESX.SetTimeout(15000, function() - CurrentAction = nil - end) + ESX.SetTimeout(15000, function() + CurrentAction = nil + end) - end + end end) RegisterNetEvent('esx_phone:stopDispatch') AddEventHandler('esx_phone:stopDispatch', function(dispatchRequestId, playerName) - if CurrentDispatchRequestId == dispatchRequestId and CurrentAction == 'dispatch' then - CurrentAction = nil - ESX.ShowNotification(playerName .. _U('taken_call')) - end + if CurrentDispatchRequestId == dispatchRequestId and CurrentAction == 'dispatch' then + CurrentAction = nil + ESX.ShowNotification(playerName .. _U('taken_call')) + end end) RegisterNetEvent('esx_phone:setPhoneNumberSource') AddEventHandler('esx_phone:setPhoneNumberSource', function(phoneNumber, source) - if source == -1 then - PhoneNumberSources[phoneNumber] = nil - else - PhoneNumberSources[phoneNumber] = source - end + if source == -1 then + PhoneNumberSources[phoneNumber] = nil + else + PhoneNumberSources[phoneNumber] = source + end end) RegisterNUICallback('setGPS', function(data) - SetNewWaypoint(data.x, data.y) - ESX.ShowNotification(_U('gps_position')) + SetNewWaypoint(data.x, data.y) + ESX.ShowNotification(_U('gps_position')) end) RegisterNUICallback('send', function(data) - local phoneNumber = data.number - local playerPed = GetPlayerPed(-1) - local coords = GetEntityCoords(playerPed) + local phoneNumber = data.number + local playerPed = GetPlayerPed(-1) + local coords = GetEntityCoords(playerPed) - if tonumber(phoneNumber) ~= nil then - phoneNumber = tonumber(phoneNumber) - end + if tonumber(phoneNumber) ~= nil then + phoneNumber = tonumber(phoneNumber) + end - TriggerServerEvent('esx_phone:send', phoneNumber, data.message, data.anonyme, { - x = coords.x, - y = coords.y, - z = coords.z - }) + TriggerServerEvent('esx_phone:send', phoneNumber, data.message, data.anonyme, { + x = coords.x, + y = coords.y, + z = coords.z + }) - SendNUIMessage({ - showMessageEditor = false - }) + SendNUIMessage({ + showMessageEditor = false + }) - ESX.ShowNotification(_U('message_sent')) + ESX.ShowNotification(_U('message_sent')) end) RegisterNUICallback('escape', function() - ESX.UI.Menu.Close('phone', GetCurrentResourceName(), 'main') + ESX.UI.Menu.Close('phone', GetCurrentResourceName(), 'main') end) Citizen.CreateThread(function() - while true do + while true do - Wait(0) + Wait(0) if GUI.PhoneIsShowed then -- codes here: https://pastebin.com/guYd0ht4 DisableControlAction(0, 1, true) -- LookLeftRight DisableControlAction(0, 2, true) -- LookUpDown DisableControlAction(0, 25, true) -- Input Aim - DisableControlAction(0, 106, true) -- Vehicle Mouse Control Override + DisableControlAction(0, 106, true) -- Vehicle Mouse Control Override DisableControlAction(0, 24, true) -- Input Attack DisableControlAction(0, 140, true) -- Melee Attack Alternate @@ -265,8 +266,8 @@ Citizen.CreateThread(function() if IsControlPressed(0, Keys['F1']) and (GetGameTimer() - GUI.Time) > 150 then if not ESX.UI.Menu.IsOpen('phone', GetCurrentResourceName(), 'main') then - ESX.UI.Menu.CloseAll() - ESX.UI.Menu.Open('phone', GetCurrentResourceName(), 'main') + ESX.UI.Menu.CloseAll() + ESX.UI.Menu.Open('phone', GetCurrentResourceName(), 'main') end GUI.Time = GetGameTimer() @@ -279,29 +280,29 @@ end) -- Key controls Citizen.CreateThread(function() - while true do + while true do - Citizen.Wait(0) + Citizen.Wait(0) - if CurrentAction ~= nil then + if CurrentAction ~= nil then - SetTextComponentFormat('STRING') - AddTextComponentString(CurrentActionMsg) - DisplayHelpTextFromStringLabel(0, 0, 1, -1) + SetTextComponentFormat('STRING') + AddTextComponentString(CurrentActionMsg) + DisplayHelpTextFromStringLabel(0, 0, 1, -1) - if IsControlPressed(0, Keys['E']) and (GetGameTimer() - GUI.Time) > 300 then + if IsControlPressed(0, Keys['E']) and (GetGameTimer() - GUI.Time) > 300 then - if CurrentAction == 'dispatch' then - TriggerServerEvent('esx_phone:stopDispatch', CurrentDispatchRequestId) - SetNewWaypoint(CurrentActionData.position.x, CurrentActionData.position.y) - end + if CurrentAction == 'dispatch' then + TriggerServerEvent('esx_phone:stopDispatch', CurrentDispatchRequestId) + SetNewWaypoint(CurrentActionData.position.x, CurrentActionData.position.y) + end - CurrentAction = nil - GUI.Time = GetGameTimer() + CurrentAction = nil + GUI.Time = GetGameTimer() - end + end - end + end - end + end end) diff --git a/esx_phone.sql b/esx_phone.sql index 521f074a..e9069869 100644 --- a/esx_phone.sql +++ b/esx_phone.sql @@ -6,5 +6,5 @@ CREATE TABLE user_contacts ( PRIMARY KEY (id) ); -ALTER TABLE `users` +ALTER TABLE `users` ADD COLUMN `phone_number` INT NULL; diff --git a/locales/br.lua b/locales/br.lua index 81698bb7..600b3848 100644 --- a/locales/br.lua +++ b/locales/br.lua @@ -1,13 +1,11 @@ Locales['br'] = { - - ['new_message'] = '~b~Nova mensagem :~s~ %s', - ['press_take_call'] = ' - Pressione ~INPUT_CONTEXT~ para aceitar a chamada', - ['taken_call'] = ' recebeu uma chamada', - ['gps_position'] = 'Destino inserido no GPS', - ['message_sent'] = 'mensagem enviada', - ['cannot_add_self'] = 'Você não pode se adicionar', - ['number_in_contacts'] = 'Este número já está na sua lista de contatos', - ['contact_added'] = 'Contato adicionado', - ['number_not_assigned'] = 'Este número não foi atribuído...', - + ['new_message'] = '~b~Nova mensagem :~s~ %s', + ['press_take_call'] = ' - Pressione ~INPUT_CONTEXT~ para aceitar a chamada', + ['taken_call'] = ' recebeu uma chamada', + ['gps_position'] = 'Destino inserido no GPS', + ['message_sent'] = 'mensagem enviada', + ['cannot_add_self'] = 'Você não pode se adicionar', + ['number_in_contacts'] = 'Este número já está na sua lista de contatos', + ['contact_added'] = 'Contato adicionado', + ['number_not_assigned'] = 'Este número não foi atribuído...', } diff --git a/locales/de.lua b/locales/de.lua index e69dae2e..6216e74b 100644 --- a/locales/de.lua +++ b/locales/de.lua @@ -1,13 +1,11 @@ Locales['de'] = { - - ['new_message'] = '~b~Neue Nachricht', - ['press_take_call'] = ' - Drücke ~INPUT_CONTEXT~ um den Anruf anzunehmen', - ['taken_call'] = ' hat den Anruf angenommen', - ['gps_position'] = 'Ziel in GPS eingegeben', - ['message_sent'] = 'nachricht gesendet', - ['cannot_add_self'] = 'du kannst dich nicht selbst hinzufügen', - ['number_in_contacts'] = 'diese Nummer ist bereits in deinen Kontakten', - ['contact_added'] = 'Kontakt hinzugefügt', - ['number_not_assigned'] = 'diese Nummer ist nicht vergeben...', - + ['new_message'] = '~b~Neue Nachricht', + ['press_take_call'] = ' - Drücke ~INPUT_CONTEXT~ um den Anruf anzunehmen', + ['taken_call'] = ' hat den Anruf angenommen', + ['gps_position'] = 'Ziel in GPS eingegeben', + ['message_sent'] = 'nachricht gesendet', + ['cannot_add_self'] = 'du kannst dich nicht selbst hinzufügen', + ['number_in_contacts'] = 'diese Nummer ist bereits in deinen Kontakten', + ['contact_added'] = 'Kontakt hinzugefügt', + ['number_not_assigned'] = 'diese Nummer ist nicht vergeben...', } diff --git a/locales/en.lua b/locales/en.lua index bcf71598..4edcb040 100644 --- a/locales/en.lua +++ b/locales/en.lua @@ -1,13 +1,11 @@ Locales['en'] = { - - ['new_message'] = '~b~New message:~s~ %s', - ['press_take_call'] = ' - Press ~INPUT_CONTEXT~ to take the call', - ['taken_call'] = ' has taken the call', - ['gps_position'] = 'destination entered into the GPS', - ['message_sent'] = 'message sent', - ['cannot_add_self'] = 'you can not add yourself', - ['number_in_contacts'] = 'this number is alraedy in your contact list', - ['contact_added'] = 'contact added', - ['number_not_assigned'] = 'this number is not assigned...', - + ['new_message'] = '~b~New message:~s~ %s', + ['press_take_call'] = ' - Press ~INPUT_CONTEXT~ to take the call', + ['taken_call'] = ' has taken the call', + ['gps_position'] = 'destination entered into the GPS', + ['message_sent'] = 'message sent', + ['cannot_add_self'] = 'you can not add yourself', + ['number_in_contacts'] = 'this number is alraedy in your contact list', + ['contact_added'] = 'contact added', + ['number_not_assigned'] = 'this number is not assigned...', } diff --git a/locales/es.lua b/locales/es.lua index 36983dce..68460324 100644 --- a/locales/es.lua +++ b/locales/es.lua @@ -1,12 +1,11 @@ Locales['es'] = { - - ['new_message'] = '~b~Nuevo menaje :~s~ %s', - ['press_take_call'] = ' - Presionar ~INPUT_CONTEXT~ para coger la llamada', - ['taken_call'] = ' Has contestado la llamada', - ['gps_position'] = 'Posición GPS', - ['message_sent'] = 'Mensaje enviado', - ['cannot_add_self'] = 'No has podido agregar', - ['number_in_contacts'] = 'Este número ya está en tu lista de contactos', - ['contact_added'] = 'Contacto añadido', - ['number_not_assigned'] = 'El número no se ha añadido todavía...', + ['new_message'] = '~b~Nuevo menaje :~s~ %s', + ['press_take_call'] = ' - Presionar ~INPUT_CONTEXT~ para coger la llamada', + ['taken_call'] = ' Has contestado la llamada', + ['gps_position'] = 'Posición GPS', + ['message_sent'] = 'Mensaje enviado', + ['cannot_add_self'] = 'No has podido agregar', + ['number_in_contacts'] = 'Este número ya está en tu lista de contactos', + ['contact_added'] = 'Contacto añadido', + ['number_not_assigned'] = 'El número no se ha añadido todavía...', } diff --git a/locales/fr.lua b/locales/fr.lua index 4b6c8159..bba0228e 100644 --- a/locales/fr.lua +++ b/locales/fr.lua @@ -1,13 +1,11 @@ Locales['fr'] = { - - ['new_message'] = '~b~Nouveau message :~s~ %s', - ['press_take_call'] = ' - Appuyez sur ~INPUT_CONTEXT~ pour prendre l\'appel', - ['taken_call'] = ' a pris l\'appel', - ['gps_position'] = 'position entrée dans le GPS', - ['message_sent'] = 'message envoyé', - ['cannot_add_self'] = 'vous ne pouvez pas vous ajouter vous-même', - ['number_in_contacts'] = 'ce numéro est déja dans votre liste de contacts', - ['contact_added'] = 'contact ajouté', - ['number_not_assigned'] = 'ce numéro n\'est pas attribué...', - + ['new_message'] = '~b~Nouveau message :~s~ %s', + ['press_take_call'] = ' - Appuyez sur ~INPUT_CONTEXT~ pour prendre l\'appel', + ['taken_call'] = ' a pris l\'appel', + ['gps_position'] = 'position entrée dans le GPS', + ['message_sent'] = 'message envoyé', + ['cannot_add_self'] = 'vous ne pouvez pas vous ajouter vous-même', + ['number_in_contacts'] = 'ce numéro est déja dans votre liste de contacts', + ['contact_added'] = 'contact ajouté', + ['number_not_assigned'] = 'ce numéro n\'est pas attribué...', } diff --git a/server/main.lua b/server/main.lua index 89aa7a01..7cca9d59 100644 --- a/server/main.lua +++ b/server/main.lua @@ -3,309 +3,309 @@ local DisptachRequestId = 0 local PhoneNumbers = {} TriggerEvent('esx:getSharedObject', function(obj) - ESX = obj + ESX = obj end) function GenerateUniquePhoneNumber() - local foundNumber = false - local phoneNumber = nil + local foundNumber = false + local phoneNumber = nil - while not foundNumber do + while not foundNumber do - phoneNumber = math.random(10000, 99999) + phoneNumber = math.random(10000, 99999) - local result = MySQL.Sync.fetchAll( - 'SELECT COUNT(*) as count FROM users WHERE phone_number = @phoneNumber', - { - ['@phoneNumber'] = phoneNumber - } - ) + local result = MySQL.Sync.fetchAll( + 'SELECT COUNT(*) as count FROM users WHERE phone_number = @phoneNumber', + { + ['@phoneNumber'] = phoneNumber + } + ) - local count = tonumber(result[1].count) + local count = tonumber(result[1].count) - if count == 0 then - foundNumber = true - end + if count == 0 then + foundNumber = true + end - end + end - return phoneNumber + return phoneNumber end function GetDistpatchRequestId() - local requestId = DisptachRequestId + local requestId = DisptachRequestId - if DisptachRequestId < 65535 then - DisptachRequestId = DisptachRequestId + 1 - else - DisptachRequestId = 0 - end + if DisptachRequestId < 65535 then + DisptachRequestId = DisptachRequestId + 1 + else + DisptachRequestId = 0 + end - return requestId + return requestId end AddEventHandler('esx_phone:getDistpatchRequestId', function(cb) - cb(GetDistpatchRequestId()) + cb(GetDistpatchRequestId()) end) AddEventHandler('onResourceStart', function(ressource) - if ressource == 'esx_phone' then - TriggerEvent('esx_phone:ready') - end + if ressource == 'esx_phone' then + TriggerEvent('esx_phone:ready') + end end) AddEventHandler('esx:playerLoaded', function(source) - local xPlayer = ESX.GetPlayerFromId(source) + local xPlayer = ESX.GetPlayerFromId(source) - for num,v in pairs(PhoneNumbers) do - if tonumber(num) == num then -- If phonenumber is a player phone number - for src,_ in pairs(v.sources) do - TriggerClientEvent('esx_phone:setPhoneNumberSource', source, num, tonumber(src)) - end - end - end + for num,v in pairs(PhoneNumbers) do + if tonumber(num) == num then -- If phonenumber is a player phone number + for src,_ in pairs(v.sources) do + TriggerClientEvent('esx_phone:setPhoneNumberSource', source, num, tonumber(src)) + end + end + end - MySQL.Async.fetchAll( - 'SELECT * FROM users WHERE identifier = @identifier', - { - ['@identifier'] = xPlayer.identifier - }, - function(result) + MySQL.Async.fetchAll( + 'SELECT * FROM users WHERE identifier = @identifier', + { + ['@identifier'] = xPlayer.identifier + }, + function(result) - local phoneNumber = result[1].phone_number + local phoneNumber = result[1].phone_number - if phoneNumber == nil then + if phoneNumber == nil then - phoneNumber = GenerateUniquePhoneNumber() + phoneNumber = GenerateUniquePhoneNumber() - MySQL.Async.execute( - 'UPDATE users SET phone_number = @phone_number WHERE identifier = @identifier', - { - ['@identifier'] = xPlayer.identifier, - ['@phone_number'] = phoneNumber - } - ) - end + MySQL.Async.execute( + 'UPDATE users SET phone_number = @phone_number WHERE identifier = @identifier', + { + ['@identifier'] = xPlayer.identifier, + ['@phone_number'] = phoneNumber + } + ) + end - TriggerClientEvent('esx_phone:setPhoneNumberSource', -1, phoneNumber, source) + TriggerClientEvent('esx_phone:setPhoneNumberSource', -1, phoneNumber, source) - PhoneNumbers[phoneNumber] = { - type = 'player', - hashDispatch = false, - sharePos = false, - hideNumber = false, - hidePosIfAnon = false, - sources = {[source] = true} - } + PhoneNumbers[phoneNumber] = { + type = 'player', + hashDispatch = false, + sharePos = false, + hideNumber = false, + hidePosIfAnon = false, + sources = {[source] = true} + } - xPlayer.set('phoneNumber', phoneNumber) + xPlayer.set('phoneNumber', phoneNumber) - if PhoneNumbers[xPlayer.job.name] ~= nil then - TriggerEvent('esx_phone:addSource', xPlayer.job.name, source) - end + if PhoneNumbers[xPlayer.job.name] ~= nil then + TriggerEvent('esx_phone:addSource', xPlayer.job.name, source) + end - local contacts = {} + local contacts = {} - MySQL.Async.fetchAll( - 'SELECT * FROM user_contacts WHERE identifier = @identifier ORDER BY name ASC', - { - ['@identifier'] = xPlayer.identifier - }, - function(result2) + MySQL.Async.fetchAll( + 'SELECT * FROM user_contacts WHERE identifier = @identifier ORDER BY name ASC', + { + ['@identifier'] = xPlayer.identifier + }, + function(result2) - for i=1, #result2, 1 do + for i=1, #result2, 1 do - table.insert(contacts, { - name = result2[i].name, - number = result2[i].number, - }) - end + table.insert(contacts, { + name = result2[i].name, + number = result2[i].number, + }) + end - xPlayer.set('contacts', contacts) + xPlayer.set('contacts', contacts) - TriggerClientEvent('esx_phone:loaded', source, phoneNumber, contacts) + TriggerClientEvent('esx_phone:loaded', source, phoneNumber, contacts) - end - ) + end + ) - end - ) + end + ) end) AddEventHandler('esx:playerDropped', function(source) - - local xPlayer = ESX.GetPlayerFromId(source) - - TriggerClientEvent('esx_phone:setPhoneNumberSource', -1, xPlayer.get('phoneNumber'), -1) - PhoneNumbers[xPlayer.get('phoneNumber')] = nil + local xPlayer = ESX.GetPlayerFromId(source) - if PhoneNumbers[xPlayer.job.name] ~= nil then - TriggerEvent('esx_phone:removeSource', xPlayer.job.name, source) - end + TriggerClientEvent('esx_phone:setPhoneNumberSource', -1, xPlayer.get('phoneNumber'), -1) + + PhoneNumbers[xPlayer.get('phoneNumber')] = nil + + if PhoneNumbers[xPlayer.job.name] ~= nil then + TriggerEvent('esx_phone:removeSource', xPlayer.job.name, source) + end end) AddEventHandler('esx:setJob', function(source, job, lastJob) - - if PhoneNumbers[lastJob.name] ~= nil then - TriggerEvent('esx_phone:removeSource', lastJob.name, source) - end - if PhoneNumbers[job.name] ~= nil then - TriggerEvent('esx_phone:addSource', job.name, source) - end + if PhoneNumbers[lastJob.name] ~= nil then + TriggerEvent('esx_phone:removeSource', lastJob.name, source) + end + + if PhoneNumbers[job.name] ~= nil then + TriggerEvent('esx_phone:addSource', job.name, source) + end end) RegisterServerEvent('esx_phone:reload') AddEventHandler('esx_phone:reload', function(phoneNumber) - local _source = source - local xPlayer = ESX.GetPlayerFromId(_source) - local contacts = xPlayer.get('contacts') + local _source = source + local xPlayer = ESX.GetPlayerFromId(_source) + local contacts = xPlayer.get('contacts') - TriggerClientEvent('esx_phone:loaded', _source, phoneNumber, contacts) + TriggerClientEvent('esx_phone:loaded', _source, phoneNumber, contacts) end) RegisterServerEvent('esx_phone:send') AddEventHandler('esx_phone:send', function(phoneNumber, message, anon, position) - local _source = source - local xPlayer = ESX.GetPlayerFromId(_source) + local _source = source + local xPlayer = ESX.GetPlayerFromId(_source) - print('MESSAGE => ' .. xPlayer.name .. '@' .. phoneNumber .. ' : ' .. message) + print('MESSAGE => ' .. xPlayer.name .. '@' .. phoneNumber .. ' : ' .. message) - if PhoneNumbers[phoneNumber] ~= nil then + if PhoneNumbers[phoneNumber] ~= nil then - for k,v in pairs(PhoneNumbers[phoneNumber].sources) do + for k,v in pairs(PhoneNumbers[phoneNumber].sources) do - local numType = PhoneNumbers[phoneNumber].type - local numHasDispatch = PhoneNumbers[phoneNumber].hasDispatch - local numHide = PhoneNumbers[phoneNumber].hideNumber - local numHidePosIfAnon = PhoneNumbers[phoneNumber].hidePosIfAnon - local numPosition = (PhoneNumbers[phoneNumber].sharePos and position or false) - local numSource = tonumber(k) + local numType = PhoneNumbers[phoneNumber].type + local numHasDispatch = PhoneNumbers[phoneNumber].hasDispatch + local numHide = PhoneNumbers[phoneNumber].hideNumber + local numHidePosIfAnon = PhoneNumbers[phoneNumber].hidePosIfAnon + local numPosition = (PhoneNumbers[phoneNumber].sharePos and position or false) + local numSource = tonumber(k) - if numHidePosIfAnon and anon then - numPosition = false - end + if numHidePosIfAnon and anon then + numPosition = false + end - if numHasDispatch then - TriggerClientEvent('esx_phone:onMessage', numSource, xPlayer.get('phoneNumber'), message, numPosition, (numHide and true or anon), numType, GetDistpatchRequestId()) - else - TriggerClientEvent('esx_phone:onMessage', numSource, xPlayer.get('phoneNumber'), message, numPosition, (numHide and true or anon), numType, false) - end + if numHasDispatch then + TriggerClientEvent('esx_phone:onMessage', numSource, xPlayer.get('phoneNumber'), message, numPosition, (numHide and true or anon), numType, GetDistpatchRequestId()) + else + TriggerClientEvent('esx_phone:onMessage', numSource, xPlayer.get('phoneNumber'), message, numPosition, (numHide and true or anon), numType, false) + end - end + end - end + end end) AddEventHandler('esx_phone:registerNumber', function(number, type, sharePos, hasDispatch, hideNumber, hidePosIfAnon) - - local hideNumber = hideNumber or false - local hidePosIfAnon = hidePosIfAnon or false - PhoneNumbers[number] = { - type = type, - sharePos = sharePos, - hasDispatch = (hasDispatch or false), - hideNumber = hideNumber, - hidePosIfAnon = hidePosIfAnon, - sources = {} - } + local hideNumber = hideNumber or false + local hidePosIfAnon = hidePosIfAnon or false + + PhoneNumbers[number] = { + type = type, + sharePos = sharePos, + hasDispatch = (hasDispatch or false), + hideNumber = hideNumber, + hidePosIfAnon = hidePosIfAnon, + sources = {} + } end) AddEventHandler('esx_phone:addSource', function(number, source) - PhoneNumbers[number].sources[tostring(source)] = true + PhoneNumbers[number].sources[tostring(source)] = true end) AddEventHandler('esx_phone:removeSource', function(number, source) - PhoneNumbers[number].sources[tostring(source)] = nil + PhoneNumbers[number].sources[tostring(source)] = nil end) RegisterServerEvent('esx_phone:addPlayerContact') AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) - local _source = source - local xPlayer = ESX.GetPlayerFromId(_source) - local foundNumber = false - local foundPlayer = nil + local _source = source + local xPlayer = ESX.GetPlayerFromId(_source) + local foundNumber = false + local foundPlayer = nil - MySQL.Async.fetchAll( - 'SELECT phone_number FROM users WHERE phone_number = @number', - { - ['@number'] = phoneNumber - }, - function(result) + MySQL.Async.fetchAll( + 'SELECT phone_number FROM users WHERE phone_number = @number', + { + ['@number'] = phoneNumber + }, + function(result) - if result[1] ~= nil then - foundNumber = true - end + if result[1] ~= nil then + foundNumber = true + end - if foundNumber then + if foundNumber then - if phoneNumber == xPlayer.get('phoneNumber') then - TriggerClientEvent('esx:showNotification', _source, _U('cannot_add_self')) - else + if phoneNumber == xPlayer.get('phoneNumber') then + TriggerClientEvent('esx:showNotification', _source, _U('cannot_add_self')) + else - local hasAlreadyAdded = false - local contacts = xPlayer.get('contacts') + local hasAlreadyAdded = false + local contacts = xPlayer.get('contacts') - for i=1, #contacts, 1 do - if contacts[i].number == phoneNumber then - hasAlreadyAdded = true - end - end + for i=1, #contacts, 1 do + if contacts[i].number == phoneNumber then + hasAlreadyAdded = true + end + end - if hasAlreadyAdded then - TriggerClientEvent('esx:showNotification', _source, _U('number_in_contacts')) - else + if hasAlreadyAdded then + TriggerClientEvent('esx:showNotification', _source, _U('number_in_contacts')) + else - table.insert(contacts, { - name = contactName, - number = phoneNumber, - }) + table.insert(contacts, { + name = contactName, + number = phoneNumber, + }) - xPlayer.set('contacts', contacts) + xPlayer.set('contacts', contacts) - MySQL.Async.execute( - 'INSERT INTO user_contacts (identifier, name, number) VALUES (@identifier, @name, @number)', - { - ['@identifier'] = xPlayer.identifier, - ['@name'] = contactName, - ['@number'] = phoneNumber - }, - function(rowsChanged) + MySQL.Async.execute( + 'INSERT INTO user_contacts (identifier, name, number) VALUES (@identifier, @name, @number)', + { + ['@identifier'] = xPlayer.identifier, + ['@name'] = contactName, + ['@number'] = phoneNumber + }, + function(rowsChanged) - TriggerClientEvent('esx:showNotification', _source, _U('contact_added')) + TriggerClientEvent('esx:showNotification', _source, _U('contact_added')) - TriggerClientEvent('esx_phone:addContact', _source, contactName, phoneNumber) - end - ) + TriggerClientEvent('esx_phone:addContact', _source, contactName, phoneNumber) + end + ) - end - end + end + end - else - TriggerClientEvent('esx:showNotification', source, _U('number_not_assigned')) - end + else + TriggerClientEvent('esx:showNotification', source, _U('number_not_assigned')) + end - end - ) + end + ) end) RegisterServerEvent('esx_phone:stopDispatch') AddEventHandler('esx_phone:stopDispatch', function(dispatchRequestId) - TriggerClientEvent('esx_phone:stopDispatch', -1, dispatchRequestId, GetPlayerName(source)) + TriggerClientEvent('esx_phone:stopDispatch', -1, dispatchRequestId, GetPlayerName(source)) end) From bdc4caf53a69cae2f7c16275e1c8fef2e6e628f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Thu, 14 Sep 2017 23:18:04 +0200 Subject: [PATCH 46/76] Update LICENSE.txt --- LICENSE.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/LICENSE.txt b/LICENSE.txt index 30ace6a8..9f1d07ea 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -631,8 +631,8 @@ to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - {one line to give the program's name and a brief idea of what it does.} - Copyright (C) {year} {name of author} + fxserver-esx_phone + Copyright (C) 2015 Jérémie N'gadi This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - {project} Copyright (C) {year} {fullname} + fxserver-esx_phone Copyright (C) 2015 Jérémie N'gadi This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. From 79c9f96ce046e14814f42b0d6d7d3b73822543e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Fri, 15 Sep 2017 15:38:03 +0200 Subject: [PATCH 47/76] chore(resource): Add version --- __resource.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/__resource.lua b/__resource.lua index 4456627f..33f1d883 100644 --- a/__resource.lua +++ b/__resource.lua @@ -2,6 +2,8 @@ resource_manifest_version '44febabe-d386-4d18-afbe-5e627f4af937' description 'ESX Phone' +version '1.0.0' + server_scripts { '@mysql-async/lib/MySQL.lua', 'config.lua', From 45a3f8e3d24da1580fcf2b65054d5d04f728d920 Mon Sep 17 00:00:00 2001 From: ElPumpo Date: Sat, 14 Apr 2018 11:04:48 +0200 Subject: [PATCH 48/76] Only listen to keyboard input --- client/main.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/main.lua b/client/main.lua index 5348f408..7a8cb3b4 100644 --- a/client/main.lua +++ b/client/main.lua @@ -240,7 +240,7 @@ end) Citizen.CreateThread(function() while true do - Wait(0) + Citizen.Wait(10) if GUI.PhoneIsShowed then -- codes here: https://pastebin.com/guYd0ht4 DisableControlAction(0, 1, true) -- LookLeftRight @@ -282,7 +282,7 @@ end) Citizen.CreateThread(function() while true do - Citizen.Wait(0) + Citizen.Wait(10) if CurrentAction ~= nil then @@ -290,7 +290,7 @@ Citizen.CreateThread(function() AddTextComponentString(CurrentActionMsg) DisplayHelpTextFromStringLabel(0, 0, 1, -1) - if IsControlPressed(0, Keys['E']) and (GetGameTimer() - GUI.Time) > 300 then + if IsControlPressed(0, Keys['E']) and GetLastInputMethod(2) and (GetGameTimer() - GUI.Time) > 300 then if CurrentAction == 'dispatch' then TriggerServerEvent('esx_phone:stopDispatch', CurrentDispatchRequestId) From 4ab3c12c0e1324c801222409831394900fbf006e Mon Sep 17 00:00:00 2001 From: ElPumpo Date: Wed, 25 Apr 2018 21:11:17 +0200 Subject: [PATCH 49/76] Minor improvements, only keyboard input --- client/main.lua | 2 +- locales/en.lua | 10 +++++----- server/main.lua | 6 ------ 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/client/main.lua b/client/main.lua index 7a8cb3b4..e5aa9924 100644 --- a/client/main.lua +++ b/client/main.lua @@ -263,7 +263,7 @@ Citizen.CreateThread(function() DisableControlAction(0, 17, true) -- Select Prev Weapon else - if IsControlPressed(0, Keys['F1']) and (GetGameTimer() - GUI.Time) > 150 then + if IsControlPressed(0, Keys['F1']) and GetLastInputMethod(2) and (GetGameTimer() - GUI.Time) > 150 then if not ESX.UI.Menu.IsOpen('phone', GetCurrentResourceName(), 'main') then ESX.UI.Menu.CloseAll() diff --git a/locales/en.lua b/locales/en.lua index 4edcb040..3d7f75df 100644 --- a/locales/en.lua +++ b/locales/en.lua @@ -1,11 +1,11 @@ Locales['en'] = { - ['new_message'] = '~b~New message:~s~ %s', + ['new_message'] = '~b~Recived message:~s~ %s', ['press_take_call'] = ' - Press ~INPUT_CONTEXT~ to take the call', ['taken_call'] = ' has taken the call', ['gps_position'] = 'destination entered into the GPS', - ['message_sent'] = 'message sent', + ['message_sent'] = 'the message has been sent', ['cannot_add_self'] = 'you can not add yourself', - ['number_in_contacts'] = 'this number is alraedy in your contact list', - ['contact_added'] = 'contact added', - ['number_not_assigned'] = 'this number is not assigned...', + ['number_in_contacts'] = 'this number is already in your contact list', + ['contact_added'] = 'the contact has been added!', + ['number_not_assigned'] = 'this number is not assigned to anyone!', } diff --git a/server/main.lua b/server/main.lua index 7cca9d59..c5effb06 100644 --- a/server/main.lua +++ b/server/main.lua @@ -51,12 +51,6 @@ AddEventHandler('esx_phone:getDistpatchRequestId', function(cb) cb(GetDistpatchRequestId()) end) -AddEventHandler('onResourceStart', function(ressource) - if ressource == 'esx_phone' then - TriggerEvent('esx_phone:ready') - end -end) - AddEventHandler('esx:playerLoaded', function(source) local xPlayer = ESX.GetPlayerFromId(source) From fcca14f901f2109aa743e78457e9c251b0a8cd3b Mon Sep 17 00:00:00 2001 From: pleasega Date: Tue, 1 May 2018 02:51:31 +0800 Subject: [PATCH 50/76] Add delete contact feature --- client/main.lua | 25 ++++++++++++++++++++ html/css/app.css | 2 +- html/scripts/app.js | 19 ++++++++++++++- locales/br.lua | 1 + locales/de.lua | 1 + locales/en.lua | 2 ++ locales/es.lua | 1 + locales/fr.lua | 1 + server/main.lua | 57 ++++++++++++++++++++++++++++++++++++++++++++- 9 files changed, 106 insertions(+), 3 deletions(-) diff --git a/client/main.lua b/client/main.lua index e5aa9924..4e30a3fc 100644 --- a/client/main.lua +++ b/client/main.lua @@ -109,6 +109,21 @@ AddEventHandler('esx_phone:addContact', function(name, phoneNumber) }) end) +RegisterNetEvent('esx_phone:removeContact') +AddEventHandler('esx_phone:removeContact', function(name, phoneNumber) + + for key, value in pairs(PhoneData.contacts) do + if value.name == name and value.number == phoneNumber then + table.remove(PhoneData.contacts,key) + end + end + -- CALL HERE RELOADCONTACT + SendNUIMessage({ + contactRemoved = true, + phoneData = PhoneData + }) +end) + RegisterNetEvent('esx_phone:addSpecialContact') AddEventHandler('esx_phone:addSpecialContact', function(name, phoneNumber, base64Icon) @@ -142,6 +157,16 @@ RegisterNUICallback('add_contact', function(data, cb) end) +RegisterNUICallback('remove_contact', function(data, cb) + + local phoneNumber = tonumber(data.phoneNumber) + local contactName = tostring(data.contactName) + + if phoneNumber then + TriggerServerEvent('esx_phone:removePlayerContact', phoneNumber, contactName) + end + +end) RegisterNetEvent('esx_phone:onMessage') AddEventHandler('esx_phone:onMessage', function(phoneNumber, message, position, anon, job, dispatchRequestId) diff --git a/html/css/app.css b/html/css/app.css index d29c0273..061b08e5 100644 --- a/html/css/app.css +++ b/html/css/app.css @@ -305,7 +305,7 @@ html, body { display: block; } -.actions .new-msg:hover, .actions .gps:hover, .actions .ok-btn:hover { +.actions .new-msg:hover, .actions .gps:hover, .actions .ok-btn:hover, .actions .del-contact:hover { background-color: #f1f1f1; } diff --git a/html/scripts/app.js b/html/scripts/app.js index 3afebe53..fd5c4835 100644 --- a/html/scripts/app.js +++ b/html/scripts/app.js @@ -7,7 +7,10 @@ '{{sender}}
    #{{phoneNumber}}' + '
    ' + '
    ' + - '
    ' + + '
    ' + + 'X' + + '' + + '
    ' + '
    ' ; @@ -119,6 +122,16 @@ } $('#phone #repertoire .repertoire-list').html(contactHTML); + + $('.contact .del-contact').click(function() { + let name = $(this).attr('data-contact-name'); + let phoneNumber = $(this).attr('data-contact-number'); + + $.post('http://esx_phone/remove_contact', JSON.stringify({ + contactName: name, + phoneNumber: phoneNumber + })) + }); $('.contact.online .new-msg').click(function() { showNewMessage($(this).attr('data-contact-number'), $(this).attr('data-contact-name')); @@ -451,6 +464,10 @@ hideAddContact(); } + if(data.contactRemoved === true){ + reloadPhone(data.phoneData); + } + if(data.addSpecialContact === true){ addSpecialContact(data.name, data.number, data.base64Icon); } diff --git a/locales/br.lua b/locales/br.lua index 600b3848..043754e2 100644 --- a/locales/br.lua +++ b/locales/br.lua @@ -7,5 +7,6 @@ Locales['br'] = { ['cannot_add_self'] = 'Você não pode se adicionar', ['number_in_contacts'] = 'Este número já está na sua lista de contatos', ['contact_added'] = 'Contato adicionado', + ['contact_removed'] = 'the contact has been removed!', ['number_not_assigned'] = 'Este número não foi atribuído...', } diff --git a/locales/de.lua b/locales/de.lua index 6216e74b..5f8606f7 100644 --- a/locales/de.lua +++ b/locales/de.lua @@ -7,5 +7,6 @@ Locales['de'] = { ['cannot_add_self'] = 'du kannst dich nicht selbst hinzufügen', ['number_in_contacts'] = 'diese Nummer ist bereits in deinen Kontakten', ['contact_added'] = 'Kontakt hinzugefügt', + ['contact_removed'] = 'the contact has been removed!', ['number_not_assigned'] = 'diese Nummer ist nicht vergeben...', } diff --git a/locales/en.lua b/locales/en.lua index 3d7f75df..b6d5c8b0 100644 --- a/locales/en.lua +++ b/locales/en.lua @@ -6,6 +6,8 @@ Locales['en'] = { ['message_sent'] = 'the message has been sent', ['cannot_add_self'] = 'you can not add yourself', ['number_in_contacts'] = 'this number is already in your contact list', + ['number_in_contacts'] = 'this number is already in your contact list', ['contact_added'] = 'the contact has been added!', + ['contact_removed'] = 'the contact has been removed!', ['number_not_assigned'] = 'this number is not assigned to anyone!', } diff --git a/locales/es.lua b/locales/es.lua index 68460324..af889ebf 100644 --- a/locales/es.lua +++ b/locales/es.lua @@ -7,5 +7,6 @@ Locales['es'] = { ['cannot_add_self'] = 'No has podido agregar', ['number_in_contacts'] = 'Este número ya está en tu lista de contactos', ['contact_added'] = 'Contacto añadido', + ['contact_removed'] = 'the contact has been removed!', ['number_not_assigned'] = 'El número no se ha añadido todavía...', } diff --git a/locales/fr.lua b/locales/fr.lua index bba0228e..ef6d2166 100644 --- a/locales/fr.lua +++ b/locales/fr.lua @@ -7,5 +7,6 @@ Locales['fr'] = { ['cannot_add_self'] = 'vous ne pouvez pas vous ajouter vous-même', ['number_in_contacts'] = 'ce numéro est déja dans votre liste de contacts', ['contact_added'] = 'contact ajouté', + ['contact_removed'] = 'contact supprimé', ['number_not_assigned'] = 'ce numéro n\'est pas attribué...', } diff --git a/server/main.lua b/server/main.lua index c5effb06..0c9a798b 100644 --- a/server/main.lua +++ b/server/main.lua @@ -248,7 +248,8 @@ AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) if foundNumber then - if phoneNumber == xPlayer.get('phoneNumber') then + -- if phoneNumber == xPlayer.get('phoneNumber') then + if false then TriggerClientEvent('esx:showNotification', _source, _U('cannot_add_self')) else @@ -299,6 +300,60 @@ AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) end) +RegisterServerEvent('esx_phone:removePlayerContact') +AddEventHandler('esx_phone:removePlayerContact', function(phoneNumber, contactName) + + local _source = source + local xPlayer = ESX.GetPlayerFromId(_source) + local foundNumber = false + local foundPlayer = nil + + MySQL.Async.fetchAll( + 'SELECT phone_number FROM users WHERE phone_number = @number', + { + ['@number'] = phoneNumber + }, + function(result) + + if result[1] ~= nil then + foundNumber = true + end + + if foundNumber then + + local contacts = xPlayer.get('contacts') + + for key, value in pairs(contacts) do + if value.name == contactName and value.number == phoneNumber then + table.remove(contacts,key) + end + end + + xPlayer.set('contacts', contacts) + + MySQL.Async.execute( + 'DELETE FROM user_contacts WHERE identifier=@identifier AND name=@name AND number=@number', + { + ['@identifier'] = xPlayer.identifier, + ['@name'] = contactName, + ['@number'] = phoneNumber + }, + function(rowsChanged) + + TriggerClientEvent('esx:showNotification', _source, _U('contact_removed')) + + TriggerClientEvent('esx_phone:removeContact', _source, contactName, phoneNumber) + end + ) + else + TriggerClientEvent('esx:showNotification', source, _U('number_not_assigned')) + end + + end + ) + +end) + RegisterServerEvent('esx_phone:stopDispatch') AddEventHandler('esx_phone:stopDispatch', function(dispatchRequestId) TriggerClientEvent('esx_phone:stopDispatch', -1, dispatchRequestId, GetPlayerName(source)) From 37ab2cb2b0aff5fa760241fcafb5df479c0854bc Mon Sep 17 00:00:00 2001 From: ElPumpo Date: Tue, 1 May 2018 21:02:54 +0200 Subject: [PATCH 51/76] Removed code debug --- client/main.lua | 2 +- locales/br.lua | 2 +- locales/de.lua | 4 ++-- locales/en.lua | 1 - locales/es.lua | 2 +- server/main.lua | 3 +-- 6 files changed, 6 insertions(+), 8 deletions(-) diff --git a/client/main.lua b/client/main.lua index 4e30a3fc..f3ca2bf1 100644 --- a/client/main.lua +++ b/client/main.lua @@ -315,7 +315,7 @@ Citizen.CreateThread(function() AddTextComponentString(CurrentActionMsg) DisplayHelpTextFromStringLabel(0, 0, 1, -1) - if IsControlPressed(0, Keys['E']) and GetLastInputMethod(2) and (GetGameTimer() - GUI.Time) > 300 then + if IsControlPressed(0, Keys['E']) and GetLastInputMethod(2) and (GetGameTimer() - GUI.Time) > 300 then if CurrentAction == 'dispatch' then TriggerServerEvent('esx_phone:stopDispatch', CurrentDispatchRequestId) diff --git a/locales/br.lua b/locales/br.lua index 043754e2..9214fe24 100644 --- a/locales/br.lua +++ b/locales/br.lua @@ -6,7 +6,7 @@ Locales['br'] = { ['message_sent'] = 'mensagem enviada', ['cannot_add_self'] = 'Você não pode se adicionar', ['number_in_contacts'] = 'Este número já está na sua lista de contatos', - ['contact_added'] = 'Contato adicionado', + ['contact_added'] = 'contato adicionado', ['contact_removed'] = 'the contact has been removed!', ['number_not_assigned'] = 'Este número não foi atribuído...', } diff --git a/locales/de.lua b/locales/de.lua index 5f8606f7..425463f3 100644 --- a/locales/de.lua +++ b/locales/de.lua @@ -2,11 +2,11 @@ Locales['de'] = { ['new_message'] = '~b~Neue Nachricht', ['press_take_call'] = ' - Drücke ~INPUT_CONTEXT~ um den Anruf anzunehmen', ['taken_call'] = ' hat den Anruf angenommen', - ['gps_position'] = 'Ziel in GPS eingegeben', + ['gps_position'] = 'ziel in GPS eingegeben', ['message_sent'] = 'nachricht gesendet', ['cannot_add_self'] = 'du kannst dich nicht selbst hinzufügen', ['number_in_contacts'] = 'diese Nummer ist bereits in deinen Kontakten', - ['contact_added'] = 'Kontakt hinzugefügt', + ['contact_added'] = 'kontakt hinzugefügt', ['contact_removed'] = 'the contact has been removed!', ['number_not_assigned'] = 'diese Nummer ist nicht vergeben...', } diff --git a/locales/en.lua b/locales/en.lua index b6d5c8b0..2d6c31ea 100644 --- a/locales/en.lua +++ b/locales/en.lua @@ -6,7 +6,6 @@ Locales['en'] = { ['message_sent'] = 'the message has been sent', ['cannot_add_self'] = 'you can not add yourself', ['number_in_contacts'] = 'this number is already in your contact list', - ['number_in_contacts'] = 'this number is already in your contact list', ['contact_added'] = 'the contact has been added!', ['contact_removed'] = 'the contact has been removed!', ['number_not_assigned'] = 'this number is not assigned to anyone!', diff --git a/locales/es.lua b/locales/es.lua index af889ebf..ad06dff6 100644 --- a/locales/es.lua +++ b/locales/es.lua @@ -8,5 +8,5 @@ Locales['es'] = { ['number_in_contacts'] = 'Este número ya está en tu lista de contactos', ['contact_added'] = 'Contacto añadido', ['contact_removed'] = 'the contact has been removed!', - ['number_not_assigned'] = 'El número no se ha añadido todavía...', + ['number_not_assigned'] = 'el número no se ha añadido todavía...', } diff --git a/server/main.lua b/server/main.lua index 0c9a798b..548823b9 100644 --- a/server/main.lua +++ b/server/main.lua @@ -248,8 +248,7 @@ AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) if foundNumber then - -- if phoneNumber == xPlayer.get('phoneNumber') then - if false then + if phoneNumber == xPlayer.get('phoneNumber') then TriggerClientEvent('esx:showNotification', _source, _U('cannot_add_self')) else From 4326238f2821a84ab8547bc7f72c727ca9411446 Mon Sep 17 00:00:00 2001 From: ElPumpo Date: Wed, 2 May 2018 17:42:16 +0200 Subject: [PATCH 52/76] Closes #20, fixes #23, security improvements, added swedish language, improvements - Tab indents - Swedish translation - Security update - Readme improvements - SQL file improved - Improved i18n formatting --- README.md | 73 +++++++-- __resource.lua | 78 ++++----- client/main.lua | 424 ++++++++++++++++++++++-------------------------- esx_phone.sql | 18 +- locales/br.lua | 21 +-- locales/de.lua | 21 +-- locales/en.lua | 23 +-- locales/es.lua | 21 +-- locales/fr.lua | 21 +-- locales/sv.lua | 13 ++ server/main.lua | 143 ++++++++-------- 11 files changed, 444 insertions(+), 412 deletions(-) create mode 100644 locales/sv.lua diff --git a/README.md b/README.md index 9a42a9c6..14151baa 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,71 @@ -# fxserver-esx_phone -FXServer esx_phone +# esx_phone -[INFO] +### Note +- All scripts that implement the usage of esx phone must be started **after** this script, or else they wont register and no messages will be received. -Original script by FiveDev => https://github.com/FiveDev-FR/esx_phone-by-FiveDev +## Download & Installation -[INSTALLATION] - -1) CD in your resources/[esx] folder -2) Clone the repository +### Using [fvm](https://github.com/qlaffont/fvm-installer) ``` -git clone https://github.com/FXServer-ESX/fxserver-esx_phone esx_phone +fvm install --save --folder=esx esx-org/esx_phone ``` -3) Import esx_phone.sql in your database -4) Add this in your server.cfg : + +### Using Git +``` +cd resources +git clone https://github.com/ESX-Org/esx_phone [esx]/esx_phone +``` + +### Manually +- Download https://github.com/ESX-Org/esx_phone/archive/master.zip +- Put it in the `[esx]` directory + +- Import `esx_phone.sql` in your database +- Add this in your `server.cfg`: ``` start esx_phone ``` + +### Add custom contacts + +Client side + +```lua +RegisterNetEvent('esx_phone:loaded') +AddEventHandler('esx_phone:loaded', function(phoneNumber, contacts) + + local specialContact = { + name = 'societyName', + number = 'societyNumber', + base64Icon = 'insert base 64 icon (PNG recommended)' + } + + TriggerEvent('esx_phone:addSpecialContact', specialContact.name, specialContact.number, specialContact.base64Icon) + +end) +``` + +Server side + +```lua +TriggerServerEvent('esx_phone:registerNumber', number, type, sharePos, hasDispatch, hideNumber, hidePosIfAnon) + +example + +TriggerEvent('esx_phone:registerNumber', 'ambulance', _U('alert_ambulance'), true, true) + +last two booleans are optional +``` + +# Legal +### License +esx_phone - phone script for fivem + +Copyright (C) 2015-2018 Jérémie N'gadi + +This program Is free software: you can redistribute it And/Or modify it under the terms Of the GNU General Public License As published by the Free Software Foundation, either version 3 Of the License, Or (at your option) any later version. + +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 http://www.gnu.org/licenses/. diff --git a/__resource.lua b/__resource.lua index 33f1d883..8069fbc4 100644 --- a/__resource.lua +++ b/__resource.lua @@ -2,52 +2,54 @@ resource_manifest_version '44febabe-d386-4d18-afbe-5e627f4af937' description 'ESX Phone' -version '1.0.0' +version '1.1.0' server_scripts { - '@mysql-async/lib/MySQL.lua', - 'config.lua', - '@es_extended/locale.lua', - 'locales/de.lua', - 'locales/br.lua', - 'locales/en.lua', - 'locales/fr.lua', - 'locales/es.lua', - 'server/main.lua' + '@mysql-async/lib/MySQL.lua', + 'config.lua', + '@es_extended/locale.lua', + 'locales/de.lua', + 'locales/br.lua', + 'locales/en.lua', + 'locales/fr.lua', + 'locales/es.lua', + 'locales/sv.lua', + 'server/main.lua' } client_scripts { - '@es_extended/locale.lua', - 'config.lua', - 'locales/de.lua', - 'locales/br.lua', - 'locales/en.lua', - 'locales/fr.lua', - 'locales/es.lua', - 'client/main.lua' + '@es_extended/locale.lua', + 'config.lua', + 'locales/de.lua', + 'locales/br.lua', + 'locales/en.lua', + 'locales/fr.lua', + 'locales/es.lua', + 'locales/sv.lua', + 'client/main.lua' } ui_page 'html/ui.html' files { - 'html/ui.html', - 'html/bankgothic.ttf', - 'html/pdown.ttf', - 'html/css/app.css', - 'html/scripts/mustache.min.js', - 'html/scripts/app.js', - 'html/img/cursor.png', - 'html/img/keys/enter.png', - 'html/img/keys/return.png', - 'html/img/phone.png', - 'html/img/icons/signal.png', - 'html/img/icons/rep.png', - 'html/img/icons/msg.png', - 'html/img/icons/add.png', - 'html/img/icons/back.png', - 'html/img/icons/new-msg.png', - 'html/img/icons/reply.png', - 'html/img/icons/write.png', - 'html/img/icons/edit.png', - 'html/img/icons/location.png' + 'html/ui.html', + 'html/bankgothic.ttf', + 'html/pdown.ttf', + 'html/css/app.css', + 'html/scripts/mustache.min.js', + 'html/scripts/app.js', + 'html/img/cursor.png', + 'html/img/keys/enter.png', + 'html/img/keys/return.png', + 'html/img/phone.png', + 'html/img/icons/signal.png', + 'html/img/icons/rep.png', + 'html/img/icons/msg.png', + 'html/img/icons/add.png', + 'html/img/icons/back.png', + 'html/img/icons/new-msg.png', + 'html/img/icons/reply.png', + 'html/img/icons/write.png', + 'html/img/icons/edit.png', + 'html/img/icons/location.png' } diff --git a/client/main.lua b/client/main.lua index f3ca2bf1..1b658e73 100644 --- a/client/main.lua +++ b/client/main.lua @@ -1,13 +1,13 @@ local Keys = { - ["ESC"] = 322, ["F1"] = 288, ["F2"] = 289, ["F3"] = 170, ["F5"] = 166, ["F6"] = 167, ["F7"] = 168, ["F8"] = 169, ["F9"] = 56, ["F10"] = 57, - ["~"] = 243, ["1"] = 157, ["2"] = 158, ["3"] = 160, ["4"] = 164, ["5"] = 165, ["6"] = 159, ["7"] = 161, ["8"] = 162, ["9"] = 163, ["-"] = 84, ["="] = 83, ["BACKSPACE"] = 177, - ["TAB"] = 37, ["Q"] = 44, ["W"] = 32, ["E"] = 38, ["R"] = 45, ["T"] = 245, ["Y"] = 246, ["U"] = 303, ["P"] = 199, ["["] = 39, ["]"] = 40, ["ENTER"] = 18, - ["CAPS"] = 137, ["A"] = 34, ["S"] = 8, ["D"] = 9, ["F"] = 23, ["G"] = 47, ["H"] = 74, ["K"] = 311, ["L"] = 182, - ["LEFTSHIFT"] = 21, ["Z"] = 20, ["X"] = 73, ["C"] = 26, ["V"] = 0, ["B"] = 29, ["N"] = 249, ["M"] = 244, [","] = 82, ["."] = 81, - ["LEFTCTRL"] = 36, ["LEFTALT"] = 19, ["SPACE"] = 22, ["RIGHTCTRL"] = 70, - ["HOME"] = 213, ["PAGEUP"] = 10, ["PAGEDOWN"] = 11, ["DELETE"] = 178, - ["LEFT"] = 174, ["RIGHT"] = 175, ["TOP"] = 27, ["DOWN"] = 173, - ["NENTER"] = 201, ["N4"] = 108, ["N5"] = 60, ["N6"] = 107, ["N+"] = 96, ["N-"] = 97, ["N7"] = 117, ["N8"] = 61, ["N9"] = 118 + ["ESC"] = 322, ["F1"] = 288, ["F2"] = 289, ["F3"] = 170, ["F5"] = 166, ["F6"] = 167, ["F7"] = 168, ["F8"] = 169, ["F9"] = 56, ["F10"] = 57, + ["~"] = 243, ["1"] = 157, ["2"] = 158, ["3"] = 160, ["4"] = 164, ["5"] = 165, ["6"] = 159, ["7"] = 161, ["8"] = 162, ["9"] = 163, ["-"] = 84, ["="] = 83, ["BACKSPACE"] = 177, + ["TAB"] = 37, ["Q"] = 44, ["W"] = 32, ["E"] = 38, ["R"] = 45, ["T"] = 245, ["Y"] = 246, ["U"] = 303, ["P"] = 199, ["["] = 39, ["]"] = 40, ["ENTER"] = 18, + ["CAPS"] = 137, ["A"] = 34, ["S"] = 8, ["D"] = 9, ["F"] = 23, ["G"] = 47, ["H"] = 74, ["K"] = 311, ["L"] = 182, + ["LEFTSHIFT"] = 21, ["Z"] = 20, ["X"] = 73, ["C"] = 26, ["V"] = 0, ["B"] = 29, ["N"] = 249, ["M"] = 244, [","] = 82, ["."] = 81, + ["LEFTCTRL"] = 36, ["LEFTALT"] = 19, ["SPACE"] = 22, ["RIGHTCTRL"] = 70, + ["HOME"] = 213, ["PAGEUP"] = 10, ["PAGEDOWN"] = 11, ["DELETE"] = 178, + ["LEFT"] = 174, ["RIGHT"] = 175, ["TOP"] = 27, ["DOWN"] = 173, + ["NENTER"] = 201, ["N4"] = 108, ["N5"] = 60, ["N6"] = 107, ["N+"] = 96, ["N-"] = 97, ["N7"] = 117, ["N8"] = 61, ["N9"] = 118 } local GUI = {} @@ -27,307 +27,273 @@ GUI.MessagesIsShowed = false GUI.AddContactIsShowed = false Citizen.CreateThread(function() - - while ESX == nil do - TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end) - Citizen.Wait(0) - end - - ESX.UI.Menu.RegisterType('phone', OpenPhone, ClosePhone) - + while ESX == nil do + TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end) + Citizen.Wait(0) + end + + ESX.UI.Menu.RegisterType('phone', OpenPhone, ClosePhone) end) function OpenPhone() + local playerPed = GetPlayerPed(-1) - local playerPed = GetPlayerPed(-1) + TriggerServerEvent('esx_phone:reload', PhoneData.phoneNumber) - TriggerServerEvent('esx_phone:reload', PhoneData.phoneNumber) + SendNUIMessage({ + showPhone = true, + phoneData = PhoneData + }) - SendNUIMessage({ - showPhone = true, - phoneData = PhoneData - }) + GUI.PhoneIsShowed = true - GUI.PhoneIsShowed = true - - ESX.SetTimeout(250, function() - SetNuiFocus(true, true) - end) - - if not IsPedInAnyVehicle(playerPed, false) then - TaskStartScenarioInPlace(playerPed, "WORLD_HUMAN_STAND_MOBILE", 0, true); - end + ESX.SetTimeout(250, function() + SetNuiFocus(true, true) + end) + if not IsPedInAnyVehicle(playerPed, false) then + TaskStartScenarioInPlace(playerPed, "WORLD_HUMAN_STAND_MOBILE", 0, true); + end end function ClosePhone() + local playerPed = GetPlayerPed(-1) - local playerPed = GetPlayerPed(-1) - - SendNUIMessage({ - showPhone = false - }) - - SetNuiFocus(false) - - GUI.PhoneIsShowed = false - - ClearPedTasks(playerPed) + SendNUIMessage({ + showPhone = false + }) + SetNuiFocus(false) + GUI.PhoneIsShowed = false + ClearPedTasks(playerPed) end RegisterNetEvent('esx_phone:loaded') AddEventHandler('esx_phone:loaded', function(phoneNumber, contacts) + PhoneData.phoneNumber = phoneNumber + PhoneData.contacts = {} - PhoneData.phoneNumber = phoneNumber - PhoneData.contacts = {} - - for i=1, #contacts, 1 do - contacts[i].online = (PhoneNumberSources[contacts[i].number] == nil and false or NetworkIsPlayerActive(GetPlayerFromServerId(PhoneNumberSources[contacts[i].number]))), - table.insert(PhoneData.contacts, contacts[i]) - end - - SendNUIMessage({ - reloadPhone = true, - phoneData = PhoneData - }) + for i=1, #contacts, 1 do + contacts[i].online = (PhoneNumberSources[contacts[i].number] == nil and false or NetworkIsPlayerActive(GetPlayerFromServerId(PhoneNumberSources[contacts[i].number]))), + table.insert(PhoneData.contacts, contacts[i]) + end + SendNUIMessage({ + reloadPhone = true, + phoneData = PhoneData + }) end) RegisterNetEvent('esx_phone:addContact') AddEventHandler('esx_phone:addContact', function(name, phoneNumber) - - table.insert(PhoneData.contacts, { - name = name, - number = phoneNumber, - online = (PhoneNumberSources[contacts[i].number] == nil and false or NetworkIsPlayerActive(GetPlayerFromServerId(PhoneNumberSources[contacts[i].number]))), - }) - -- CALL HERE RELOADCONTACT - SendNUIMessage({ - contactAdded = true, - phoneData = PhoneData - }) + table.insert(PhoneData.contacts, { + name = name, + number = phoneNumber, + online = (PhoneNumberSources[contacts[i].number] == nil and false or NetworkIsPlayerActive(GetPlayerFromServerId(PhoneNumberSources[contacts[i].number]))), + }) + -- CALL HERE RELOADCONTACT + SendNUIMessage({ + contactAdded = true, + phoneData = PhoneData + }) end) RegisterNetEvent('esx_phone:removeContact') AddEventHandler('esx_phone:removeContact', function(name, phoneNumber) - - for key, value in pairs(PhoneData.contacts) do - if value.name == name and value.number == phoneNumber then - table.remove(PhoneData.contacts,key) - end - end - -- CALL HERE RELOADCONTACT - SendNUIMessage({ - contactRemoved = true, - phoneData = PhoneData - }) + for key, value in pairs(PhoneData.contacts) do + if value.name == name and value.number == phoneNumber then + table.remove(PhoneData.contacts,key) + break + end + end + -- CALL HERE RELOADCONTACT + SendNUIMessage({ + contactRemoved = true, + phoneData = PhoneData + }) end) RegisterNetEvent('esx_phone:addSpecialContact') AddEventHandler('esx_phone:addSpecialContact', function(name, phoneNumber, base64Icon) - - SendNUIMessage({ - addSpecialContact = true, - name = name, - number = phoneNumber, - base64Icon = base64Icon - }) - + SendNUIMessage({ + addSpecialContact = true, + name = name, + number = phoneNumber, + base64Icon = base64Icon + }) end) RegisterNetEvent('esx_phone:removeSpecialContact') AddEventHandler('esx_phone:removeSpecialContact', function(phoneNumber) - - SendNUIMessage({ - removeSpecialContact = true, - number = phoneNumber - }) - + SendNUIMessage({ + removeSpecialContact = true, + number = phoneNumber + }) end) RegisterNUICallback('add_contact', function(data, cb) - - local phoneNumber = tonumber(data.phoneNumber) - local contactName = tostring(data.contactName) - - if phoneNumber then - TriggerServerEvent('esx_phone:addPlayerContact', phoneNumber, contactName) - end - + local phoneNumber = tonumber(data.phoneNumber) + local contactName = tostring(data.contactName) + + if phoneNumber then + TriggerServerEvent('esx_phone:addPlayerContact', phoneNumber, contactName) + else + ESX.ShowNotification(_U('invalid_number')) + end end) RegisterNUICallback('remove_contact', function(data, cb) + local phoneNumber = tonumber(data.phoneNumber) + local contactName = tostring(data.contactName) - local phoneNumber = tonumber(data.phoneNumber) - local contactName = tostring(data.contactName) - - if phoneNumber then - TriggerServerEvent('esx_phone:removePlayerContact', phoneNumber, contactName) - end - + if phoneNumber then + TriggerServerEvent('esx_phone:removePlayerContact', phoneNumber, contactName) + end end) RegisterNetEvent('esx_phone:onMessage') AddEventHandler('esx_phone:onMessage', function(phoneNumber, message, position, anon, job, dispatchRequestId) + if job == 'player' then + ESX.ShowNotification(_U('new_message', message)) + else + ESX.ShowNotification('~b~' .. job .. ': ~s~' .. message) + end - if job == 'player' then - ESX.ShowNotification(_U('new_message', message)) - else - ESX.ShowNotification('~b~' .. job .. ': ~s~' .. message) - end + SendNUIMessage({ + newMessage = true, + phoneNumber = phoneNumber, + message = message, + position = position, + anonyme = anon, + job = job + }) - SendNUIMessage({ - newMessage = true, - phoneNumber = phoneNumber, - message = message, - position = position, - anonyme = anon, - job = job - }) - - if dispatchRequestId then - - CurrentAction = 'dispatch' - CurrentActionMsg = job .. _U('press_take_call') - CurrentDispatchRequestId = dispatchRequestId - - CurrentActionData = { - phoneNumber = phoneNumber, - message = message, - position = position, - actions = actions, - anonyme = anon, - job = job - } - - ESX.SetTimeout(15000, function() - CurrentAction = nil - end) - - end + if dispatchRequestId then + CurrentAction = 'dispatch' + CurrentActionMsg = _U('press_take_call', job) + CurrentDispatchRequestId = dispatchRequestId + CurrentActionData = { + phoneNumber = phoneNumber, + message = message, + position = position, + actions = actions, + anonyme = anon, + job = job + } + + ESX.SetTimeout(15000, function() + CurrentAction = nil + end) + end end) RegisterNetEvent('esx_phone:stopDispatch') AddEventHandler('esx_phone:stopDispatch', function(dispatchRequestId, playerName) - - if CurrentDispatchRequestId == dispatchRequestId and CurrentAction == 'dispatch' then - CurrentAction = nil - ESX.ShowNotification(playerName .. _U('taken_call')) - end - + if CurrentDispatchRequestId == dispatchRequestId and CurrentAction == 'dispatch' then + CurrentAction = nil + ESX.ShowNotification(_U('taken_call', playerName)) + end end) RegisterNetEvent('esx_phone:setPhoneNumberSource') AddEventHandler('esx_phone:setPhoneNumberSource', function(phoneNumber, source) - - if source == -1 then - PhoneNumberSources[phoneNumber] = nil - else - PhoneNumberSources[phoneNumber] = source - end + if source == -1 then + PhoneNumberSources[phoneNumber] = nil + else + PhoneNumberSources[phoneNumber] = source + end end) RegisterNUICallback('setGPS', function(data) - SetNewWaypoint(data.x, data.y) - ESX.ShowNotification(_U('gps_position')) + SetNewWaypoint(data.x, data.y) + ESX.ShowNotification(_U('gps_position')) end) RegisterNUICallback('send', function(data) + local phoneNumber = data.number + local playerPed = GetPlayerPed(-1) + local coords = GetEntityCoords(playerPed) - local phoneNumber = data.number - local playerPed = GetPlayerPed(-1) - local coords = GetEntityCoords(playerPed) + if tonumber(phoneNumber) ~= nil then + phoneNumber = tonumber(phoneNumber) + end - if tonumber(phoneNumber) ~= nil then - phoneNumber = tonumber(phoneNumber) - end - - TriggerServerEvent('esx_phone:send', phoneNumber, data.message, data.anonyme, { - x = coords.x, - y = coords.y, - z = coords.z - }) - - SendNUIMessage({ - showMessageEditor = false - }) - - ESX.ShowNotification(_U('message_sent')) + TriggerServerEvent('esx_phone:send', phoneNumber, data.message, data.anonyme, { + x = coords.x, + y = coords.y, + z = coords.z + }) + + SendNUIMessage({ + showMessageEditor = false + }) + ESX.ShowNotification(_U('message_sent')) end) RegisterNUICallback('escape', function() - ESX.UI.Menu.Close('phone', GetCurrentResourceName(), 'main') + ESX.UI.Menu.Close('phone', GetCurrentResourceName(), 'main') end) Citizen.CreateThread(function() - while true do + while true do + Citizen.Wait(10) - Citizen.Wait(10) + if GUI.PhoneIsShowed then -- codes here: https://pastebin.com/guYd0ht4 + DisableControlAction(0, 1, true) -- LookLeftRight + DisableControlAction(0, 2, true) -- LookUpDown + DisableControlAction(0, 25, true) -- Input Aim + DisableControlAction(0, 106, true) -- Vehicle Mouse Control Override - if GUI.PhoneIsShowed then -- codes here: https://pastebin.com/guYd0ht4 - DisableControlAction(0, 1, true) -- LookLeftRight - DisableControlAction(0, 2, true) -- LookUpDown - DisableControlAction(0, 25, true) -- Input Aim - DisableControlAction(0, 106, true) -- Vehicle Mouse Control Override + DisableControlAction(0, 24, true) -- Input Attack + DisableControlAction(0, 140, true) -- Melee Attack Alternate + DisableControlAction(0, 141, true) -- Melee Attack Alternate + DisableControlAction(0, 142, true) -- Melee Attack Alternate + DisableControlAction(0, 257, true) -- Input Attack 2 + DisableControlAction(0, 263, true) -- Input Melee Attack + DisableControlAction(0, 264, true) -- Input Melee Attack 2 - DisableControlAction(0, 24, true) -- Input Attack - DisableControlAction(0, 140, true) -- Melee Attack Alternate - DisableControlAction(0, 141, true) -- Melee Attack Alternate - DisableControlAction(0, 142, true) -- Melee Attack Alternate - DisableControlAction(0, 257, true) -- Input Attack 2 - DisableControlAction(0, 263, true) -- Input Melee Attack - DisableControlAction(0, 264, true) -- Input Melee Attack 2 - - DisableControlAction(0, 12, true) -- Weapon Wheel Up Down - DisableControlAction(0, 14, true) -- Weapon Wheel Next - DisableControlAction(0, 15, true) -- Weapon Wheel Prev - DisableControlAction(0, 16, true) -- Select Next Weapon - DisableControlAction(0, 17, true) -- Select Prev Weapon - else - - if IsControlPressed(0, Keys['F1']) and GetLastInputMethod(2) and (GetGameTimer() - GUI.Time) > 150 then - - if not ESX.UI.Menu.IsOpen('phone', GetCurrentResourceName(), 'main') then - ESX.UI.Menu.CloseAll() - ESX.UI.Menu.Open('phone', GetCurrentResourceName(), 'main') - end - - GUI.Time = GetGameTimer() - - end - - end - end + DisableControlAction(0, 12, true) -- Weapon Wheel Up Down + DisableControlAction(0, 14, true) -- Weapon Wheel Next + DisableControlAction(0, 15, true) -- Weapon Wheel Prev + DisableControlAction(0, 16, true) -- Select Next Weapon + DisableControlAction(0, 17, true) -- Select Prev Weapon + else + -- open phone + -- todo: is player busy (handcuffed, etc) + if IsControlPressed(0, Keys['F1']) and GetLastInputMethod(2) and (GetGameTimer() - GUI.Time) > 150 then + if not ESX.UI.Menu.IsOpen('phone', GetCurrentResourceName(), 'main') then + ESX.UI.Menu.CloseAll() + ESX.UI.Menu.Open('phone', GetCurrentResourceName(), 'main') + end + GUI.Time = GetGameTimer() + end + end + end end) -- Key controls Citizen.CreateThread(function() - while true do + while true do + Citizen.Wait(10) + + if CurrentAction ~= nil then - Citizen.Wait(10) + SetTextComponentFormat('STRING') + AddTextComponentString(CurrentActionMsg) + DisplayHelpTextFromStringLabel(0, 0, 1, -1) - if CurrentAction ~= nil then + if IsControlPressed(0, Keys['E']) and GetLastInputMethod(2) and (GetGameTimer() - GUI.Time) > 300 then - SetTextComponentFormat('STRING') - AddTextComponentString(CurrentActionMsg) - DisplayHelpTextFromStringLabel(0, 0, 1, -1) + if CurrentAction == 'dispatch' then + TriggerServerEvent('esx_phone:stopDispatch', CurrentDispatchRequestId) + SetNewWaypoint(CurrentActionData.position.x, CurrentActionData.position.y) + end + CurrentAction = nil + GUI.Time = GetGameTimer() + end - if IsControlPressed(0, Keys['E']) and GetLastInputMethod(2) and (GetGameTimer() - GUI.Time) > 300 then - - if CurrentAction == 'dispatch' then - TriggerServerEvent('esx_phone:stopDispatch', CurrentDispatchRequestId) - SetNewWaypoint(CurrentActionData.position.x, CurrentActionData.position.y) - end - - CurrentAction = nil - GUI.Time = GetGameTimer() - - end - - end - - end + end + end end) diff --git a/esx_phone.sql b/esx_phone.sql index e9069869..0a47211e 100644 --- a/esx_phone.sql +++ b/esx_phone.sql @@ -1,10 +1,14 @@ -CREATE TABLE user_contacts ( - id int(11) NOT NULL AUTO_INCREMENT, - identifier varchar(255) NOT NULL, - name varchar(255) NOT NULL, - number int(11) NOT NULL, - PRIMARY KEY (id) +USE `essentialmode`; + +CREATE TABLE `user_contacts` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `identifier` varchar(255) NOT NULL, + `name` varchar(255) NOT NULL, + `number` int(11) NOT NULL, + + PRIMARY KEY (`id`) ); ALTER TABLE `users` -ADD COLUMN `phone_number` INT NULL; + ADD COLUMN `phone_number` INT NULL +; diff --git a/locales/br.lua b/locales/br.lua index 9214fe24..3275b1fc 100644 --- a/locales/br.lua +++ b/locales/br.lua @@ -1,12 +1,13 @@ Locales['br'] = { - ['new_message'] = '~b~Nova mensagem :~s~ %s', - ['press_take_call'] = ' - Pressione ~INPUT_CONTEXT~ para aceitar a chamada', - ['taken_call'] = ' recebeu uma chamada', - ['gps_position'] = 'Destino inserido no GPS', - ['message_sent'] = 'mensagem enviada', - ['cannot_add_self'] = 'Você não pode se adicionar', - ['number_in_contacts'] = 'Este número já está na sua lista de contatos', - ['contact_added'] = 'contato adicionado', - ['contact_removed'] = 'the contact has been removed!', - ['number_not_assigned'] = 'Este número não foi atribuído...', + ['new_message'] = '~b~Nova mensagem :~s~ %s', + ['press_take_call'] = '%s - Pressione ~INPUT_CONTEXT~ para aceitar a chamada', + ['taken_call'] = '~y~%s~s~ recebeu uma chamada', + ['gps_position'] = 'destino inserido no GPS', + ['message_sent'] = 'mensagem enviada', + ['cannot_add_self'] = 'você não pode se adicionar', + ['number_in_contacts'] = 'este número já está na sua lista de contatos', + ['contact_added'] = 'contato adicionado', + ['contact_removed'] = 'the contact has been removed!', + ['number_not_assigned'] = 'este número não foi atribuído...', + ['invalid_number'] = 'that\'s not an valid number!', } diff --git a/locales/de.lua b/locales/de.lua index 425463f3..bea36169 100644 --- a/locales/de.lua +++ b/locales/de.lua @@ -1,12 +1,13 @@ Locales['de'] = { - ['new_message'] = '~b~Neue Nachricht', - ['press_take_call'] = ' - Drücke ~INPUT_CONTEXT~ um den Anruf anzunehmen', - ['taken_call'] = ' hat den Anruf angenommen', - ['gps_position'] = 'ziel in GPS eingegeben', - ['message_sent'] = 'nachricht gesendet', - ['cannot_add_self'] = 'du kannst dich nicht selbst hinzufügen', - ['number_in_contacts'] = 'diese Nummer ist bereits in deinen Kontakten', - ['contact_added'] = 'kontakt hinzugefügt', - ['contact_removed'] = 'the contact has been removed!', - ['number_not_assigned'] = 'diese Nummer ist nicht vergeben...', + ['new_message'] = '~b~Neue Nachricht', + ['press_take_call'] = '%s - Drücke ~INPUT_CONTEXT~ um den Anruf anzunehmen', + ['taken_call'] = '~y~%s~s~ hat den Anruf angenommen', + ['gps_position'] = 'ziel in GPS eingegeben', + ['message_sent'] = 'nachricht gesendet', + ['cannot_add_self'] = 'du kannst dich nicht selbst hinzufügen', + ['number_in_contacts'] = 'diese Nummer ist bereits in deinen Kontakten', + ['contact_added'] = 'kontakt hinzugefügt', + ['contact_removed'] = 'the contact has been removed!', + ['number_not_assigned'] = 'diese Nummer ist nicht vergeben...', + ['invalid_number'] = 'that\'s not an valid number!', } diff --git a/locales/en.lua b/locales/en.lua index 2d6c31ea..9fea81d3 100644 --- a/locales/en.lua +++ b/locales/en.lua @@ -1,12 +1,13 @@ Locales['en'] = { - ['new_message'] = '~b~Recived message:~s~ %s', - ['press_take_call'] = ' - Press ~INPUT_CONTEXT~ to take the call', - ['taken_call'] = ' has taken the call', - ['gps_position'] = 'destination entered into the GPS', - ['message_sent'] = 'the message has been sent', - ['cannot_add_self'] = 'you can not add yourself', - ['number_in_contacts'] = 'this number is already in your contact list', - ['contact_added'] = 'the contact has been added!', - ['contact_removed'] = 'the contact has been removed!', - ['number_not_assigned'] = 'this number is not assigned to anyone!', -} + ['new_message'] = '~b~You have recived a message:~s~ %s', + ['press_take_call'] = '%s - Press ~INPUT_CONTEXT~ to take the call', + ['taken_call'] = '~y~%s~s~ has taken the call', + ['gps_position'] = 'the destination has been added to your GPS', + ['message_sent'] = 'the message has been sent', + ['cannot_add_self'] = 'you can not add yourself!', + ['number_in_contacts'] = 'this number is already in your contact list', + ['contact_added'] = 'the contact has been added!', + ['contact_removed'] = 'the contact has been removed!', + ['number_not_assigned'] = 'the number has not been assigned!', + ['invalid_number'] = 'that\'s not an valid number!', +} \ No newline at end of file diff --git a/locales/es.lua b/locales/es.lua index ad06dff6..97ca7ad7 100644 --- a/locales/es.lua +++ b/locales/es.lua @@ -1,12 +1,13 @@ Locales['es'] = { - ['new_message'] = '~b~Nuevo menaje :~s~ %s', - ['press_take_call'] = ' - Presionar ~INPUT_CONTEXT~ para coger la llamada', - ['taken_call'] = ' Has contestado la llamada', - ['gps_position'] = 'Posición GPS', - ['message_sent'] = 'Mensaje enviado', - ['cannot_add_self'] = 'No has podido agregar', - ['number_in_contacts'] = 'Este número ya está en tu lista de contactos', - ['contact_added'] = 'Contacto añadido', - ['contact_removed'] = 'the contact has been removed!', - ['number_not_assigned'] = 'el número no se ha añadido todavía...', + ['new_message'] = '~b~Nuevo menaje :~s~ %s', + ['press_take_call'] = '%s - Presionar ~INPUT_CONTEXT~ para coger la llamada', + ['taken_call'] = '~y~%s~s~ has contestado la llamada', + ['gps_position'] = 'posición GPS', + ['message_sent'] = 'mensaje enviado', + ['cannot_add_self'] = 'no has podido agregar', + ['number_in_contacts'] = 'este número ya está en tu lista de contactos', + ['contact_added'] = 'contacto añadido', + ['contact_removed'] = 'the contact has been removed!', + ['number_not_assigned'] = 'el número no se ha añadido todavía...', + ['invalid_number'] = 'that\'s not an valid number!', } diff --git a/locales/fr.lua b/locales/fr.lua index ef6d2166..aa2f88d2 100644 --- a/locales/fr.lua +++ b/locales/fr.lua @@ -1,12 +1,13 @@ Locales['fr'] = { - ['new_message'] = '~b~Nouveau message :~s~ %s', - ['press_take_call'] = ' - Appuyez sur ~INPUT_CONTEXT~ pour prendre l\'appel', - ['taken_call'] = ' a pris l\'appel', - ['gps_position'] = 'position entrée dans le GPS', - ['message_sent'] = 'message envoyé', - ['cannot_add_self'] = 'vous ne pouvez pas vous ajouter vous-même', - ['number_in_contacts'] = 'ce numéro est déja dans votre liste de contacts', - ['contact_added'] = 'contact ajouté', - ['contact_removed'] = 'contact supprimé', - ['number_not_assigned'] = 'ce numéro n\'est pas attribué...', + ['new_message'] = '~b~Nouveau message :~s~ %s', + ['press_take_call'] = '%s - Appuyez sur ~INPUT_CONTEXT~ pour prendre l\'appel', + ['taken_call'] = '~y~%s~s~ a pris l\'appel', + ['gps_position'] = 'position entrée dans le GPS', + ['message_sent'] = 'message envoyé', + ['cannot_add_self'] = 'vous ne pouvez pas vous ajouter vous-même', + ['number_in_contacts'] = 'ce numéro est déja dans votre liste de contacts', + ['contact_added'] = 'contact ajouté', + ['contact_removed'] = 'contact supprimé', + ['number_not_assigned'] = 'ce numéro n\'est pas attribué...', + ['invalid_number'] = 'that\'s not an valid number!', } diff --git a/locales/sv.lua b/locales/sv.lua new file mode 100644 index 00000000..51e39990 --- /dev/null +++ b/locales/sv.lua @@ -0,0 +1,13 @@ +Locales['sv'] = { + ['new_message'] = '~b~Nytt meddelande:~s~ %s', + ['press_take_call'] = '%s - tryck ~INPUT_CONTEXT~ för att svara', + ['taken_call'] = '~y~%s~s~ har tagit emot ditt meddelande', + ['gps_position'] = 'destinationen är markerad på din GPS!', + ['message_sent'] = 'meddelandet har skickats!', + ['cannot_add_self'] = 'du kan inte lägga till dig själv!', + ['number_in_contacts'] = 'det här nummret finns redan bland dina kontakter!', + ['contact_added'] = 'kontakten har lags till!', + ['contact_removed'] = 'kontakten har tagits bort!', + ['number_not_assigned'] = 'det här nummret finns inte!', + ['invalid_number'] = 'det är inte ett giltigt nummer!', +} \ No newline at end of file diff --git a/server/main.lua b/server/main.lua index 548823b9..0c1a3663 100644 --- a/server/main.lua +++ b/server/main.lua @@ -204,108 +204,99 @@ AddEventHandler('esx_phone:send', function(phoneNumber, message, anon, position) end) AddEventHandler('esx_phone:registerNumber', function(number, type, sharePos, hasDispatch, hideNumber, hidePosIfAnon) + local hideNumber = hideNumber or false + local hidePosIfAnon = hidePosIfAnon or false - local hideNumber = hideNumber or false - local hidePosIfAnon = hidePosIfAnon or false - - PhoneNumbers[number] = { - type = type, - sharePos = sharePos, - hasDispatch = (hasDispatch or false), - hideNumber = hideNumber, - hidePosIfAnon = hidePosIfAnon, - sources = {} - } - + PhoneNumbers[number] = { + type = type, + sharePos = sharePos, + hasDispatch = (hasDispatch or false), + hideNumber = hideNumber, + hidePosIfAnon = hidePosIfAnon, + sources = {} + } end) AddEventHandler('esx_phone:addSource', function(number, source) - PhoneNumbers[number].sources[tostring(source)] = true + PhoneNumbers[number].sources[tostring(source)] = true end) AddEventHandler('esx_phone:removeSource', function(number, source) - PhoneNumbers[number].sources[tostring(source)] = nil + PhoneNumbers[number].sources[tostring(source)] = nil end) RegisterServerEvent('esx_phone:addPlayerContact') AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) + local _source = source + local xPlayer = ESX.GetPlayerFromId(_source) + local foundNumber = false + phoneNumber = tonumber(phoneNumber) + + -- is the player trying to enter something else into the database? + if phoneNumber == nil then + print('esx_phone: ' .. xPlayer.identifier .. ' attempted to crash the database!') + return + end - local _source = source - local xPlayer = ESX.GetPlayerFromId(_source) - local foundNumber = false - local foundPlayer = nil + MySQL.Async.fetchAll( + 'SELECT phone_number FROM users WHERE phone_number = @number', + { + ['@number'] = phoneNumber + }, function(result) + if result[1] ~= nil then + foundNumber = true + end + end) - MySQL.Async.fetchAll( - 'SELECT phone_number FROM users WHERE phone_number = @number', - { - ['@number'] = phoneNumber - }, - function(result) + if foundNumber then + if phoneNumber == xPlayer.get('phoneNumber') then + TriggerClientEvent('esx:showNotification', _source, _U('cannot_add_self')) + else + local hasAlreadyAdded = false + local contacts = xPlayer.get('contacts') - if result[1] ~= nil then - foundNumber = true - end + for i=1, #contacts, 1 do + if contacts[i].number == phoneNumber then + hasAlreadyAdded = true + break + end + end - if foundNumber then + if hasAlreadyAdded then + TriggerClientEvent('esx:showNotification', _source, _U('number_in_contacts')) + else - if phoneNumber == xPlayer.get('phoneNumber') then - TriggerClientEvent('esx:showNotification', _source, _U('cannot_add_self')) - else + table.insert(contacts, { + name = contactName, + number = phoneNumber, + }) - local hasAlreadyAdded = false - local contacts = xPlayer.get('contacts') + xPlayer.set('contacts', contacts) - for i=1, #contacts, 1 do - if contacts[i].number == phoneNumber then - hasAlreadyAdded = true - end - end - - if hasAlreadyAdded then - TriggerClientEvent('esx:showNotification', _source, _U('number_in_contacts')) - else - - table.insert(contacts, { - name = contactName, - number = phoneNumber, - }) - - xPlayer.set('contacts', contacts) - - MySQL.Async.execute( - 'INSERT INTO user_contacts (identifier, name, number) VALUES (@identifier, @name, @number)', - { - ['@identifier'] = xPlayer.identifier, - ['@name'] = contactName, - ['@number'] = phoneNumber - }, - function(rowsChanged) - - TriggerClientEvent('esx:showNotification', _source, _U('contact_added')) - - TriggerClientEvent('esx_phone:addContact', _source, contactName, phoneNumber) - end - ) - - end - end - - else - TriggerClientEvent('esx:showNotification', source, _U('number_not_assigned')) - end - - end - ) + MySQL.Async.execute( + 'INSERT INTO user_contacts (identifier, name, number) VALUES (@identifier, @name, @number)', + { + ['@identifier'] = xPlayer.identifier, + ['@name'] = contactName, + ['@number'] = phoneNumber + }, function(rowsChanged) + TriggerClientEvent('esx:showNotification', _source, _U('contact_added')) + TriggerClientEvent('esx_phone:addContact', _source, contactName, phoneNumber) + end) + + end + end + else + TriggerClientEvent('esx:showNotification', source, _U('number_not_assigned')) + end end) RegisterServerEvent('esx_phone:removePlayerContact') AddEventHandler('esx_phone:removePlayerContact', function(phoneNumber, contactName) - local _source = source local xPlayer = ESX.GetPlayerFromId(_source) local foundNumber = false - local foundPlayer = nil MySQL.Async.fetchAll( 'SELECT phone_number FROM users WHERE phone_number = @number', From 53e5a1d5e5e9e573a93a3d5f1dd16561353dc877 Mon Sep 17 00:00:00 2001 From: ElPumpo Date: Sat, 5 May 2018 14:59:55 +0200 Subject: [PATCH 53/76] Workaround for cannot add players --- server/main.lua | 84 +++++++++++++++++++++---------------------------- 1 file changed, 36 insertions(+), 48 deletions(-) diff --git a/server/main.lua b/server/main.lua index 0c1a3663..e09f17ba 100644 --- a/server/main.lua +++ b/server/main.lua @@ -227,10 +227,9 @@ end) RegisterServerEvent('esx_phone:addPlayerContact') AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) - local _source = source - local xPlayer = ESX.GetPlayerFromId(_source) - local foundNumber = false - phoneNumber = tonumber(phoneNumber) + local _source = source + local xPlayer = ESX.GetPlayerFromId(_source) + phoneNumber = tonumber(phoneNumber) -- is the player trying to enter something else into the database? if phoneNumber == nil then @@ -244,52 +243,41 @@ AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) ['@number'] = phoneNumber }, function(result) if result[1] ~= nil then - foundNumber = true + if phoneNumber == xPlayer.get('phoneNumber') then + TriggerClientEvent('esx:showNotification', _source, _U('cannot_add_self')) + else + local contacts = xPlayer.get('contacts') + + for i=1, #contacts, 1 do + if contacts[i].number == phoneNumber then + TriggerClientEvent('esx:showNotification', _source, _U('number_in_contacts')) + return + end + end + + table.insert(contacts, { + name = contactName, + number = phoneNumber, + }) + + xPlayer.set('contacts', contacts) + + MySQL.Async.execute( + 'INSERT INTO user_contacts (identifier, name, number) VALUES (@identifier, @name, @number)', + { + ['@identifier'] = xPlayer.identifier, + ['@name'] = contactName, + ['@number'] = phoneNumber + }, function(rowsChanged) + TriggerClientEvent('esx:showNotification', _source, _U('contact_added')) + TriggerClientEvent('esx_phone:addContact', _source, contactName, phoneNumber) + end) + end + -- there's a bug with mysql-async where some statements will break everything... + --else + --TriggerClientEvent('esx:showNotification', _source, _U('number_not_assigned')) end end) - - if foundNumber then - if phoneNumber == xPlayer.get('phoneNumber') then - TriggerClientEvent('esx:showNotification', _source, _U('cannot_add_self')) - else - local hasAlreadyAdded = false - local contacts = xPlayer.get('contacts') - - for i=1, #contacts, 1 do - if contacts[i].number == phoneNumber then - hasAlreadyAdded = true - break - end - end - - if hasAlreadyAdded then - TriggerClientEvent('esx:showNotification', _source, _U('number_in_contacts')) - else - - table.insert(contacts, { - name = contactName, - number = phoneNumber, - }) - - xPlayer.set('contacts', contacts) - - MySQL.Async.execute( - 'INSERT INTO user_contacts (identifier, name, number) VALUES (@identifier, @name, @number)', - { - ['@identifier'] = xPlayer.identifier, - ['@name'] = contactName, - ['@number'] = phoneNumber - }, function(rowsChanged) - TriggerClientEvent('esx:showNotification', _source, _U('contact_added')) - TriggerClientEvent('esx_phone:addContact', _source, contactName, phoneNumber) - end) - - end - end - else - TriggerClientEvent('esx:showNotification', source, _U('number_not_assigned')) - end - end) RegisterServerEvent('esx_phone:removePlayerContact') From 840fa459271264063297ef83cc7d005a6372c14f Mon Sep 17 00:00:00 2001 From: ElPumpo Date: Sun, 22 Jul 2018 14:27:53 +0200 Subject: [PATCH 54/76] Implemented esx_phone:cancelMessage for canceling dispatch messages --- .editorconfig | 4 +- __resource.lua | 6 + client/main.lua | 54 ++++---- esx_phone.sql | 4 +- html/scripts/app.js | 8 +- server/main.lua | 323 ++++++++++++++++++++------------------------ 6 files changed, 188 insertions(+), 211 deletions(-) diff --git a/.editorconfig b/.editorconfig index 41c2aaf2..6866fb40 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,8 +1,8 @@ root = true [*] -indent_size = 2 -indent_style = space +indent_size = 4 +indent_style = tab end_of_line = lf charset = utf-8 trim_trailing_whitespace = true diff --git a/__resource.lua b/__resource.lua index 8069fbc4..b2a39070 100644 --- a/__resource.lua +++ b/__resource.lua @@ -33,11 +33,15 @@ ui_page 'html/ui.html' files { 'html/ui.html', + 'html/bankgothic.ttf', 'html/pdown.ttf', + 'html/css/app.css', + 'html/scripts/mustache.min.js', 'html/scripts/app.js', + 'html/img/cursor.png', 'html/img/keys/enter.png', 'html/img/keys/return.png', @@ -53,3 +57,5 @@ files { 'html/img/icons/edit.png', 'html/img/icons/location.png' } + +dependency 'es_extended' \ No newline at end of file diff --git a/client/main.lua b/client/main.lua index 1b658e73..991e44dd 100644 --- a/client/main.lua +++ b/client/main.lua @@ -10,21 +10,18 @@ local Keys = { ["NENTER"] = 201, ["N4"] = 108, ["N5"] = 60, ["N6"] = 107, ["N+"] = 96, ["N-"] = 97, ["N7"] = 117, ["N8"] = 61, ["N9"] = 118 } -local GUI = {} -local PhoneData = { phoneNumber = 0, contacts = {} } -local RegisteredMessageCallbacks = {} -local ContactJustAdded = false -local CurrentAction = nil -local CurrentActionMsg = '' -local CurrentActionData = {} -local CurrentDispatchRequestId = -1 -local PhoneNumberSources = {} +local GUI = {} +local PhoneData = { phoneNumber = 0, contacts = {} } +local CurrentAction = nil +local CurrentActionMsg = '' +local CurrentActionData = {} +local CurrentDispatchRequestId = -1 +local PhoneNumberSources = {} -ESX = nil -GUI.Time = 0 -GUI.PhoneIsShowed = false -GUI.MessagesIsShowed = false -GUI.AddContactIsShowed = false +ESX = nil +GUI.PhoneIsShowed = false +GUI.MessagesIsShowed = false +GUI.AddContactIsShowed = false Citizen.CreateThread(function() while ESX == nil do @@ -36,7 +33,7 @@ Citizen.CreateThread(function() end) function OpenPhone() - local playerPed = GetPlayerPed(-1) + local playerPed = PlayerPedId() TriggerServerEvent('esx_phone:reload', PhoneData.phoneNumber) @@ -51,13 +48,13 @@ function OpenPhone() SetNuiFocus(true, true) end) - if not IsPedInAnyVehicle(playerPed, false) then - TaskStartScenarioInPlace(playerPed, "WORLD_HUMAN_STAND_MOBILE", 0, true); + if not IsPedInAnyVehicle(playerPed, false) then + TaskStartScenarioInPlace(playerPed, 'WORLD_HUMAN_STAND_MOBILE', 0, true) end end function ClosePhone() - local playerPed = GetPlayerPed(-1) + local playerPed = PlayerPedId() SendNUIMessage({ showPhone = false @@ -102,7 +99,7 @@ RegisterNetEvent('esx_phone:removeContact') AddEventHandler('esx_phone:removeContact', function(name, phoneNumber) for key, value in pairs(PhoneData.contacts) do if value.name == name and value.number == phoneNumber then - table.remove(PhoneData.contacts,key) + table.remove(PhoneData.contacts, key) break end end @@ -152,7 +149,16 @@ RegisterNUICallback('remove_contact', function(data, cb) end) RegisterNetEvent('esx_phone:onMessage') -AddEventHandler('esx_phone:onMessage', function(phoneNumber, message, position, anon, job, dispatchRequestId) +AddEventHandler('esx_phone:onMessage', function(phoneNumber, message, position, anon, job, dispatchRequestId, dispatchNumber) + + if dispatchNumber then + TriggerEvent('esx_phone:cancelMessage', dispatchNumber) + + if WasEventCanceled() then + return + end + end + if job == 'player' then ESX.ShowNotification(_U('new_message', message)) else @@ -212,7 +218,7 @@ end) RegisterNUICallback('send', function(data) local phoneNumber = data.number - local playerPed = GetPlayerPed(-1) + local playerPed = PlayerPedId() local coords = GetEntityCoords(playerPed) if tonumber(phoneNumber) ~= nil then @@ -262,12 +268,11 @@ Citizen.CreateThread(function() else -- open phone -- todo: is player busy (handcuffed, etc) - if IsControlPressed(0, Keys['F1']) and GetLastInputMethod(2) and (GetGameTimer() - GUI.Time) > 150 then + if IsControlJustReleased(0, Keys['F1']) and GetLastInputMethod(2) then if not ESX.UI.Menu.IsOpen('phone', GetCurrentResourceName(), 'main') then ESX.UI.Menu.CloseAll() ESX.UI.Menu.Open('phone', GetCurrentResourceName(), 'main') end - GUI.Time = GetGameTimer() end end end @@ -284,14 +289,13 @@ Citizen.CreateThread(function() AddTextComponentString(CurrentActionMsg) DisplayHelpTextFromStringLabel(0, 0, 1, -1) - if IsControlPressed(0, Keys['E']) and GetLastInputMethod(2) and (GetGameTimer() - GUI.Time) > 300 then + if IsControlJustReleased(0, Keys['E']) and GetLastInputMethod(2) then if CurrentAction == 'dispatch' then TriggerServerEvent('esx_phone:stopDispatch', CurrentDispatchRequestId) SetNewWaypoint(CurrentActionData.position.x, CurrentActionData.position.y) end CurrentAction = nil - GUI.Time = GetGameTimer() end end diff --git a/esx_phone.sql b/esx_phone.sql index 0a47211e..76da439f 100644 --- a/esx_phone.sql +++ b/esx_phone.sql @@ -9,6 +9,4 @@ CREATE TABLE `user_contacts` ( PRIMARY KEY (`id`) ); -ALTER TABLE `users` - ADD COLUMN `phone_number` INT NULL -; +ALTER TABLE `users`ADD COLUMN `phone_number` INT NULL; \ No newline at end of file diff --git a/html/scripts/app.js b/html/scripts/app.js index fd5c4835..151df962 100644 --- a/html/scripts/app.js +++ b/html/scripts/app.js @@ -50,12 +50,12 @@ $('#repertoire').removeClass('active'); } - let showMessages = function(){ + let showMessages = function(){ $('#messages').addClass('active'); isMessagesOpen = true; } - let hideMessages = function(){ + let hideMessages = function(){ $('#messages').removeClass('active'); isMessagesOpen = false; } @@ -80,7 +80,7 @@ $('.screen.active *').removeAttr('disabled'); } - let hideNewMessage = function() { + let hideNewMessage = function() { $('#writer').removeClass('active'); $('#writer_number').val(''); $('#writer_message').val(''); @@ -167,7 +167,7 @@ isPhoneShowed = true; } - let hidePhone = function(){ + let hidePhone = function(){ $('#phone').hide(); isPhoneShowed = false; } diff --git a/server/main.lua b/server/main.lua index e09f17ba..3a8e7745 100644 --- a/server/main.lua +++ b/server/main.lua @@ -7,200 +7,173 @@ TriggerEvent('esx:getSharedObject', function(obj) end) function GenerateUniquePhoneNumber() + local foundNumber = false + local phoneNumber = nil - local foundNumber = false - local phoneNumber = nil + while not foundNumber do + Citizen.Wait(20) + phoneNumber = math.random(10000, 99999) - while not foundNumber do + local result = MySQL.Sync.fetchAll('SELECT COUNT(*) as count FROM users WHERE phone_number = @phoneNumber', + { + ['@phoneNumber'] = phoneNumber + }) - phoneNumber = math.random(10000, 99999) + local count = tonumber(result[1].count) - local result = MySQL.Sync.fetchAll( - 'SELECT COUNT(*) as count FROM users WHERE phone_number = @phoneNumber', - { - ['@phoneNumber'] = phoneNumber - } - ) + -- if the result is empty, aka the phone number is available + if count == 0 then + foundNumber = true + end - local count = tonumber(result[1].count) + end - if count == 0 then - foundNumber = true - end - - end - - return phoneNumber + return phoneNumber end function GetDistpatchRequestId() + local requestId = DisptachRequestId - local requestId = DisptachRequestId - - if DisptachRequestId < 65535 then - DisptachRequestId = DisptachRequestId + 1 - else - DisptachRequestId = 0 - end - - return requestId + if DisptachRequestId < 65535 then + DisptachRequestId = DisptachRequestId + 1 + else + DisptachRequestId = 0 + end + return requestId end AddEventHandler('esx_phone:getDistpatchRequestId', function(cb) - cb(GetDistpatchRequestId()) + cb(GetDistpatchRequestId()) end) AddEventHandler('esx:playerLoaded', function(source) + local xPlayer = ESX.GetPlayerFromId(source) - local xPlayer = ESX.GetPlayerFromId(source) + for num,v in pairs(PhoneNumbers) do + if tonumber(num) == num then -- If phonenumber is a player phone number + for src,_ in pairs(v.sources) do + TriggerClientEvent('esx_phone:setPhoneNumberSource', source, num, tonumber(src)) + end + end + end - for num,v in pairs(PhoneNumbers) do - if tonumber(num) == num then -- If phonenumber is a player phone number - for src,_ in pairs(v.sources) do - TriggerClientEvent('esx_phone:setPhoneNumberSource', source, num, tonumber(src)) - end - end - end + MySQL.Async.fetchAll('SELECT * FROM users WHERE identifier = @identifier', + { + ['@identifier'] = xPlayer.identifier + }, function(result) + local phoneNumber = result[1].phone_number - MySQL.Async.fetchAll( - 'SELECT * FROM users WHERE identifier = @identifier', - { - ['@identifier'] = xPlayer.identifier - }, - function(result) + if phoneNumber == nil then + phoneNumber = GenerateUniquePhoneNumber() - local phoneNumber = result[1].phone_number + MySQL.Async.execute('UPDATE users SET phone_number = @phone_number WHERE identifier = @identifier', + { + ['@identifier'] = xPlayer.identifier, + ['@phone_number'] = phoneNumber + }) + end - if phoneNumber == nil then + TriggerClientEvent('esx_phone:setPhoneNumberSource', -1, phoneNumber, source) - phoneNumber = GenerateUniquePhoneNumber() + PhoneNumbers[phoneNumber] = { + type = 'player', + hashDispatch = false, + sharePos = false, + hideNumber = false, + hidePosIfAnon = false, + sources = {[source] = true} + } - MySQL.Async.execute( - 'UPDATE users SET phone_number = @phone_number WHERE identifier = @identifier', - { - ['@identifier'] = xPlayer.identifier, - ['@phone_number'] = phoneNumber - } - ) - end + xPlayer.set('phoneNumber', phoneNumber) - TriggerClientEvent('esx_phone:setPhoneNumberSource', -1, phoneNumber, source) + if PhoneNumbers[xPlayer.job.name] ~= nil then + TriggerEvent('esx_phone:addSource', xPlayer.job.name, source) + end - PhoneNumbers[phoneNumber] = { - type = 'player', - hashDispatch = false, - sharePos = false, - hideNumber = false, - hidePosIfAnon = false, - sources = {[source] = true} - } + local contacts = {} - xPlayer.set('phoneNumber', phoneNumber) + MySQL.Async.fetchAll('SELECT * FROM user_contacts WHERE identifier = @identifier ORDER BY name ASC', + { + ['@identifier'] = xPlayer.identifier + }, function(result2) - if PhoneNumbers[xPlayer.job.name] ~= nil then - TriggerEvent('esx_phone:addSource', xPlayer.job.name, source) - end + for i=1, #result2, 1 do - local contacts = {} + table.insert(contacts, { + name = result2[i].name, + number = result2[i].number, + }) + end - MySQL.Async.fetchAll( - 'SELECT * FROM user_contacts WHERE identifier = @identifier ORDER BY name ASC', - { - ['@identifier'] = xPlayer.identifier - }, - function(result2) - - for i=1, #result2, 1 do - - table.insert(contacts, { - name = result2[i].name, - number = result2[i].number, - }) - end - - xPlayer.set('contacts', contacts) - - TriggerClientEvent('esx_phone:loaded', source, phoneNumber, contacts) - - end - ) - - end - ) + xPlayer.set('contacts', contacts) + TriggerClientEvent('esx_phone:loaded', source, phoneNumber, contacts) + end) + end) end) AddEventHandler('esx:playerDropped', function(source) + local xPlayer = ESX.GetPlayerFromId(source) - local xPlayer = ESX.GetPlayerFromId(source) - - TriggerClientEvent('esx_phone:setPhoneNumberSource', -1, xPlayer.get('phoneNumber'), -1) - - PhoneNumbers[xPlayer.get('phoneNumber')] = nil - - if PhoneNumbers[xPlayer.job.name] ~= nil then - TriggerEvent('esx_phone:removeSource', xPlayer.job.name, source) - end + TriggerClientEvent('esx_phone:setPhoneNumberSource', -1, xPlayer.get('phoneNumber'), -1) + PhoneNumbers[xPlayer.get('phoneNumber')] = nil + if PhoneNumbers[xPlayer.job.name] ~= nil then + TriggerEvent('esx_phone:removeSource', xPlayer.job.name, source) + end end) AddEventHandler('esx:setJob', function(source, job, lastJob) + if PhoneNumbers[lastJob.name] ~= nil then + TriggerEvent('esx_phone:removeSource', lastJob.name, source) + end - if PhoneNumbers[lastJob.name] ~= nil then - TriggerEvent('esx_phone:removeSource', lastJob.name, source) - end - - if PhoneNumbers[job.name] ~= nil then - TriggerEvent('esx_phone:addSource', job.name, source) - end - + if PhoneNumbers[job.name] ~= nil then + TriggerEvent('esx_phone:addSource', job.name, source) + end end) RegisterServerEvent('esx_phone:reload') AddEventHandler('esx_phone:reload', function(phoneNumber) + local _source = source + local xPlayer = ESX.GetPlayerFromId(_source) + local contacts = xPlayer.get('contacts') - local _source = source - local xPlayer = ESX.GetPlayerFromId(_source) - local contacts = xPlayer.get('contacts') - - TriggerClientEvent('esx_phone:loaded', _source, phoneNumber, contacts) - + TriggerClientEvent('esx_phone:loaded', _source, phoneNumber, contacts) end) RegisterServerEvent('esx_phone:send') AddEventHandler('esx_phone:send', function(phoneNumber, message, anon, position) + local _source = source + local xPlayer = ESX.GetPlayerFromId(_source) - local _source = source - local xPlayer = ESX.GetPlayerFromId(_source) + print('MESSAGE => ' .. xPlayer.name .. '@' .. phoneNumber .. ' : ' .. message) - print('MESSAGE => ' .. xPlayer.name .. '@' .. phoneNumber .. ' : ' .. message) + if PhoneNumbers[phoneNumber] ~= nil then - if PhoneNumbers[phoneNumber] ~= nil then + for k,v in pairs(PhoneNumbers[phoneNumber].sources) do - for k,v in pairs(PhoneNumbers[phoneNumber].sources) do + local numType = PhoneNumbers[phoneNumber].type + local numHasDispatch = PhoneNumbers[phoneNumber].hasDispatch + local numHide = PhoneNumbers[phoneNumber].hideNumber + local numHidePosIfAnon = PhoneNumbers[phoneNumber].hidePosIfAnon + local numPosition = (PhoneNumbers[phoneNumber].sharePos and position or false) + local numSource = tonumber(k) - local numType = PhoneNumbers[phoneNumber].type - local numHasDispatch = PhoneNumbers[phoneNumber].hasDispatch - local numHide = PhoneNumbers[phoneNumber].hideNumber - local numHidePosIfAnon = PhoneNumbers[phoneNumber].hidePosIfAnon - local numPosition = (PhoneNumbers[phoneNumber].sharePos and position or false) - local numSource = tonumber(k) + if numHidePosIfAnon and anon then + numPosition = false + end - if numHidePosIfAnon and anon then - numPosition = false - end + if numHasDispatch then + TriggerClientEvent('esx_phone:onMessage', numSource, xPlayer.get('phoneNumber'), message, numPosition, (numHide and true or anon), numType, GetDistpatchRequestId(), phoneNumber) + else + TriggerClientEvent('esx_phone:onMessage', numSource, xPlayer.get('phoneNumber'), message, numPosition, (numHide and true or anon), numType, false) + end - if numHasDispatch then - TriggerClientEvent('esx_phone:onMessage', numSource, xPlayer.get('phoneNumber'), message, numPosition, (numHide and true or anon), numType, GetDistpatchRequestId()) - else - TriggerClientEvent('esx_phone:onMessage', numSource, xPlayer.get('phoneNumber'), message, numPosition, (numHide and true or anon), numType, false) - end - - end - - end + end + end end) AddEventHandler('esx_phone:registerNumber', function(number, type, sharePos, hasDispatch, hideNumber, hidePosIfAnon) @@ -217,6 +190,10 @@ AddEventHandler('esx_phone:registerNumber', function(number, type, sharePos, has } end) +AddEventHandler('esx_phone:removeNumber', function(number) + PhoneNumbers[number] = nil +end) + AddEventHandler('esx_phone:addSource', function(number, source) PhoneNumbers[number].sources[tostring(source)] = true end) @@ -282,57 +259,49 @@ end) RegisterServerEvent('esx_phone:removePlayerContact') AddEventHandler('esx_phone:removePlayerContact', function(phoneNumber, contactName) - local _source = source - local xPlayer = ESX.GetPlayerFromId(_source) - local foundNumber = false + local _source = source + local xPlayer = ESX.GetPlayerFromId(_source) + local foundNumber = false - MySQL.Async.fetchAll( - 'SELECT phone_number FROM users WHERE phone_number = @number', - { - ['@number'] = phoneNumber - }, - function(result) + MySQL.Async.fetchAll('SELECT phone_number FROM users WHERE phone_number = @number', + { + ['@number'] = phoneNumber + }, function(result) - if result[1] ~= nil then - foundNumber = true - end + if result[1] ~= nil then + foundNumber = true + end - if foundNumber then + if foundNumber then - local contacts = xPlayer.get('contacts') + local contacts = xPlayer.get('contacts') - for key, value in pairs(contacts) do - if value.name == contactName and value.number == phoneNumber then - table.remove(contacts,key) - end - end + for key, value in pairs(contacts) do + if value.name == contactName and value.number == phoneNumber then + table.remove(contacts, key) + end + end - xPlayer.set('contacts', contacts) + xPlayer.set('contacts', contacts) - MySQL.Async.execute( - 'DELETE FROM user_contacts WHERE identifier=@identifier AND name=@name AND number=@number', - { - ['@identifier'] = xPlayer.identifier, - ['@name'] = contactName, - ['@number'] = phoneNumber - }, - function(rowsChanged) + MySQL.Async.execute('DELETE FROM user_contacts WHERE identifier=@identifier AND name=@name AND number=@number', + { + ['@identifier'] = xPlayer.identifier, + ['@name'] = contactName, + ['@number'] = phoneNumber + }, function(rowsChanged) + TriggerClientEvent('esx:showNotification', _source, _U('contact_removed')) + TriggerClientEvent('esx_phone:removeContact', _source, contactName, phoneNumber) + end) + else + TriggerClientEvent('esx:showNotification', source, _U('number_not_assigned')) + end - TriggerClientEvent('esx:showNotification', _source, _U('contact_removed')) - - TriggerClientEvent('esx_phone:removeContact', _source, contactName, phoneNumber) - end - ) - else - TriggerClientEvent('esx:showNotification', source, _U('number_not_assigned')) - end - - end - ) + end) end) RegisterServerEvent('esx_phone:stopDispatch') AddEventHandler('esx_phone:stopDispatch', function(dispatchRequestId) - TriggerClientEvent('esx_phone:stopDispatch', -1, dispatchRequestId, GetPlayerName(source)) + TriggerClientEvent('esx_phone:stopDispatch', -1, dispatchRequestId, GetPlayerName(source)) end) From ff62e87e0ee246929cea6bef7302e06537769a36 Mon Sep 17 00:00:00 2001 From: deviljin112 <39461646+deviljin112@users.noreply.github.com> Date: Mon, 23 Jul 2018 17:17:25 +0100 Subject: [PATCH 55/76] Little Fix up + Translation PL Fixed up other app.js missing delete Contact, moved the phone slightly more to the right of the screen, Added Polish Translation. --- __resource.lua | 2 + html/css/app.css | 2 +- html/scripts/app_de.js | 19 +- html/scripts/app_en.js | 21 +- html/scripts/app_pl.js | 512 +++++++++++++++++++++++++++++++++++++++++ html/ui_pl.html | 82 +++++++ locales/pl.lua | 13 ++ 7 files changed, 647 insertions(+), 4 deletions(-) create mode 100644 html/scripts/app_pl.js create mode 100644 html/ui_pl.html create mode 100644 locales/pl.lua diff --git a/__resource.lua b/__resource.lua index b2a39070..f7a49666 100644 --- a/__resource.lua +++ b/__resource.lua @@ -14,6 +14,7 @@ server_scripts { 'locales/fr.lua', 'locales/es.lua', 'locales/sv.lua', + 'locales/pl.lua', 'server/main.lua' } @@ -26,6 +27,7 @@ client_scripts { 'locales/fr.lua', 'locales/es.lua', 'locales/sv.lua', + 'locales/pl.lua', 'client/main.lua' } diff --git a/html/css/app.css b/html/css/app.css index 061b08e5..d02ccc9c 100644 --- a/html/css/app.css +++ b/html/css/app.css @@ -33,7 +33,7 @@ html, body { height: 480px; color: #212121; position: absolute; - right: 300px; + right: 50px; bottom: 0; overflow: hidden; cursor: default; diff --git a/html/scripts/app_de.js b/html/scripts/app_de.js index fa6bb6a7..5643ea02 100644 --- a/html/scripts/app_de.js +++ b/html/scripts/app_de.js @@ -7,7 +7,10 @@ '{{sender}}
    #{{phoneNumber}}' + '
    ' + '
    ' + - '
    ' + + '
    ' + + 'X' + + '' + + '
    ' + '
    ' ; @@ -120,6 +123,16 @@ $('#phone #repertoire .repertoire-list').html(contactHTML); + $('.contact .del-contact').click(function() { + let name = $(this).attr('data-contact-name'); + let phoneNumber = $(this).attr('data-contact-number'); + + $.post('http://esx_phone/remove_contact', JSON.stringify({ + contactName: name, + phoneNumber: phoneNumber + })) + }); + $('.contact.online .new-msg').click(function() { showNewMessage($(this).attr('data-contact-number'), $(this).attr('data-contact-name')); }); @@ -451,6 +464,10 @@ hideAddContact(); } + if(data.contactRemoved === true){ + reloadPhone(data.phoneData); + } + if(data.addSpecialContact === true){ addSpecialContact(data.name, data.number, data.base64Icon); } diff --git a/html/scripts/app_en.js b/html/scripts/app_en.js index 36515331..21a3caf0 100644 --- a/html/scripts/app_en.js +++ b/html/scripts/app_en.js @@ -7,7 +7,10 @@ '{{sender}}
    #{{phoneNumber}}' + '' + '' + - '
    ' + + '
    ' + + 'X' + + '' + + '
    ' + '' ; @@ -119,6 +122,16 @@ } $('#phone #repertoire .repertoire-list').html(contactHTML); + + $('.contact .del-contact').click(function() { + let name = $(this).attr('data-contact-name'); + let phoneNumber = $(this).attr('data-contact-number'); + + $.post('http://esx_phone/remove_contact', JSON.stringify({ + contactName: name, + phoneNumber: phoneNumber + })) + }); $('.contact.online .new-msg').click(function() { showNewMessage($(this).attr('data-contact-number'), $(this).attr('data-contact-name')); @@ -383,7 +396,7 @@ }); $('#btn-head-new-message').click(function() { - showNewMessage('', 'Nouveau message'); + showNewMessage('', 'New message'); }); $('#btn-head-new-contact').click(function() { @@ -451,6 +464,10 @@ hideAddContact(); } + if(data.contactRemoved === true){ + reloadPhone(data.phoneData); + } + if(data.addSpecialContact === true){ addSpecialContact(data.name, data.number, data.base64Icon); } diff --git a/html/scripts/app_pl.js b/html/scripts/app_pl.js new file mode 100644 index 00000000..a29efe00 --- /dev/null +++ b/html/scripts/app_pl.js @@ -0,0 +1,512 @@ +(function(){ + + let ContactTpl = + '
    ' + + '
    ' + + '
    ' + + '{{sender}}
    #{{phoneNumber}}' + + '
    ' + + '
    ' + + '
    ' + + 'X' + + '' + + '
    ' + + '
    ' + ; + + let MessageTpl = + '
    ' + + '
    ' + + '
    ' + + '{{sender}}
    #{{phoneNumber}}' + + '
    ' + + '
    ' + + '
    {{message}}
    ' + + '
    ' + + '
    ' + ; + + let SpecialContactTpl = '
  • {{name}}
  • '; + + let contacts = []; + let specialContacts = []; + let menu = []; + let currentItem = 0; + let currentVal = null; + let isMessageEditorOpen = false; + let isMessagesOpen = false; + let isPhoneShowed = false; + + let showMain = function() { + $('.screen').removeClass('active'); + $('.screen *').attr('disabled', 'disabled'); + } + + let showRepertoire = function() { + $('#repertoire').addClass('active'); + } + + let hideRepertoire = function() { + $('#repertoire').removeClass('active'); + } + + let showMessages = function(){ + $('#messages').addClass('active'); + isMessagesOpen = true; + } + + let hideMessages = function(){ + $('#messages').removeClass('active'); + isMessagesOpen = false; + } + + let showAddContact = function() { + $('#contact').addClass('active'); + $('.screen *').attr('disabled', 'disabled'); + $('.screen.active *').removeAttr('disabled'); + } + + let hideAddContact = function() { + $('#contact').removeClass('active'); + $('#contact_name').val(''); + $('#contact_number').val(''); + } + + let showNewMessage = function(cnum, cname) { + $('#writer').addClass('active'); + $('#writer_number').val(cnum); + $('#writer .header-title').html(cname); + $('.screen *').attr('disabled', 'disabled'); + $('.screen.active *').removeAttr('disabled'); + } + + let hideNewMessage = function() { + $('#writer').removeClass('active'); + $('#writer_number').val(''); + $('#writer_message').val(''); + $('#writer .header-title').html(''); + } + + let showGPS = function(xPos, yPos) { + $.post('http://esx_phone/setGPS', JSON.stringify({ + x: parseFloat(xPos), + y: parseFloat(yPos) + } + )); + } + + let renderContacts = function(){ + + let contactHTML = ''; + + if(contacts.length > 0) { + + for(let i=0; i 0) { + + for(let i=0; i { + return a.name.localeCompare(b.name); + }); + + renderSpecialContacts(); + + } + + } + + let removeSpecialContact = function(number){ + + for(let i=0; iKontakty' + + '
  • Wiadomości
  • ' + ); + + for(let i=0; i + + + + + + + +
    + +
    + +
    + + #123456 +
    + + + +
    + +
    Kontakty
    +
    +
    +
    +

    Brak kontaktów

    +
    +
    +
    +
    + +
    +
    Wiadomości
    +
    +
    +
    +

    Brak wiadomości

    +
    +
    +
    +
    + +
    +
    +
    +
    + + + + + +
    +
    +
    + +
    +
    Dodaj kontakt
    +
    +
    + + + + +
    +
    +
    + +
    +
    + + + + + + + + diff --git a/locales/pl.lua b/locales/pl.lua new file mode 100644 index 00000000..c658c8be --- /dev/null +++ b/locales/pl.lua @@ -0,0 +1,13 @@ +Locales['en'] = { + ['new_message'] = '~b~Masz nową wiadomość:~s~ %s', + ['press_take_call'] = '%s - Wciśnij ~INPUT_CONTEXT~ aby odebrać telefon', + ['taken_call'] = '~y~%s~s~ odebrał telefon', + ['gps_position'] = 'Cel podróży został dodany do GPS\'a', + ['message_sent'] = 'Wiadomość wysłana', + ['cannot_add_self'] = 'Nie możesz dodać samego siebie!', + ['number_in_contacts'] = 'Ten numer jest już na twojej liście kontaktów', + ['contact_added'] = 'Kontakt dodany!', + ['contact_removed'] = 'Kontakt usunięty!', + ['number_not_assigned'] = 'Numer nie jest przypisany!', + ['invalid_number'] = 'To nie jest prawidłowy numer telefonu!', +} \ No newline at end of file From edb4df88b9a53ebe123712727d9980c429d84d31 Mon Sep 17 00:00:00 2001 From: deviljin112 <39461646+deviljin112@users.noreply.github.com> Date: Mon, 23 Jul 2018 18:03:52 +0100 Subject: [PATCH 56/76] Update pl.lua lower case change --- locales/pl.lua | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/locales/pl.lua b/locales/pl.lua index c658c8be..6e95c436 100644 --- a/locales/pl.lua +++ b/locales/pl.lua @@ -1,13 +1,13 @@ Locales['en'] = { - ['new_message'] = '~b~Masz nową wiadomość:~s~ %s', - ['press_take_call'] = '%s - Wciśnij ~INPUT_CONTEXT~ aby odebrać telefon', + ['new_message'] = '~b~masz nową wiadomość:~s~ %s', + ['press_take_call'] = '%s - wciśnij ~INPUT_CONTEXT~ aby odebrać telefon', ['taken_call'] = '~y~%s~s~ odebrał telefon', - ['gps_position'] = 'Cel podróży został dodany do GPS\'a', - ['message_sent'] = 'Wiadomość wysłana', - ['cannot_add_self'] = 'Nie możesz dodać samego siebie!', - ['number_in_contacts'] = 'Ten numer jest już na twojej liście kontaktów', - ['contact_added'] = 'Kontakt dodany!', - ['contact_removed'] = 'Kontakt usunięty!', - ['number_not_assigned'] = 'Numer nie jest przypisany!', - ['invalid_number'] = 'To nie jest prawidłowy numer telefonu!', -} \ No newline at end of file + ['gps_position'] = 'cel podróży został dodany do GPS\'a', + ['message_sent'] = 'wiadomość wysłana', + ['cannot_add_self'] = 'nie możesz dodać samego siebie!', + ['number_in_contacts'] = 'ten numer jest już na twojej liście kontaktów', + ['contact_added'] = 'kontakt dodany!', + ['contact_removed'] = 'kontakt usunięty!', + ['number_not_assigned'] = 'numer nie jest przypisany!', + ['invalid_number'] = 'to nie jest prawidłowy numer telefonu!', +} From d9c74a7dd0eeceff6b8c208e9636871c728762c9 Mon Sep 17 00:00:00 2001 From: deviljin112 <39461646+deviljin112@users.noreply.github.com> Date: Mon, 23 Jul 2018 19:32:55 +0100 Subject: [PATCH 57/76] Update pl.lua Fixed small error --- locales/pl.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/pl.lua b/locales/pl.lua index 6e95c436..ca85a4c2 100644 --- a/locales/pl.lua +++ b/locales/pl.lua @@ -1,4 +1,4 @@ -Locales['en'] = { +Locales['pl'] = { ['new_message'] = '~b~masz nową wiadomość:~s~ %s', ['press_take_call'] = '%s - wciśnij ~INPUT_CONTEXT~ aby odebrać telefon', ['taken_call'] = '~y~%s~s~ odebrał telefon', From bb44db07ecef3ad85f0daf148adfa3c96ec038d5 Mon Sep 17 00:00:00 2001 From: deviljin112 <39461646+deviljin112@users.noreply.github.com> Date: Tue, 24 Jul 2018 13:26:10 +0100 Subject: [PATCH 58/76] Update pl.lua Capital first line --- locales/pl.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/pl.lua b/locales/pl.lua index ca85a4c2..b3218307 100644 --- a/locales/pl.lua +++ b/locales/pl.lua @@ -1,5 +1,5 @@ Locales['pl'] = { - ['new_message'] = '~b~masz nową wiadomość:~s~ %s', + ['new_message'] = '~b~Masz nową wiadomość:~s~ %s', ['press_take_call'] = '%s - wciśnij ~INPUT_CONTEXT~ aby odebrać telefon', ['taken_call'] = '~y~%s~s~ odebrał telefon', ['gps_position'] = 'cel podróży został dodany do GPS\'a', From d84d4e3131f2d4c20b3a66104f7467dd4364eb7d Mon Sep 17 00:00:00 2001 From: nearbyplayer Date: Fri, 10 Aug 2018 00:55:01 -0400 Subject: [PATCH 59/76] Repo Update --- .editorconfig | 2 +- LICENSE.txt => LICENSE | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename LICENSE.txt => LICENSE (100%) diff --git a/.editorconfig b/.editorconfig index 6866fb40..9b425256 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,7 +3,7 @@ root = true [*] indent_size = 4 indent_style = tab -end_of_line = lf +end_of_line = crlf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE similarity index 100% rename from LICENSE.txt rename to LICENSE From 3a87aef280c5a375bb79ee4f3b66ba6738f88f12 Mon Sep 17 00:00:00 2001 From: ElPumpo Date: Thu, 23 Aug 2018 19:57:00 +0200 Subject: [PATCH 60/76] Fixed esx_policejob/issues/144 --- client/main.lua | 3 +-- server/main.lua | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/client/main.lua b/client/main.lua index 991e44dd..e784116d 100644 --- a/client/main.lua +++ b/client/main.lua @@ -150,8 +150,7 @@ end) RegisterNetEvent('esx_phone:onMessage') AddEventHandler('esx_phone:onMessage', function(phoneNumber, message, position, anon, job, dispatchRequestId, dispatchNumber) - - if dispatchNumber then + if dispatchNumber and phoneNumber == PhoneData.phoneNumber then TriggerEvent('esx_phone:cancelMessage', dispatchNumber) if WasEventCanceled() then diff --git a/server/main.lua b/server/main.lua index 3a8e7745..2f04c503 100644 --- a/server/main.lua +++ b/server/main.lua @@ -23,7 +23,7 @@ function GenerateUniquePhoneNumber() -- if the result is empty, aka the phone number is available if count == 0 then - foundNumber = true + foundNumber = true end end From 41403673e7ea38a1d01dad8927076834e65c9533 Mon Sep 17 00:00:00 2001 From: ElPumpo Date: Sat, 22 Sep 2018 00:21:16 +0200 Subject: [PATCH 61/76] Improved randomness with generating phone number --- client/main.lua | 14 ++++++-------- server/main.lua | 40 +++++++++++----------------------------- 2 files changed, 17 insertions(+), 37 deletions(-) diff --git a/client/main.lua b/client/main.lua index e784116d..646d52d0 100644 --- a/client/main.lua +++ b/client/main.lua @@ -281,22 +281,20 @@ end) Citizen.CreateThread(function() while true do Citizen.Wait(10) - - if CurrentAction ~= nil then - SetTextComponentFormat('STRING') - AddTextComponentString(CurrentActionMsg) - DisplayHelpTextFromStringLabel(0, 0, 1, -1) + if CurrentAction ~= nil then + ESX.ShowHelpNotification(CurrentActionMsg) if IsControlJustReleased(0, Keys['E']) and GetLastInputMethod(2) then - if CurrentAction == 'dispatch' then TriggerServerEvent('esx_phone:stopDispatch', CurrentDispatchRequestId) - SetNewWaypoint(CurrentActionData.position.x, CurrentActionData.position.y) + SetNewWaypoint(CurrentActionData.position.x, CurrentActionData.position.y) end + CurrentAction = nil end - + else + Citizen.Wait(500) end end end) diff --git a/server/main.lua b/server/main.lua index 2f04c503..b16cee5b 100644 --- a/server/main.lua +++ b/server/main.lua @@ -7,25 +7,21 @@ TriggerEvent('esx:getSharedObject', function(obj) end) function GenerateUniquePhoneNumber() - local foundNumber = false - local phoneNumber = nil + local foundNumber, phoneNumber = false, nil while not foundNumber do - Citizen.Wait(20) + Citizen.Wait(100) + + math.randomseed(GetGameTimer()) phoneNumber = math.random(10000, 99999) - local result = MySQL.Sync.fetchAll('SELECT COUNT(*) as count FROM users WHERE phone_number = @phoneNumber', - { + local result = MySQL.Sync.fetchAll('SELECT COUNT(*) as count FROM users WHERE phone_number = @phoneNumber', { ['@phoneNumber'] = phoneNumber }) - local count = tonumber(result[1].count) - - -- if the result is empty, aka the phone number is available - if count == 0 then + if tonumber(result[1].count) == 0 then foundNumber = true end - end return phoneNumber @@ -58,8 +54,7 @@ AddEventHandler('esx:playerLoaded', function(source) end end - MySQL.Async.fetchAll('SELECT * FROM users WHERE identifier = @identifier', - { + MySQL.Async.fetchAll('SELECT * FROM users WHERE identifier = @identifier', { ['@identifier'] = xPlayer.identifier }, function(result) local phoneNumber = result[1].phone_number @@ -86,20 +81,16 @@ AddEventHandler('esx:playerLoaded', function(source) } xPlayer.set('phoneNumber', phoneNumber) + local contacts = {} if PhoneNumbers[xPlayer.job.name] ~= nil then TriggerEvent('esx_phone:addSource', xPlayer.job.name, source) end - local contacts = {} - - MySQL.Async.fetchAll('SELECT * FROM user_contacts WHERE identifier = @identifier ORDER BY name ASC', - { + MySQL.Async.fetchAll('SELECT * FROM user_contacts WHERE identifier = @identifier ORDER BY name ASC', { ['@identifier'] = xPlayer.identifier }, function(result2) - for i=1, #result2, 1 do - table.insert(contacts, { name = result2[i].name, number = result2[i].number, @@ -153,7 +144,6 @@ AddEventHandler('esx_phone:send', function(phoneNumber, message, anon, position) if PhoneNumbers[phoneNumber] ~= nil then for k,v in pairs(PhoneNumbers[phoneNumber].sources) do - local numType = PhoneNumbers[phoneNumber].type local numHasDispatch = PhoneNumbers[phoneNumber].hasDispatch local numHide = PhoneNumbers[phoneNumber].hideNumber @@ -170,7 +160,6 @@ AddEventHandler('esx_phone:send', function(phoneNumber, message, anon, position) else TriggerClientEvent('esx_phone:onMessage', numSource, xPlayer.get('phoneNumber'), message, numPosition, (numHide and true or anon), numType, false) end - end end @@ -214,9 +203,7 @@ AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) return end - MySQL.Async.fetchAll( - 'SELECT phone_number FROM users WHERE phone_number = @number', - { + MySQL.Async.fetchAll('SELECT phone_number FROM users WHERE phone_number = @number', { ['@number'] = phoneNumber }, function(result) if result[1] ~= nil then @@ -239,8 +226,7 @@ AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) xPlayer.set('contacts', contacts) - MySQL.Async.execute( - 'INSERT INTO user_contacts (identifier, name, number) VALUES (@identifier, @name, @number)', + MySQL.Async.execute('INSERT INTO user_contacts (identifier, name, number) VALUES (@identifier, @name, @number)', { ['@identifier'] = xPlayer.identifier, ['@name'] = contactName, @@ -267,13 +253,11 @@ AddEventHandler('esx_phone:removePlayerContact', function(phoneNumber, contactNa { ['@number'] = phoneNumber }, function(result) - if result[1] ~= nil then foundNumber = true end if foundNumber then - local contacts = xPlayer.get('contacts') for key, value in pairs(contacts) do @@ -296,9 +280,7 @@ AddEventHandler('esx_phone:removePlayerContact', function(phoneNumber, contactNa else TriggerClientEvent('esx:showNotification', source, _U('number_not_assigned')) end - end) - end) RegisterServerEvent('esx_phone:stopDispatch') From 58bbe4ebcbdec92ae74fb91caed6c243ec510066 Mon Sep 17 00:00:00 2001 From: ElPumpo Date: Tue, 30 Oct 2018 13:00:41 +0100 Subject: [PATCH 62/76] Re-added error message if number is unassigned --- server/main.lua | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/server/main.lua b/server/main.lua index b16cee5b..4ae0286e 100644 --- a/server/main.lua +++ b/server/main.lua @@ -3,7 +3,7 @@ local DisptachRequestId = 0 local PhoneNumbers = {} TriggerEvent('esx:getSharedObject', function(obj) - ESX = obj + ESX = obj end) function GenerateUniquePhoneNumber() @@ -54,7 +54,7 @@ AddEventHandler('esx:playerLoaded', function(source) end end - MySQL.Async.fetchAll('SELECT * FROM users WHERE identifier = @identifier', { + MySQL.Async.fetchAll('SELECT phone_number FROM users WHERE identifier = @identifier', { ['@identifier'] = xPlayer.identifier }, function(result) local phoneNumber = result[1].phone_number @@ -106,9 +106,10 @@ end) AddEventHandler('esx:playerDropped', function(source) local xPlayer = ESX.GetPlayerFromId(source) + local phoneNumber = xPlayer.get('phoneNumber') - TriggerClientEvent('esx_phone:setPhoneNumberSource', -1, xPlayer.get('phoneNumber'), -1) - PhoneNumbers[xPlayer.get('phoneNumber')] = nil + TriggerClientEvent('esx_phone:setPhoneNumberSource', -1, phoneNumber, -1) + PhoneNumbers[phoneNumber] = nil if PhoneNumbers[xPlayer.job.name] ~= nil then TriggerEvent('esx_phone:removeSource', xPlayer.job.name, source) @@ -236,9 +237,8 @@ AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) TriggerClientEvent('esx_phone:addContact', _source, contactName, phoneNumber) end) end - -- there's a bug with mysql-async where some statements will break everything... - --else - --TriggerClientEvent('esx:showNotification', _source, _U('number_not_assigned')) + else + TriggerClientEvent('esx:showNotification', _source, _U('number_not_assigned')) end end) end) @@ -249,8 +249,7 @@ AddEventHandler('esx_phone:removePlayerContact', function(phoneNumber, contactNa local xPlayer = ESX.GetPlayerFromId(_source) local foundNumber = false - MySQL.Async.fetchAll('SELECT phone_number FROM users WHERE phone_number = @number', - { + MySQL.Async.fetchAll('SELECT phone_number FROM users WHERE phone_number = @number', { ['@number'] = phoneNumber }, function(result) if result[1] ~= nil then From 1ef8df207ada07a4f3183d887ab704a7cd12d8ee Mon Sep 17 00:00:00 2001 From: ElPumpo Date: Tue, 30 Oct 2018 13:17:19 +0100 Subject: [PATCH 63/76] Removed unused files --- __resource.lua | 7 +- html/bankgothic.ttf | Bin 36272 -> 0 bytes html/css/app.css | 134 +++++++++++++++++++-------------------- html/img/cursor.png | Bin 272 -> 0 bytes html/img/keys/enter.png | Bin 941 -> 0 bytes html/img/keys/return.png | Bin 376 -> 0 bytes html/img/pointer.png | Bin 2639 -> 0 bytes html/pdown.ttf | Bin 151512 -> 0 bytes 8 files changed, 68 insertions(+), 73 deletions(-) delete mode 100644 html/bankgothic.ttf delete mode 100644 html/img/cursor.png delete mode 100644 html/img/keys/enter.png delete mode 100644 html/img/keys/return.png delete mode 100644 html/img/pointer.png delete mode 100644 html/pdown.ttf diff --git a/__resource.lua b/__resource.lua index f7a49666..2f2390ac 100644 --- a/__resource.lua +++ b/__resource.lua @@ -36,18 +36,13 @@ ui_page 'html/ui.html' files { 'html/ui.html', - 'html/bankgothic.ttf', - 'html/pdown.ttf', - 'html/css/app.css', 'html/scripts/mustache.min.js', 'html/scripts/app.js', - 'html/img/cursor.png', - 'html/img/keys/enter.png', - 'html/img/keys/return.png', 'html/img/phone.png', + 'html/img/icons/signal.png', 'html/img/icons/rep.png', 'html/img/icons/msg.png', diff --git a/html/bankgothic.ttf b/html/bankgothic.ttf deleted file mode 100644 index f3d8049b052a1f9cfd27557eb3675bc85ce4b9fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36272 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
    D;NyGoM}BX%_3nO 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 D`#l06 diff --git a/html/css/app.css b/html/css/app.css index d02ccc9c..b1a4a4f4 100644 --- a/html/css/app.css +++ b/html/css/app.css @@ -5,9 +5,9 @@ html, body { } #cursor { - position: absolute; - z-index: 999999; - display: none; + position: absolute; + z-index: 999999; + display: none; } #console { @@ -19,7 +19,7 @@ html, body { background-color: rgba(0, 0, 0, 0.8); color: white; padding: 5px; - overflow-y: auto; + overflow-y: auto; overflow-x: hidden; } @@ -40,13 +40,13 @@ html, body { display: none; } -#phone > .container { +#phone>.container { position: absolute; - top: 59px; - left: 46px; - width: 207px; - height: 365px; - background-repeat: no-repeat; + top: 59px; + left: 46px; + width: 207px; + height: 365px; + background-repeat: no-repeat; overflow: hidden; } @@ -54,17 +54,17 @@ html, body { position: relative; text-transform: uppercase; text-align: center; - height: 20px; - line-height: 20px; - background-color: black; + height: 20px; + line-height: 20px; + background-color: black; z-index: 5; } #phone-icon { display: block; position: absolute; - height: 12px; - line-height: 20px; + height: 12px; + line-height: 20px; top: 4px; right: 5px; } @@ -72,17 +72,17 @@ html, body { #phone-number { display: block; position: absolute; - height: 20px; - line-height: 20px; + height: 20px; + line-height: 20px; top: 0; left: 5px; color: white; } #phone .menu { - max-height: 320px; - overflow-y: scroll; - z-index: 1; + max-height: 320px; + overflow-y: scroll; + z-index: 1; } #phone .menu .home { @@ -124,8 +124,8 @@ html, body { background-image: url("../img/icons/msg.png"); } -#phone .menu::-webkit-scrollbar { - display: none; +#phone .menu::-webkit-scrollbar { + display: none; } #phone .menu .phone-icon.selected, #phone .menu .phone-icon:hover { @@ -148,7 +148,7 @@ html, body { left: 0; } -.screen > .container { +.screen>.container { position: absolute; top: 50px; left: 0; @@ -282,19 +282,19 @@ html, body { } .actions .gps { - display: none; - width: 24px; - height: 24px; - background: transparent url(../img/icons/location.png) no-repeat 50%; - cursor: pointer; + display: none; + width: 24px; + height: 24px; + background: transparent url(../img/icons/location.png) no-repeat 50%; + cursor: pointer; } .actions .ok-btn { - display: none; - width: 24px; - height: 24px; - background: transparent url(../img/icons/ok.png) no-repeat 50%; - cursor: pointer; + display: none; + width: 24px; + height: 24px; + background: transparent url(../img/icons/ok.png) no-repeat 50%; + cursor: pointer; } .actions .new-msg.anonyme, .contact .actions .new-msg { @@ -310,37 +310,37 @@ html, body { } #writer textarea { - width: 207px; - min-width: 207px; - max-width: 207px; - height: 255px; - min-height: 255px; - max-height: 255px; - background-color: #f1f1f1; - border: none; - box-shadow: 0 2px 3px 1px rgba(0, 0, 0, 0.1) inset; - padding: 5px; + width: 207px; + min-width: 207px; + max-width: 207px; + height: 255px; + min-height: 255px; + max-height: 255px; + background-color: #f1f1f1; + border: none; + box-shadow: 0 2px 3px 1px rgba(0, 0, 0, 0.1) inset; + padding: 5px; outline: none; } #writer label { - display: block; - width: 100%; - height: 30px; - line-height: 34px; - background-color: #f1f1f1; - border: none; - box-shadow: 0 2px 3px 1px rgba(0, 0, 0, 0.1) inset; - font-size: 14px; - font-weight: 600; - color: #6b6b6b; + display: block; + width: 100%; + height: 30px; + line-height: 34px; + background-color: #f1f1f1; + border: none; + box-shadow: 0 2px 3px 1px rgba(0, 0, 0, 0.1) inset; + font-size: 14px; + font-weight: 600; + color: #6b6b6b; } #writer label input { - top: 2px; - position: relative; - left: 5px; - margin-right: 10px; + top: 2px; + position: relative; + left: 5px; + margin-right: 10px; } #writer button, #contact button { @@ -379,14 +379,14 @@ html, body { } #contact input { - display: block; - width: 100%; - height: 30px; - line-height: 30px; - padding: 2px 5px; - background-color: #f1f1f1; - border: none; - border-bottom: 1px solid rgba(0, 0, 0, 0.3); - outline: none; - box-shadow: 0 2px 3px 1px rgba(0, 0, 0, 0.1) inset; + display: block; + width: 100%; + height: 30px; + line-height: 30px; + padding: 2px 5px; + background-color: #f1f1f1; + border: none; + border-bottom: 1px solid rgba(0, 0, 0, 0.3); + outline: none; + box-shadow: 0 2px 3px 1px rgba(0, 0, 0, 0.1) inset; } \ No newline at end of file diff --git a/html/img/cursor.png b/html/img/cursor.png deleted file mode 100644 index 39536f953d701b36a90dc56084bafd988371a2fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 272 zcmeAS@N?(olHy`uVBq!ia0vp^q9Dw{1|(OCFP#RYSkfJR9T^xl_H+M9WCij$3p^r= z85sBufiR<}hF1enP_o1|q9iy!t)x7$D3zfgF*C13FE6!3!9>qM&(L5k7uNxxqFzrI z#}Etut!FreS`>I(0^ikVp5f1PInc0ibrsXWO3z8Ee_BE>W<&%Ean9H1Y|@_Ma)5tI zP3vrppG@U}o4EJ`H!a-_TKCSz%v`zHX&3lrYRgJzbSuAt9 z@6PvoS_%Jm+&BGp_A+yZ?Fo&SwU@JtJ?kA`YTb#E`^&mC!8qZZWp1Mjt5em&H79@$ OWAJqKb6Mw<&;$S!<6)ox diff --git a/html/img/keys/enter.png b/html/img/keys/enter.png deleted file mode 100644 index 3eb9a1f69f58c651517329c92d28cdb1c605f165..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 941 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 diff --git a/html/pdown.ttf b/html/pdown.ttf deleted file mode 100644 index 69c76cf3f2a6bbc93f570124554fec06d7bbac25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 151512 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 From 291f6eb753a8a179d061a75885f78a88d143377f Mon Sep 17 00:00:00 2001 From: ElPumpo Date: Tue, 30 Oct 2018 13:39:07 +0100 Subject: [PATCH 64/76] Properly formatted html files --- html/ui.html | 151 +++++++++++++++++++++++++----------------------- html/ui_br.html | 151 +++++++++++++++++++++++++----------------------- html/ui_de.html | 149 ++++++++++++++++++++++++----------------------- html/ui_en.html | 149 ++++++++++++++++++++++++----------------------- 4 files changed, 310 insertions(+), 290 deletions(-) diff --git a/html/ui.html b/html/ui.html index fbf8bfa0..e9023e74 100644 --- a/html/ui.html +++ b/html/ui.html @@ -1,82 +1,87 @@ - - - - - - -
    - -
    - -
    - - #123456 -
    - - + + + + + -
    - -
    Répertoire
    -
    -
    -
    -

    Aucun contact

    -
    -
    -
    -
    + -
    -
    Messages
    -
    -
    -
    -

    Aucun message

    -
    -
    -
    -
    +
    -
    -
    -
    -
    - - - - - -
    -
    -
    - -
    -
    Ajouter un contact
    -
    -
    - - - - -
    -
    -
    +
    +
    + + unknown
    -
    - - - - - - + + +
    + +
    Répertoire
    +
    +
    +
    +

    Aucun contact

    +
    +
    +
    +
    + +
    +
    Messages + +
    +
    +
    +
    +

    Aucun message

    +
    +
    +
    +
    + +
    +
    +
    +
    + + + + + +
    +
    +
    + +
    +
    Ajouter un contact
    +
    +
    + + + + +
    +
    +
    + +
    +
    + + + + + + + \ No newline at end of file diff --git a/html/ui_br.html b/html/ui_br.html index 9a28cd97..af05a98d 100644 --- a/html/ui_br.html +++ b/html/ui_br.html @@ -1,82 +1,87 @@ - - - - - - -
    - -
    - -
    - - #123456 -
    - - + + + + + -
    - -
    Agenda
    -
    -
    -
    -

    Nenhum contato

    -
    -
    -
    -
    + -
    -
    Mensagens
    -
    -
    -
    -

    Nenhuma mensagem

    -
    -
    -
    -
    +
    -
    -
    -
    -
    - - - - - -
    -
    -
    - -
    -
    Adicionar contato
    -
    -
    - - - - -
    -
    -
    +
    +
    + + unknown
    -
    - - - - - - + + +
    + +
    Agenda
    +
    +
    +
    +

    Nenhum contato

    +
    +
    +
    +
    + +
    +
    Mensagens + +
    +
    +
    +
    +

    Nenhuma mensagem

    +
    +
    +
    +
    + +
    +
    +
    +
    + + + + + +
    +
    +
    + +
    +
    Adicionar contato
    +
    +
    + + + + +
    +
    +
    + +
    +
    + + + + + + + \ No newline at end of file diff --git a/html/ui_de.html b/html/ui_de.html index 55d92593..87faddcd 100644 --- a/html/ui_de.html +++ b/html/ui_de.html @@ -1,82 +1,87 @@ - - - - - - -
    + + + + + -
    + -
    - - #123456 -
    +
    - - -
    - -
    Kontakte
    -
    -
    -
    -

    Keine Kontakte

    -
    -
    -
    -
    - -
    -
    Nachrichten
    -
    -
    -
    -

    Keine Nachrichten

    -
    -
    -
    -
    - -
    -
    -
    -
    - - - - - -
    -
    -
    - -
    -
    Kontakt hinzufügen
    -
    -
    - - - - -
    -
    -
    +
    +
    + + unknown
    + + + +
    + +
    Kontakte
    +
    +
    +
    +

    Keine Kontakte

    +
    +
    +
    +
    + +
    +
    Nachrichten + +
    +
    +
    +
    +

    Keine Nachrichten

    +
    +
    +
    +
    + +
    +
    +
    +
    + + + + + +
    +
    +
    + +
    +
    Kontakt hinzufügen
    +
    +
    + + + + +
    +
    +
    +
    +
    - - - - + + + - - + + + \ No newline at end of file diff --git a/html/ui_en.html b/html/ui_en.html index 9aaacf2b..469fb76a 100644 --- a/html/ui_en.html +++ b/html/ui_en.html @@ -1,82 +1,87 @@ - - - - - - -
    + + + + + -
    + -
    - - #123456 -
    +
    - - -
    - -
    Contacts
    -
    -
    -
    -

    No contacts

    -
    -
    -
    -
    - -
    -
    Messages
    -
    -
    -
    -

    No messages

    -
    -
    -
    -
    - -
    -
    -
    -
    - - - - - -
    -
    -
    - -
    -
    Add a contact
    -
    -
    - - - - -
    -
    -
    +
    +
    + + unknown
    + + + +
    + +
    Contacts
    +
    +
    +
    +

    No contacts

    +
    +
    +
    +
    + +
    +
    Messages + +
    +
    +
    +
    +

    No messages

    +
    +
    +
    +
    + +
    +
    +
    +
    + + + + + +
    +
    +
    + +
    +
    Add a contact
    +
    +
    + + + + +
    +
    +
    +
    +
    - - - - + + + - - + + + \ No newline at end of file From bc233a144168a43695c856b533a2d779ba6b8e97 Mon Sep 17 00:00:00 2001 From: ElPumpo Date: Tue, 30 Oct 2018 15:25:53 +0100 Subject: [PATCH 65/76] See description, resolves #38, resolves #36 - Fixed console error when adding invalid number (use _source) - Fix contacts not refreshing when added (fixed broken code) - Play sound when message recieved - Close phone if open when script is stopping - Get back contacts, etc when restarted mid-game --- client/main.lua | 20 ++++++-- server/main.lua | 122 +++++++++++++++++++++++++++--------------------- 2 files changed, 83 insertions(+), 59 deletions(-) diff --git a/client/main.lua b/client/main.lua index 646d52d0..ba62b52d 100644 --- a/client/main.lua +++ b/client/main.lua @@ -82,13 +82,13 @@ AddEventHandler('esx_phone:loaded', function(phoneNumber, contacts) end) RegisterNetEvent('esx_phone:addContact') -AddEventHandler('esx_phone:addContact', function(name, phoneNumber) +AddEventHandler('esx_phone:addContact', function(name, phoneNumber, playerOnline) table.insert(PhoneData.contacts, { name = name, number = phoneNumber, - online = (PhoneNumberSources[contacts[i].number] == nil and false or NetworkIsPlayerActive(GetPlayerFromServerId(PhoneNumberSources[contacts[i].number]))), + online = playerOnline }) - -- CALL HERE RELOADCONTACT + SendNUIMessage({ contactAdded = true, phoneData = PhoneData @@ -103,7 +103,7 @@ AddEventHandler('esx_phone:removeContact', function(name, phoneNumber) break end end - -- CALL HERE RELOADCONTACT + SendNUIMessage({ contactRemoved = true, phoneData = PhoneData @@ -131,7 +131,7 @@ end) RegisterNUICallback('add_contact', function(data, cb) local phoneNumber = tonumber(data.phoneNumber) local contactName = tostring(data.contactName) - + if phoneNumber then TriggerServerEvent('esx_phone:addPlayerContact', phoneNumber, contactName) else @@ -164,6 +164,8 @@ AddEventHandler('esx_phone:onMessage', function(phoneNumber, message, position, ESX.ShowNotification('~b~' .. job .. ': ~s~' .. message) end + PlaySound(-1, "Menu_Accept", "Phone_SoundSet_Default", 0, 0, 1) + SendNUIMessage({ newMessage = true, phoneNumber = phoneNumber, @@ -277,6 +279,14 @@ Citizen.CreateThread(function() end end) +AddEventHandler('onResourceStop', function(resource) + if resource == GetCurrentResourceName() then + if GUI.PhoneIsShowed then + ESX.UI.Menu.CloseAll() + end + end +end) + -- Key controls Citizen.CreateThread(function() while true do diff --git a/server/main.lua b/server/main.lua index 4ae0286e..13bcde33 100644 --- a/server/main.lua +++ b/server/main.lua @@ -1,49 +1,8 @@ -ESX = nil -local DisptachRequestId = 0 -local PhoneNumbers = {} +ESX = nil +local DisptachRequestId = 0 +local PhoneNumbers = {} -TriggerEvent('esx:getSharedObject', function(obj) - ESX = obj -end) - -function GenerateUniquePhoneNumber() - local foundNumber, phoneNumber = false, nil - - while not foundNumber do - Citizen.Wait(100) - - math.randomseed(GetGameTimer()) - phoneNumber = math.random(10000, 99999) - - local result = MySQL.Sync.fetchAll('SELECT COUNT(*) as count FROM users WHERE phone_number = @phoneNumber', { - ['@phoneNumber'] = phoneNumber - }) - - if tonumber(result[1].count) == 0 then - foundNumber = true - end - end - - return phoneNumber -end - -function GetDistpatchRequestId() - local requestId = DisptachRequestId - - if DisptachRequestId < 65535 then - DisptachRequestId = DisptachRequestId + 1 - else - DisptachRequestId = 0 - end - - return requestId -end - -AddEventHandler('esx_phone:getDistpatchRequestId', function(cb) - cb(GetDistpatchRequestId()) -end) - -AddEventHandler('esx:playerLoaded', function(source) +function LoadPlayer(source) local xPlayer = ESX.GetPlayerFromId(source) for num,v in pairs(PhoneNumbers) do @@ -101,7 +60,57 @@ AddEventHandler('esx:playerLoaded', function(source) TriggerClientEvent('esx_phone:loaded', source, phoneNumber, contacts) end) end) +end +TriggerEvent('esx:getSharedObject', function(obj) + ESX = obj + + local xPlayers = ESX.GetPlayers() + + for i=1, #xPlayers, 1 do + LoadPlayer(xPlayers[i]) + end +end) + +function GenerateUniquePhoneNumber() + local foundNumber, phoneNumber = false, nil + + while not foundNumber do + Citizen.Wait(100) + + math.randomseed(GetGameTimer()) + phoneNumber = math.random(10000, 99999) + + local result = MySQL.Sync.fetchAll('SELECT COUNT(*) as count FROM users WHERE phone_number = @phoneNumber', { + ['@phoneNumber'] = phoneNumber + }) + + if tonumber(result[1].count) == 0 then + foundNumber = true + end + end + + return phoneNumber +end + +function GetDistpatchRequestId() + local requestId = DisptachRequestId + + if DisptachRequestId < 65535 then + DisptachRequestId = DisptachRequestId + 1 + else + DisptachRequestId = 0 + end + + return requestId +end + +AddEventHandler('esx_phone:getDistpatchRequestId', function(cb) + cb(GetDistpatchRequestId()) +end) + +AddEventHandler('esx:playerLoaded', function(source) + LoadPlayer(source) end) AddEventHandler('esx:playerDropped', function(source) @@ -140,7 +149,7 @@ AddEventHandler('esx_phone:send', function(phoneNumber, message, anon, position) local _source = source local xPlayer = ESX.GetPlayerFromId(_source) - print('MESSAGE => ' .. xPlayer.name .. '@' .. phoneNumber .. ' : ' .. message) + print(('esx_phone: MESSAGE => %s@%s: %s'):format(xPlayer.name, phoneNumber, message)) if PhoneNumbers[phoneNumber] ~= nil then @@ -196,15 +205,15 @@ RegisterServerEvent('esx_phone:addPlayerContact') AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) local _source = source local xPlayer = ESX.GetPlayerFromId(_source) - phoneNumber = tonumber(phoneNumber) - - -- is the player trying to enter something else into the database? + phoneNumber = tonumber(phoneNumber) + local playerOnline = false + if phoneNumber == nil then - print('esx_phone: ' .. xPlayer.identifier .. ' attempted to crash the database!') + print(('esx_phone: %s attempted SQL injecting!'):format(xPlayer.identifier)) return end - MySQL.Async.fetchAll('SELECT phone_number FROM users WHERE phone_number = @number', { + MySQL.Async.fetchAll('SELECT phone_number, identifier FROM users WHERE phone_number = @number', { ['@number'] = phoneNumber }, function(result) if result[1] ~= nil then @@ -213,6 +222,7 @@ AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) else local contacts = xPlayer.get('contacts') + -- already added player? for i=1, #contacts, 1 do if contacts[i].number == phoneNumber then TriggerClientEvent('esx:showNotification', _source, _U('number_in_contacts')) @@ -222,11 +232,15 @@ AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) table.insert(contacts, { name = contactName, - number = phoneNumber, + number = phoneNumber }) xPlayer.set('contacts', contacts) + -- is the player currently online? + local xTarget = ESX.GetPlayerFromIdentifier(result[1].identifier) + playerOnline = (xTarget ~= nil) + MySQL.Async.execute('INSERT INTO user_contacts (identifier, name, number) VALUES (@identifier, @name, @number)', { ['@identifier'] = xPlayer.identifier, @@ -234,7 +248,7 @@ AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) ['@number'] = phoneNumber }, function(rowsChanged) TriggerClientEvent('esx:showNotification', _source, _U('contact_added')) - TriggerClientEvent('esx_phone:addContact', _source, contactName, phoneNumber) + TriggerClientEvent('esx_phone:addContact', _source, contactName, phoneNumber, playerOnline) end) end else @@ -277,7 +291,7 @@ AddEventHandler('esx_phone:removePlayerContact', function(phoneNumber, contactNa TriggerClientEvent('esx_phone:removeContact', _source, contactName, phoneNumber) end) else - TriggerClientEvent('esx:showNotification', source, _U('number_not_assigned')) + TriggerClientEvent('esx:showNotification', _source, _U('number_not_assigned')) end end) end) From 60c4f52c6bdcf3271e54a6ce9c44818a1ee36594 Mon Sep 17 00:00:00 2001 From: ElPumpo Date: Sun, 30 Dec 2018 21:55:11 +0100 Subject: [PATCH 66/76] Check IsControlJustReleased() every frame --- client/main.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/main.lua b/client/main.lua index ba62b52d..d84ffa76 100644 --- a/client/main.lua +++ b/client/main.lua @@ -245,7 +245,7 @@ end) Citizen.CreateThread(function() while true do - Citizen.Wait(10) + Citizen.Wait(0) if GUI.PhoneIsShowed then -- codes here: https://pastebin.com/guYd0ht4 DisableControlAction(0, 1, true) -- LookLeftRight @@ -290,7 +290,7 @@ end) -- Key controls Citizen.CreateThread(function() while true do - Citizen.Wait(10) + Citizen.Wait(0) if CurrentAction ~= nil then ESX.ShowHelpNotification(CurrentActionMsg) From ef8fba47f6652d5648e7fa999b1073d77b74a1c1 Mon Sep 17 00:00:00 2001 From: ElPumpo Date: Thu, 28 Feb 2019 21:21:12 +0100 Subject: [PATCH 67/76] 2 space locale indent, minor changes --- README.md | 16 +++++++--------- client/main.lua | 16 ++++++++-------- html/css/app.css | 8 ++++---- locales/br.lua | 22 +++++++++++----------- locales/de.lua | 22 +++++++++++----------- locales/en.lua | 24 ++++++++++++------------ locales/es.lua | 22 +++++++++++----------- locales/fr.lua | 22 +++++++++++----------- locales/pl.lua | 22 +++++++++++----------- locales/sv.lua | 24 ++++++++++++------------ server/main.lua | 2 +- 11 files changed, 99 insertions(+), 101 deletions(-) diff --git a/README.md b/README.md index 14151baa..96fa6c1a 100644 --- a/README.md +++ b/README.md @@ -34,15 +34,13 @@ Client side ```lua RegisterNetEvent('esx_phone:loaded') AddEventHandler('esx_phone:loaded', function(phoneNumber, contacts) + local specialContact = { + name = 'societyName', + number = 'societyNumber', + base64Icon = 'insert base 64 icon' + } - local specialContact = { - name = 'societyName', - number = 'societyNumber', - base64Icon = 'insert base 64 icon (PNG recommended)' - } - - TriggerEvent('esx_phone:addSpecialContact', specialContact.name, specialContact.number, specialContact.base64Icon) - + TriggerEvent('esx_phone:addSpecialContact', specialContact.name, specialContact.number, specialContact.base64Icon) end) ``` @@ -62,7 +60,7 @@ last two booleans are optional ### License esx_phone - phone script for fivem -Copyright (C) 2015-2018 Jérémie N'gadi +Copyright (C) 2015-2019 Jérémie N'gadi This program Is free software: you can redistribute it And/Or modify it under the terms Of the GNU General Public License As published by the Free Software Foundation, either version 3 Of the License, Or (at your option) any later version. diff --git a/client/main.lua b/client/main.lua index d84ffa76..27d25394 100644 --- a/client/main.lua +++ b/client/main.lua @@ -161,10 +161,10 @@ AddEventHandler('esx_phone:onMessage', function(phoneNumber, message, position, if job == 'player' then ESX.ShowNotification(_U('new_message', message)) else - ESX.ShowNotification('~b~' .. job .. ': ~s~' .. message) + ESX.ShowNotification(('~b~%s:~s~ %s'):format(job, message)) end - PlaySound(-1, "Menu_Accept", "Phone_SoundSet_Default", 0, 0, 1) + PlaySound(-1, 'Menu_Accept', 'Phone_SoundSet_Default', false, 0, true) SendNUIMessage({ newMessage = true, @@ -181,12 +181,12 @@ AddEventHandler('esx_phone:onMessage', function(phoneNumber, message, position, CurrentDispatchRequestId = dispatchRequestId CurrentActionData = { - phoneNumber = phoneNumber, - message = message, - position = position, - actions = actions, - anonyme = anon, - job = job + phoneNumber = phoneNumber, + message = message, + position = position, + actions = actions, + anonyme = anon, + job = job } ESX.SetTimeout(15000, function() diff --git a/html/css/app.css b/html/css/app.css index b1a4a4f4..9540a640 100644 --- a/html/css/app.css +++ b/html/css/app.css @@ -24,7 +24,7 @@ html, body { } #phone { - background-image: url(../img/phone.png); + background-image: url("../img/phone.png"); background-repeat: no-repeat; font-family: 'Catamaran', sans-serif; font-weight: 400; @@ -243,7 +243,7 @@ html, body { text-align: center; } -.message .sender-info, .message .body, .contact .sender-info, { +.message .sender-info, .message .body, .contact .sender-info { margin-right: 30px; } @@ -285,7 +285,7 @@ html, body { display: none; width: 24px; height: 24px; - background: transparent url(../img/icons/location.png) no-repeat 50%; + background: transparent url("../img/icons/location.png") no-repeat 50%; cursor: pointer; } @@ -293,7 +293,7 @@ html, body { display: none; width: 24px; height: 24px; - background: transparent url(../img/icons/ok.png) no-repeat 50%; + background: transparent url("../img/icons/ok.png") no-repeat 50%; cursor: pointer; } diff --git a/locales/br.lua b/locales/br.lua index 3275b1fc..1b1b3ac4 100644 --- a/locales/br.lua +++ b/locales/br.lua @@ -1,13 +1,13 @@ Locales['br'] = { - ['new_message'] = '~b~Nova mensagem :~s~ %s', - ['press_take_call'] = '%s - Pressione ~INPUT_CONTEXT~ para aceitar a chamada', - ['taken_call'] = '~y~%s~s~ recebeu uma chamada', - ['gps_position'] = 'destino inserido no GPS', - ['message_sent'] = 'mensagem enviada', - ['cannot_add_self'] = 'você não pode se adicionar', - ['number_in_contacts'] = 'este número já está na sua lista de contatos', - ['contact_added'] = 'contato adicionado', - ['contact_removed'] = 'the contact has been removed!', - ['number_not_assigned'] = 'este número não foi atribuído...', - ['invalid_number'] = 'that\'s not an valid number!', + ['new_message'] = '~b~Nova mensagem :~s~ %s', + ['press_take_call'] = '%s - Pressione ~INPUT_CONTEXT~ para aceitar a chamada', + ['taken_call'] = '~y~%s~s~ recebeu uma chamada', + ['gps_position'] = 'destino inserido no GPS', + ['message_sent'] = 'mensagem enviada', + ['cannot_add_self'] = 'você não pode se adicionar', + ['number_in_contacts'] = 'este número já está na sua lista de contatos', + ['contact_added'] = 'contato adicionado', + ['contact_removed'] = 'the contact has been removed!', + ['number_not_assigned'] = 'este número não foi atribuído...', + ['invalid_number'] = 'that\'s not an valid number!', } diff --git a/locales/de.lua b/locales/de.lua index bea36169..7333c33b 100644 --- a/locales/de.lua +++ b/locales/de.lua @@ -1,13 +1,13 @@ Locales['de'] = { - ['new_message'] = '~b~Neue Nachricht', - ['press_take_call'] = '%s - Drücke ~INPUT_CONTEXT~ um den Anruf anzunehmen', - ['taken_call'] = '~y~%s~s~ hat den Anruf angenommen', - ['gps_position'] = 'ziel in GPS eingegeben', - ['message_sent'] = 'nachricht gesendet', - ['cannot_add_self'] = 'du kannst dich nicht selbst hinzufügen', - ['number_in_contacts'] = 'diese Nummer ist bereits in deinen Kontakten', - ['contact_added'] = 'kontakt hinzugefügt', - ['contact_removed'] = 'the contact has been removed!', - ['number_not_assigned'] = 'diese Nummer ist nicht vergeben...', - ['invalid_number'] = 'that\'s not an valid number!', + ['new_message'] = '~b~Neue Nachricht', + ['press_take_call'] = '%s - Drücke ~INPUT_CONTEXT~ um den Anruf anzunehmen', + ['taken_call'] = '~y~%s~s~ hat den Anruf angenommen', + ['gps_position'] = 'ziel in GPS eingegeben', + ['message_sent'] = 'nachricht gesendet', + ['cannot_add_self'] = 'du kannst dich nicht selbst hinzufügen', + ['number_in_contacts'] = 'diese Nummer ist bereits in deinen Kontakten', + ['contact_added'] = 'kontakt hinzugefügt', + ['contact_removed'] = 'the contact has been removed!', + ['number_not_assigned'] = 'diese Nummer ist nicht vergeben...', + ['invalid_number'] = 'that\'s not an valid number!', } diff --git a/locales/en.lua b/locales/en.lua index 9fea81d3..f7c4babe 100644 --- a/locales/en.lua +++ b/locales/en.lua @@ -1,13 +1,13 @@ Locales['en'] = { - ['new_message'] = '~b~You have recived a message:~s~ %s', - ['press_take_call'] = '%s - Press ~INPUT_CONTEXT~ to take the call', - ['taken_call'] = '~y~%s~s~ has taken the call', - ['gps_position'] = 'the destination has been added to your GPS', - ['message_sent'] = 'the message has been sent', - ['cannot_add_self'] = 'you can not add yourself!', - ['number_in_contacts'] = 'this number is already in your contact list', - ['contact_added'] = 'the contact has been added!', - ['contact_removed'] = 'the contact has been removed!', - ['number_not_assigned'] = 'the number has not been assigned!', - ['invalid_number'] = 'that\'s not an valid number!', -} \ No newline at end of file + ['new_message'] = '~b~You have recived a message:~s~ %s', + ['press_take_call'] = '%s - Press ~INPUT_CONTEXT~ to take the call', + ['taken_call'] = '~y~%s~s~ has taken the call', + ['gps_position'] = 'the destination has been added to your GPS', + ['message_sent'] = 'the message has been sent', + ['cannot_add_self'] = 'you can not add yourself!', + ['number_in_contacts'] = 'this number is already in your contact list', + ['contact_added'] = 'the contact has been added!', + ['contact_removed'] = 'the contact has been removed!', + ['number_not_assigned'] = 'the number has not been assigned!', + ['invalid_number'] = 'that\'s not an valid number!', +} diff --git a/locales/es.lua b/locales/es.lua index 97ca7ad7..52882d8b 100644 --- a/locales/es.lua +++ b/locales/es.lua @@ -1,13 +1,13 @@ Locales['es'] = { - ['new_message'] = '~b~Nuevo menaje :~s~ %s', - ['press_take_call'] = '%s - Presionar ~INPUT_CONTEXT~ para coger la llamada', - ['taken_call'] = '~y~%s~s~ has contestado la llamada', - ['gps_position'] = 'posición GPS', - ['message_sent'] = 'mensaje enviado', - ['cannot_add_self'] = 'no has podido agregar', - ['number_in_contacts'] = 'este número ya está en tu lista de contactos', - ['contact_added'] = 'contacto añadido', - ['contact_removed'] = 'the contact has been removed!', - ['number_not_assigned'] = 'el número no se ha añadido todavía...', - ['invalid_number'] = 'that\'s not an valid number!', + ['new_message'] = '~b~Nuevo menaje :~s~ %s', + ['press_take_call'] = '%s - Presionar ~INPUT_CONTEXT~ para coger la llamada', + ['taken_call'] = '~y~%s~s~ has contestado la llamada', + ['gps_position'] = 'posición GPS', + ['message_sent'] = 'mensaje enviado', + ['cannot_add_self'] = 'no has podido agregar', + ['number_in_contacts'] = 'este número ya está en tu lista de contactos', + ['contact_added'] = 'contacto añadido', + ['contact_removed'] = 'the contact has been removed!', + ['number_not_assigned'] = 'el número no se ha añadido todavía...', + ['invalid_number'] = 'that\'s not an valid number!', } diff --git a/locales/fr.lua b/locales/fr.lua index aa2f88d2..9b47eb4d 100644 --- a/locales/fr.lua +++ b/locales/fr.lua @@ -1,13 +1,13 @@ Locales['fr'] = { - ['new_message'] = '~b~Nouveau message :~s~ %s', - ['press_take_call'] = '%s - Appuyez sur ~INPUT_CONTEXT~ pour prendre l\'appel', - ['taken_call'] = '~y~%s~s~ a pris l\'appel', - ['gps_position'] = 'position entrée dans le GPS', - ['message_sent'] = 'message envoyé', - ['cannot_add_self'] = 'vous ne pouvez pas vous ajouter vous-même', - ['number_in_contacts'] = 'ce numéro est déja dans votre liste de contacts', - ['contact_added'] = 'contact ajouté', - ['contact_removed'] = 'contact supprimé', - ['number_not_assigned'] = 'ce numéro n\'est pas attribué...', - ['invalid_number'] = 'that\'s not an valid number!', + ['new_message'] = '~b~Nouveau message :~s~ %s', + ['press_take_call'] = '%s - Appuyez sur ~INPUT_CONTEXT~ pour prendre l\'appel', + ['taken_call'] = '~y~%s~s~ a pris l\'appel', + ['gps_position'] = 'position entrée dans le GPS', + ['message_sent'] = 'message envoyé', + ['cannot_add_self'] = 'vous ne pouvez pas vous ajouter vous-même', + ['number_in_contacts'] = 'ce numéro est déja dans votre liste de contacts', + ['contact_added'] = 'contact ajouté', + ['contact_removed'] = 'contact supprimé', + ['number_not_assigned'] = 'ce numéro n\'est pas attribué...', + ['invalid_number'] = 'that\'s not an valid number!', } diff --git a/locales/pl.lua b/locales/pl.lua index b3218307..85b5eee5 100644 --- a/locales/pl.lua +++ b/locales/pl.lua @@ -1,13 +1,13 @@ Locales['pl'] = { - ['new_message'] = '~b~Masz nową wiadomość:~s~ %s', - ['press_take_call'] = '%s - wciśnij ~INPUT_CONTEXT~ aby odebrać telefon', - ['taken_call'] = '~y~%s~s~ odebrał telefon', - ['gps_position'] = 'cel podróży został dodany do GPS\'a', - ['message_sent'] = 'wiadomość wysłana', - ['cannot_add_self'] = 'nie możesz dodać samego siebie!', - ['number_in_contacts'] = 'ten numer jest już na twojej liście kontaktów', - ['contact_added'] = 'kontakt dodany!', - ['contact_removed'] = 'kontakt usunięty!', - ['number_not_assigned'] = 'numer nie jest przypisany!', - ['invalid_number'] = 'to nie jest prawidłowy numer telefonu!', + ['new_message'] = '~b~Masz nową wiadomość:~s~ %s', + ['press_take_call'] = '%s - wciśnij ~INPUT_CONTEXT~ aby odebrać telefon', + ['taken_call'] = '~y~%s~s~ odebrał telefon', + ['gps_position'] = 'cel podróży został dodany do GPS\'a', + ['message_sent'] = 'wiadomość wysłana', + ['cannot_add_self'] = 'nie możesz dodać samego siebie!', + ['number_in_contacts'] = 'ten numer jest już na twojej liście kontaktów', + ['contact_added'] = 'kontakt dodany!', + ['contact_removed'] = 'kontakt usunięty!', + ['number_not_assigned'] = 'numer nie jest przypisany!', + ['invalid_number'] = 'to nie jest prawidłowy numer telefonu!', } diff --git a/locales/sv.lua b/locales/sv.lua index 51e39990..334facfc 100644 --- a/locales/sv.lua +++ b/locales/sv.lua @@ -1,13 +1,13 @@ Locales['sv'] = { - ['new_message'] = '~b~Nytt meddelande:~s~ %s', - ['press_take_call'] = '%s - tryck ~INPUT_CONTEXT~ för att svara', - ['taken_call'] = '~y~%s~s~ har tagit emot ditt meddelande', - ['gps_position'] = 'destinationen är markerad på din GPS!', - ['message_sent'] = 'meddelandet har skickats!', - ['cannot_add_self'] = 'du kan inte lägga till dig själv!', - ['number_in_contacts'] = 'det här nummret finns redan bland dina kontakter!', - ['contact_added'] = 'kontakten har lags till!', - ['contact_removed'] = 'kontakten har tagits bort!', - ['number_not_assigned'] = 'det här nummret finns inte!', - ['invalid_number'] = 'det är inte ett giltigt nummer!', -} \ No newline at end of file + ['new_message'] = '~b~Nytt meddelande:~s~ %s', + ['press_take_call'] = '%s - tryck ~INPUT_CONTEXT~ för att svara', + ['taken_call'] = '~y~%s~s~ har tagit emot ditt meddelande', + ['gps_position'] = 'destinationen är markerad på din GPS!', + ['message_sent'] = 'meddelandet har skickats!', + ['cannot_add_self'] = 'du kan inte lägga till dig själv!', + ['number_in_contacts'] = 'det här nummret finns redan bland dina kontakter!', + ['contact_added'] = 'kontakten har lags till!', + ['contact_removed'] = 'kontakten har tagits bort!', + ['number_not_assigned'] = 'det här nummret finns inte!', + ['invalid_number'] = 'det är inte ett giltigt nummer!', +} diff --git a/server/main.lua b/server/main.lua index 13bcde33..f8c624cb 100644 --- a/server/main.lua +++ b/server/main.lua @@ -6,7 +6,7 @@ function LoadPlayer(source) local xPlayer = ESX.GetPlayerFromId(source) for num,v in pairs(PhoneNumbers) do - if tonumber(num) == num then -- If phonenumber is a player phone number + if tonumber(num) == num then -- if phonenumber is a player phone number for src,_ in pairs(v.sources) do TriggerClientEvent('esx_phone:setPhoneNumberSource', source, num, tonumber(src)) end From feceb8ac6f30bae891b2d56b7f6c150425e92839 Mon Sep 17 00:00:00 2001 From: BackrndSource Date: Wed, 13 Mar 2019 20:04:27 +0000 Subject: [PATCH 68/76] Correction Spanish Translation --- locales/es.lua | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/locales/es.lua b/locales/es.lua index 52882d8b..d8ecca20 100644 --- a/locales/es.lua +++ b/locales/es.lua @@ -1,13 +1,14 @@ Locales['es'] = { - ['new_message'] = '~b~Nuevo menaje :~s~ %s', - ['press_take_call'] = '%s - Presionar ~INPUT_CONTEXT~ para coger la llamada', - ['taken_call'] = '~y~%s~s~ has contestado la llamada', - ['gps_position'] = 'posición GPS', + ['new_message'] = '~b~Nuevo mensaje :~s~ %s', + ['press_take_call'] = '%s - Pulsar ~INPUT_CONTEXT~ para coger la llamada', + ['taken_call'] = '~y~%s~s~ ha contestado la llamada', + ['gps_position'] = 'destino añadido al GPS', ['message_sent'] = 'mensaje enviado', - ['cannot_add_self'] = 'no has podido agregar', + ['cannot_add_self'] = 'no puedes agregarte a ti mismo!', ['number_in_contacts'] = 'este número ya está en tu lista de contactos', - ['contact_added'] = 'contacto añadido', - ['contact_removed'] = 'the contact has been removed!', - ['number_not_assigned'] = 'el número no se ha añadido todavía...', - ['invalid_number'] = 'that\'s not an valid number!', + ['contact_added'] = 'contacto añadido!', + ['contact_removed'] = 'el contacto ha sido eliminado!', + ['number_not_assigned'] = 'el número no ha sido añadido!', + ['invalid_number'] = 'el número no es válido!', } + From 160fe95b39ebf09efb6f791b4d46843c05a52569 Mon Sep 17 00:00:00 2001 From: BackrndSource Date: Wed, 13 Mar 2019 20:24:08 +0000 Subject: [PATCH 69/76] Add Spanish Translation --- ui_es.html | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 ui_es.html diff --git a/ui_es.html b/ui_es.html new file mode 100644 index 00000000..9b730801 --- /dev/null +++ b/ui_es.html @@ -0,0 +1,87 @@ + + + + + + + + + + +
    + +
    + +
    + + unknown +
    + + + +
    + +
    Contactos
    +
    +
    +
    +

    No hay contactos

    +
    +
    +
    +
    + +
    +
    Mensajes + +
    +
    +
    +
    +

    No hay mensajes

    +
    +
    +
    +
    + +
    +
    +
    +
    + + + + + +
    +
    +
    + +
    +
    Añadir contacto
    +
    +
    + + + + +
    +
    +
    + +
    +
    + + + + + + + + From 3e06ce72500dd7cf340972ff75703c5349397c9c Mon Sep 17 00:00:00 2001 From: BackrndSource Date: Wed, 13 Mar 2019 20:25:08 +0000 Subject: [PATCH 70/76] Add Spanish Translation --- html/scripts/app_es.js | 512 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 512 insertions(+) create mode 100644 html/scripts/app_es.js diff --git a/html/scripts/app_es.js b/html/scripts/app_es.js new file mode 100644 index 00000000..35738a76 --- /dev/null +++ b/html/scripts/app_es.js @@ -0,0 +1,512 @@ +(function(){ + + let ContactTpl = + '
    ' + + '
    ' + + '
    ' + + '{{sender}}
    #{{phoneNumber}}' + + '
    ' + + '
    ' + + '
    ' + + 'X' + + '' + + '
    ' + + '
    ' + ; + + let MessageTpl = + '
    ' + + '
    ' + + '
    ' + + '{{sender}}
    #{{phoneNumber}}' + + '
    ' + + '
    ' + + '
    {{message}}
    ' + + '
    ' + + '
    ' + ; + + let SpecialContactTpl = '
  • {{name}}
  • '; + + let contacts = []; + let specialContacts = []; + let menu = []; + let currentItem = 0; + let currentVal = null; + let isMessageEditorOpen = false; + let isMessagesOpen = false; + let isPhoneShowed = false; + + let showMain = function() { + $('.screen').removeClass('active'); + $('.screen *').attr('disabled', 'disabled'); + } + + let showRepertoire = function() { + $('#repertoire').addClass('active'); + } + + let hideRepertoire = function() { + $('#repertoire').removeClass('active'); + } + + let showMessages = function(){ + $('#messages').addClass('active'); + isMessagesOpen = true; + } + + let hideMessages = function(){ + $('#messages').removeClass('active'); + isMessagesOpen = false; + } + + let showAddContact = function() { + $('#contact').addClass('active'); + $('.screen *').attr('disabled', 'disabled'); + $('.screen.active *').removeAttr('disabled'); + } + + let hideAddContact = function() { + $('#contact').removeClass('active'); + $('#contact_name').val(''); + $('#contact_number').val(''); + } + + let showNewMessage = function(cnum, cname) { + $('#writer').addClass('active'); + $('#writer_number').val(cnum); + $('#writer .header-title').html(cname); + $('.screen *').attr('disabled', 'disabled'); + $('.screen.active *').removeAttr('disabled'); + } + + let hideNewMessage = function() { + $('#writer').removeClass('active'); + $('#writer_number').val(''); + $('#writer_message').val(''); + $('#writer .header-title').html(''); + } + + let showGPS = function(xPos, yPos) { + $.post('http://esx_phone/setGPS', JSON.stringify({ + x: parseFloat(xPos), + y: parseFloat(yPos) + } + )); + } + + let renderContacts = function(){ + + let contactHTML = ''; + + if(contacts.length > 0) { + + for(let i=0; i 0) { + + for(let i=0; i { + return a.name.localeCompare(b.name); + }); + + renderSpecialContacts(); + + } + + } + + let removeSpecialContact = function(number){ + + for(let i=0; iContactos' + + '
  • Mensajes
  • ' + ); + + for(let i=0; i Date: Fri, 31 May 2019 12:37:48 +0200 Subject: [PATCH 71/76] Updated native name --- client/main.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/main.lua b/client/main.lua index 27d25394..0249ab82 100644 --- a/client/main.lua +++ b/client/main.lua @@ -269,7 +269,7 @@ Citizen.CreateThread(function() else -- open phone -- todo: is player busy (handcuffed, etc) - if IsControlJustReleased(0, Keys['F1']) and GetLastInputMethod(2) then + if IsControlJustReleased(0, Keys['F1']) and IsInputDisabled(0) then if not ESX.UI.Menu.IsOpen('phone', GetCurrentResourceName(), 'main') then ESX.UI.Menu.CloseAll() ESX.UI.Menu.Open('phone', GetCurrentResourceName(), 'main') @@ -295,7 +295,7 @@ Citizen.CreateThread(function() if CurrentAction ~= nil then ESX.ShowHelpNotification(CurrentActionMsg) - if IsControlJustReleased(0, Keys['E']) and GetLastInputMethod(2) then + if IsControlJustReleased(0, Keys['E']) and IsInputDisabled(0) then if CurrentAction == 'dispatch' then TriggerServerEvent('esx_phone:stopDispatch', CurrentDispatchRequestId) SetNewWaypoint(CurrentActionData.position.x, CurrentActionData.position.y) From 9f21efed2deed478b99cec1507d6d1cdeb8c319d Mon Sep 17 00:00:00 2001 From: ElPumpo Date: Fri, 31 May 2019 23:09:40 +0200 Subject: [PATCH 72/76] Minor cleanup and added missing sql index --- client/main.lua | 47 +++++---------- esx_phone.sql | 17 +++--- html/ui.html | 13 ++-- html/ui_br.html | 13 ++-- html/ui_de.html | 13 ++-- html/ui_en.html | 13 ++-- html/ui_pl.html | 153 +++++++++++++++++++++++++----------------------- server/main.lua | 94 ++++++++++++++--------------- 8 files changed, 180 insertions(+), 183 deletions(-) diff --git a/client/main.lua b/client/main.lua index 0249ab82..dd69b91f 100644 --- a/client/main.lua +++ b/client/main.lua @@ -1,40 +1,21 @@ -local Keys = { - ["ESC"] = 322, ["F1"] = 288, ["F2"] = 289, ["F3"] = 170, ["F5"] = 166, ["F6"] = 167, ["F7"] = 168, ["F8"] = 169, ["F9"] = 56, ["F10"] = 57, - ["~"] = 243, ["1"] = 157, ["2"] = 158, ["3"] = 160, ["4"] = 164, ["5"] = 165, ["6"] = 159, ["7"] = 161, ["8"] = 162, ["9"] = 163, ["-"] = 84, ["="] = 83, ["BACKSPACE"] = 177, - ["TAB"] = 37, ["Q"] = 44, ["W"] = 32, ["E"] = 38, ["R"] = 45, ["T"] = 245, ["Y"] = 246, ["U"] = 303, ["P"] = 199, ["["] = 39, ["]"] = 40, ["ENTER"] = 18, - ["CAPS"] = 137, ["A"] = 34, ["S"] = 8, ["D"] = 9, ["F"] = 23, ["G"] = 47, ["H"] = 74, ["K"] = 311, ["L"] = 182, - ["LEFTSHIFT"] = 21, ["Z"] = 20, ["X"] = 73, ["C"] = 26, ["V"] = 0, ["B"] = 29, ["N"] = 249, ["M"] = 244, [","] = 82, ["."] = 81, - ["LEFTCTRL"] = 36, ["LEFTALT"] = 19, ["SPACE"] = 22, ["RIGHTCTRL"] = 70, - ["HOME"] = 213, ["PAGEUP"] = 10, ["PAGEDOWN"] = 11, ["DELETE"] = 178, - ["LEFT"] = 174, ["RIGHT"] = 175, ["TOP"] = 27, ["DOWN"] = 173, - ["NENTER"] = 201, ["N4"] = 108, ["N5"] = 60, ["N6"] = 107, ["N+"] = 96, ["N-"] = 97, ["N7"] = 117, ["N8"] = 61, ["N9"] = 118 -} - -local GUI = {} -local PhoneData = { phoneNumber = 0, contacts = {} } -local CurrentAction = nil -local CurrentActionMsg = '' -local CurrentActionData = {} -local CurrentDispatchRequestId = -1 -local PhoneNumberSources = {} - -ESX = nil -GUI.PhoneIsShowed = false -GUI.MessagesIsShowed = false -GUI.AddContactIsShowed = false +local GUI, PhoneData, CurrentActionData, PhoneNumberSources, CurrentDispatchRequestId = {}, {phoneNumber = 0, contacts = {}}, {}, {}, -1 +local CurrentAction, CurrentActionMsg +GUI.PhoneIsShowed = false +GUI.MessagesIsShowed = false +GUI.AddContactIsShowed = false +ESX = nil Citizen.CreateThread(function() while ESX == nil do TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end) Citizen.Wait(0) end - + ESX.UI.Menu.RegisterType('phone', OpenPhone, ClosePhone) end) function OpenPhone() local playerPed = PlayerPedId() - TriggerServerEvent('esx_phone:reload', PhoneData.phoneNumber) SendNUIMessage({ @@ -188,7 +169,7 @@ AddEventHandler('esx_phone:onMessage', function(phoneNumber, message, position, anonyme = anon, job = job } - + ESX.SetTimeout(15000, function() CurrentAction = nil end) @@ -222,7 +203,7 @@ RegisterNUICallback('send', function(data) local playerPed = PlayerPedId() local coords = GetEntityCoords(playerPed) - if tonumber(phoneNumber) ~= nil then + if tonumber(phoneNumber) then phoneNumber = tonumber(phoneNumber) end @@ -231,7 +212,7 @@ RegisterNUICallback('send', function(data) y = coords.y, z = coords.z }) - + SendNUIMessage({ showMessageEditor = false }) @@ -269,7 +250,7 @@ Citizen.CreateThread(function() else -- open phone -- todo: is player busy (handcuffed, etc) - if IsControlJustReleased(0, Keys['F1']) and IsInputDisabled(0) then + if IsControlJustReleased(0, 288) and IsInputDisabled(0) then if not ESX.UI.Menu.IsOpen('phone', GetCurrentResourceName(), 'main') then ESX.UI.Menu.CloseAll() ESX.UI.Menu.Open('phone', GetCurrentResourceName(), 'main') @@ -292,10 +273,10 @@ Citizen.CreateThread(function() while true do Citizen.Wait(0) - if CurrentAction ~= nil then + if CurrentAction then ESX.ShowHelpNotification(CurrentActionMsg) - if IsControlJustReleased(0, Keys['E']) and IsInputDisabled(0) then + if IsControlJustReleased(0, 38) and IsInputDisabled(0) then if CurrentAction == 'dispatch' then TriggerServerEvent('esx_phone:stopDispatch', CurrentDispatchRequestId) SetNewWaypoint(CurrentActionData.position.x, CurrentActionData.position.y) @@ -307,4 +288,4 @@ Citizen.CreateThread(function() Citizen.Wait(500) end end -end) +end) \ No newline at end of file diff --git a/esx_phone.sql b/esx_phone.sql index 76da439f..220d5ad1 100644 --- a/esx_phone.sql +++ b/esx_phone.sql @@ -1,12 +1,15 @@ USE `essentialmode`; CREATE TABLE `user_contacts` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `identifier` varchar(255) NOT NULL, - `name` varchar(255) NOT NULL, - `number` int(11) NOT NULL, - - PRIMARY KEY (`id`) + `id` INT(11) NOT NULL AUTO_INCREMENT, + `identifier` VARCHAR(22) NOT NULL, + `name` VARCHAR(100) NOT NULL, + `number` INT(11) NOT NULL, + + PRIMARY KEY (`id`), + INDEX `index_user_contacts_identifier_name_number` (`identifier`, `name`, `number`) ); -ALTER TABLE `users`ADD COLUMN `phone_number` INT NULL; \ No newline at end of file +ALTER TABLE `users` + ADD COLUMN `phone_number` INT(11) NULL, + ADD UNIQUE INDEX `index_users_phone_number` (`phone_number`); \ No newline at end of file diff --git a/html/ui.html b/html/ui.html index e9023e74..d958ac00 100644 --- a/html/ui.html +++ b/html/ui.html @@ -25,9 +25,10 @@
    - -
    Répertoire
    +
    + Répertoire + +
    @@ -51,7 +52,8 @@
    -
    +
    @@ -64,7 +66,8 @@
    -
    Ajouter un contact
    +
    Ajouter + un contact
    diff --git a/html/ui_br.html b/html/ui_br.html index af05a98d..b0e2c9e3 100644 --- a/html/ui_br.html +++ b/html/ui_br.html @@ -25,9 +25,10 @@
    - -
    Agenda
    +
    + Agenda + +
    @@ -51,7 +52,8 @@
    -
    +
    @@ -64,7 +66,8 @@
    -
    Adicionar contato
    +
    Adicionar contato
    diff --git a/html/ui_de.html b/html/ui_de.html index 87faddcd..e8171754 100644 --- a/html/ui_de.html +++ b/html/ui_de.html @@ -25,9 +25,10 @@
    - -
    Kontakte
    +
    + Kontakte + +
    @@ -51,7 +52,8 @@
    -
    +
    @@ -64,7 +66,8 @@
    -
    Kontakt hinzufügen
    +
    Kontakt + hinzufügen
    diff --git a/html/ui_en.html b/html/ui_en.html index 469fb76a..18375822 100644 --- a/html/ui_en.html +++ b/html/ui_en.html @@ -25,9 +25,10 @@
    - -
    Contacts
    +
    + Contacts + +
    @@ -51,7 +52,8 @@
    -
    +
    @@ -64,7 +66,8 @@
    -
    Add a contact
    +
    Add a + contact
    diff --git a/html/ui_pl.html b/html/ui_pl.html index 0d731437..92448574 100644 --- a/html/ui_pl.html +++ b/html/ui_pl.html @@ -1,82 +1,91 @@ - - - - - - -
    + + + + + -
    + -
    - - #123456 -
    +
    - - -
    - -
    Kontakty
    -
    -
    -
    -

    Brak kontaktów

    -
    -
    -
    -
    - -
    -
    Wiadomości
    -
    -
    -
    -

    Brak wiadomości

    -
    -
    -
    -
    - -
    -
    -
    -
    - - - - - -
    -
    -
    - -
    -
    Dodaj kontakt
    -
    -
    - - - - -
    -
    -
    +
    +
    + + #123456
    + + + +
    +
    + Kontakty + +
    +
    +
    +
    +

    Brak kontaktów

    +
    +
    +
    +
    + +
    +
    Wiadomości + +
    +
    +
    +
    +

    Brak wiadomości

    +
    +
    +
    +
    + +
    +
    +
    +
    + + + + + +
    +
    +
    + +
    +
    Dodaj + kontakt
    +
    +
    + + + + +
    +
    +
    +
    +
    - - - - + + + + - - + + + \ No newline at end of file diff --git a/server/main.lua b/server/main.lua index f8c624cb..ee6b4e37 100644 --- a/server/main.lua +++ b/server/main.lua @@ -1,6 +1,15 @@ -ESX = nil -local DisptachRequestId = 0 -local PhoneNumbers = {} +ESX = nil +local DisptachRequestId, PhoneNumbers = 0, {} + +TriggerEvent('esx:getSharedObject', function(obj) + ESX = obj + + local xPlayers = ESX.GetPlayers() + + for i=1, #xPlayers, 1 do + LoadPlayer(xPlayers[i]) + end +end) function LoadPlayer(source) local xPlayer = ESX.GetPlayerFromId(source) @@ -21,8 +30,7 @@ function LoadPlayer(source) if phoneNumber == nil then phoneNumber = GenerateUniquePhoneNumber() - MySQL.Async.execute('UPDATE users SET phone_number = @phone_number WHERE identifier = @identifier', - { + MySQL.Async.execute('UPDATE users SET phone_number = @phone_number WHERE identifier = @identifier', { ['@identifier'] = xPlayer.identifier, ['@phone_number'] = phoneNumber }) @@ -42,7 +50,7 @@ function LoadPlayer(source) xPlayer.set('phoneNumber', phoneNumber) local contacts = {} - if PhoneNumbers[xPlayer.job.name] ~= nil then + if PhoneNumbers[xPlayer.job.name] then TriggerEvent('esx_phone:addSource', xPlayer.job.name, source) end @@ -62,16 +70,6 @@ function LoadPlayer(source) end) end -TriggerEvent('esx:getSharedObject', function(obj) - ESX = obj - - local xPlayers = ESX.GetPlayers() - - for i=1, #xPlayers, 1 do - LoadPlayer(xPlayers[i]) - end -end) - function GenerateUniquePhoneNumber() local foundNumber, phoneNumber = false, nil @@ -109,8 +107,8 @@ AddEventHandler('esx_phone:getDistpatchRequestId', function(cb) cb(GetDistpatchRequestId()) end) -AddEventHandler('esx:playerLoaded', function(source) - LoadPlayer(source) +AddEventHandler('esx:playerLoaded', function(playerId) + LoadPlayer(playerId) end) AddEventHandler('esx:playerDropped', function(source) @@ -120,39 +118,36 @@ AddEventHandler('esx:playerDropped', function(source) TriggerClientEvent('esx_phone:setPhoneNumberSource', -1, phoneNumber, -1) PhoneNumbers[phoneNumber] = nil - if PhoneNumbers[xPlayer.job.name] ~= nil then + if PhoneNumbers[xPlayer.job.name] then TriggerEvent('esx_phone:removeSource', xPlayer.job.name, source) end end) AddEventHandler('esx:setJob', function(source, job, lastJob) - if PhoneNumbers[lastJob.name] ~= nil then + if PhoneNumbers[lastJob.name] then TriggerEvent('esx_phone:removeSource', lastJob.name, source) end - if PhoneNumbers[job.name] ~= nil then + if PhoneNumbers[job.name] then TriggerEvent('esx_phone:addSource', job.name, source) end end) RegisterServerEvent('esx_phone:reload') AddEventHandler('esx_phone:reload', function(phoneNumber) - local _source = source - local xPlayer = ESX.GetPlayerFromId(_source) + local playerId = source + local xPlayer = ESX.GetPlayerFromId(playerId) local contacts = xPlayer.get('contacts') - TriggerClientEvent('esx_phone:loaded', _source, phoneNumber, contacts) + TriggerClientEvent('esx_phone:loaded', playerId, phoneNumber, contacts) end) RegisterServerEvent('esx_phone:send') AddEventHandler('esx_phone:send', function(phoneNumber, message, anon, position) - local _source = source - local xPlayer = ESX.GetPlayerFromId(_source) - + local xPlayer = ESX.GetPlayerFromId(source) print(('esx_phone: MESSAGE => %s@%s: %s'):format(xPlayer.name, phoneNumber, message)) - if PhoneNumbers[phoneNumber] ~= nil then - + if PhoneNumbers[phoneNumber] then for k,v in pairs(PhoneNumbers[phoneNumber].sources) do local numType = PhoneNumbers[phoneNumber].type local numHasDispatch = PhoneNumbers[phoneNumber].hasDispatch @@ -171,7 +166,6 @@ AddEventHandler('esx_phone:send', function(phoneNumber, message, anon, position) TriggerClientEvent('esx_phone:onMessage', numSource, xPlayer.get('phoneNumber'), message, numPosition, (numHide and true or anon), numType, false) end end - end end) @@ -203,29 +197,29 @@ end) RegisterServerEvent('esx_phone:addPlayerContact') AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) - local _source = source - local xPlayer = ESX.GetPlayerFromId(_source) + local playerId = source + local xPlayer = ESX.GetPlayerFromId(playerId) phoneNumber = tonumber(phoneNumber) local playerOnline = false if phoneNumber == nil then - print(('esx_phone: %s attempted SQL injecting!'):format(xPlayer.identifier)) + print(('esx_phone: %s parsed invalid player contact number!'):format(xPlayer.identifier)) return end MySQL.Async.fetchAll('SELECT phone_number, identifier FROM users WHERE phone_number = @number', { ['@number'] = phoneNumber }, function(result) - if result[1] ~= nil then + if result[1] then if phoneNumber == xPlayer.get('phoneNumber') then - TriggerClientEvent('esx:showNotification', _source, _U('cannot_add_self')) + TriggerClientEvent('esx:showNotification', playerId, _U('cannot_add_self')) else local contacts = xPlayer.get('contacts') -- already added player? for i=1, #contacts, 1 do if contacts[i].number == phoneNumber then - TriggerClientEvent('esx:showNotification', _source, _U('number_in_contacts')) + TriggerClientEvent('esx:showNotification', playerId, _U('number_in_contacts')) return end end @@ -239,34 +233,33 @@ AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName) -- is the player currently online? local xTarget = ESX.GetPlayerFromIdentifier(result[1].identifier) - playerOnline = (xTarget ~= nil) + playerOnline = xTarget ~= nil - MySQL.Async.execute('INSERT INTO user_contacts (identifier, name, number) VALUES (@identifier, @name, @number)', - { + MySQL.Async.execute('INSERT INTO user_contacts (identifier, name, number) VALUES (@identifier, @name, @number)', { ['@identifier'] = xPlayer.identifier, ['@name'] = contactName, ['@number'] = phoneNumber }, function(rowsChanged) - TriggerClientEvent('esx:showNotification', _source, _U('contact_added')) - TriggerClientEvent('esx_phone:addContact', _source, contactName, phoneNumber, playerOnline) + TriggerClientEvent('esx:showNotification', playerId, _U('contact_added')) + TriggerClientEvent('esx_phone:addContact', playerId, contactName, phoneNumber, playerOnline) end) end else - TriggerClientEvent('esx:showNotification', _source, _U('number_not_assigned')) + TriggerClientEvent('esx:showNotification', playerId, _U('number_not_assigned')) end end) end) RegisterServerEvent('esx_phone:removePlayerContact') AddEventHandler('esx_phone:removePlayerContact', function(phoneNumber, contactName) - local _source = source - local xPlayer = ESX.GetPlayerFromId(_source) + local playerId = source + local xPlayer = ESX.GetPlayerFromId(playerId) local foundNumber = false MySQL.Async.fetchAll('SELECT phone_number FROM users WHERE phone_number = @number', { ['@number'] = phoneNumber }, function(result) - if result[1] ~= nil then + if result[1] then foundNumber = true end @@ -281,17 +274,16 @@ AddEventHandler('esx_phone:removePlayerContact', function(phoneNumber, contactNa xPlayer.set('contacts', contacts) - MySQL.Async.execute('DELETE FROM user_contacts WHERE identifier=@identifier AND name=@name AND number=@number', - { + MySQL.Async.execute('DELETE FROM user_contacts WHERE identifier = @identifier AND name = @name AND number = @number', { ['@identifier'] = xPlayer.identifier, ['@name'] = contactName, ['@number'] = phoneNumber }, function(rowsChanged) - TriggerClientEvent('esx:showNotification', _source, _U('contact_removed')) - TriggerClientEvent('esx_phone:removeContact', _source, contactName, phoneNumber) + TriggerClientEvent('esx:showNotification', playerId, _U('contact_removed')) + TriggerClientEvent('esx_phone:removeContact', playerId, contactName, phoneNumber) end) else - TriggerClientEvent('esx:showNotification', _source, _U('number_not_assigned')) + TriggerClientEvent('esx:showNotification', playerId, _U('number_not_assigned')) end end) end) @@ -299,4 +291,4 @@ end) RegisterServerEvent('esx_phone:stopDispatch') AddEventHandler('esx_phone:stopDispatch', function(dispatchRequestId) TriggerClientEvent('esx_phone:stopDispatch', -1, dispatchRequestId, GetPlayerName(source)) -end) +end) \ No newline at end of file From ffb733cfc703ffac426f95bb0d873654e8474ab3 Mon Sep 17 00:00:00 2001 From: rex2630 Date: Sun, 19 Jan 2020 17:21:15 +0100 Subject: [PATCH 73/76] Replace __resource.lua with fxmanifest.lua --- __resource.lua => fxmanifest.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) rename __resource.lua => fxmanifest.lua (91%) diff --git a/__resource.lua b/fxmanifest.lua similarity index 91% rename from __resource.lua rename to fxmanifest.lua index 2f2390ac..8e318256 100644 --- a/__resource.lua +++ b/fxmanifest.lua @@ -1,4 +1,6 @@ -resource_manifest_version '44febabe-d386-4d18-afbe-5e627f4af937' +fx_version 'adamant' + +game 'gta5' description 'ESX Phone' @@ -55,4 +57,4 @@ files { 'html/img/icons/location.png' } -dependency 'es_extended' \ No newline at end of file +dependency 'es_extended' From ad4f678399719caa2763172c719990568c7b19b2 Mon Sep 17 00:00:00 2001 From: medina102 Date: Mon, 24 Feb 2020 07:13:06 +0100 Subject: [PATCH 74/76] Update en.lua Typo corrected. --- locales/en.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.lua b/locales/en.lua index f7c4babe..0735fe34 100644 --- a/locales/en.lua +++ b/locales/en.lua @@ -1,5 +1,5 @@ Locales['en'] = { - ['new_message'] = '~b~You have recived a message:~s~ %s', + ['new_message'] = '~b~You have received a message:~s~ %s', ['press_take_call'] = '%s - Press ~INPUT_CONTEXT~ to take the call', ['taken_call'] = '~y~%s~s~ has taken the call', ['gps_position'] = 'the destination has been added to your GPS', From 11e9bd7059bec1c0b7cb04e029e07838043ff52b Mon Sep 17 00:00:00 2001 From: ElPumpo Date: Tue, 14 Apr 2020 16:51:13 +0200 Subject: [PATCH 75/76] Latest ESX code --- README.md | 2 +- esx_phone.sql | 2 +- html/scripts/app_en.js | 6 +++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 96fa6c1a..0016229b 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ last two booleans are optional ### License esx_phone - phone script for fivem -Copyright (C) 2015-2019 Jérémie N'gadi +Copyright (C) 2015-2020 Jérémie N'gadi This program Is free software: you can redistribute it And/Or modify it under the terms Of the GNU General Public License As published by the Free Software Foundation, either version 3 Of the License, Or (at your option) any later version. diff --git a/esx_phone.sql b/esx_phone.sql index 220d5ad1..f501d301 100644 --- a/esx_phone.sql +++ b/esx_phone.sql @@ -1,4 +1,4 @@ -USE `essentialmode`; +USE `es_extended`; CREATE TABLE `user_contacts` ( `id` INT(11) NOT NULL AUTO_INCREMENT, diff --git a/html/scripts/app_en.js b/html/scripts/app_en.js index 21a3caf0..d4ea6746 100644 --- a/html/scripts/app_en.js +++ b/html/scripts/app_en.js @@ -501,7 +501,11 @@ } - window.onload = function(e){ window.addEventListener('message', function(event){ onData(event.data) }); } + window.onload = function(e){ + window.addEventListener('message', function(event) { + onData(event.data) + }); + } document.onkeydown = function (data) { if ((data.which == 120 || data.which == 27) && isPhoneShowed) { // || data.which == 8 From a36be2deb4d5f2cfbe3c5853bd42ad31861d1886 Mon Sep 17 00:00:00 2001 From: HypeLevels <61633775+HypeLevels@users.noreply.github.com> Date: Tue, 1 Dec 2020 21:43:58 +0000 Subject: [PATCH 76/76] Update config.lua --- config.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.lua b/config.lua index 56a8b5e9..5290495d 100644 --- a/config.lua +++ b/config.lua @@ -1,2 +1,2 @@ Config = {} -Config.Locale = 'fr' +Config.Locale = 'en'