Files
Leon.Schmidt 30559048fd FIX - complete LB custom app compatibility (#11)
* FIX - preserve LB custom app lifecycle

* FIX - reset LB export aliases before startup

* FIX - report competing custom app providers

* FIX - identify LB app frames as live NUI

* FIX - complete LB custom app compatibility

* FIX - provide LB PicChat runtime exports

* FIX - route LB PicChat through Sky Phone

* FIX - return cached LB equipped phone number

* FIX - harden PicChat compatibility migration

* ENH - complete modular phone provider and Creator APIs

* DOC - document the Sky Phone Creator API

---------

Co-authored-by: DerEchteAlec <bycraky@gmail.com>
Co-authored-by: DerEchteAlec <alec.schitzkat@luwan.io>
2026-08-20 18:19:16 +02:00

177 lines
5.1 KiB
Lua

SkyPhoneCalls = {}
local active_call_payload = nil
local call_channel = 0
local function copy_payload(value)
if type(value) ~= "table" then
return value
end
local copy = {}
for key, nested_value in pairs(value) do
copy[key] = copy_payload(nested_value)
end
return copy
end
local function emit_call_changed(call)
TriggerEvent("sky_phone:client:callChanged", copy_payload(call))
end
local function request_call_action(action, call)
local result = Bridge.Callbacks.Trigger("sky_phone:calls:" .. action, { id = call.id })
if not result or not result.success then
local call_error = result and result.error or "request_failed"
Bridge.Debug(
"error",
"[sky_phone] Call %s request failed: %s.",
action,
tostring(call_error)
)
return false, call_error
end
return true
end
local function leave_voice()
if call_channel == 0 then
return
end
Bridge.Calls.Leave()
call_channel = 0
end
local function join_voice(channel)
local next_channel = tonumber(channel) or 0
if not Bridge.Calls.Join(next_channel) then
return false
end
call_channel = next_channel
return true
end
function SkyPhoneCalls.Dial(phone_number, company)
if (phone_number ~= nil and (type(phone_number) ~= "string" or phone_number == ""))
or (company ~= nil and (type(company) ~= "string" or company == ""))
or (phone_number == nil and company == nil)
then
Bridge.Debug("error", "[sky_phone] Rejected invalid call target.")
return false, "invalid_request"
end
local result = Bridge.Callbacks.Trigger("sky_phone:calls:dial", {
company = company,
phoneNumber = phone_number,
})
if not result or not result.success then
local call_error = result and result.error or "request_failed"
Bridge.Debug("error", "[sky_phone] Call request failed: %s.", tostring(call_error))
return false, call_error
end
return true
end
function SkyPhoneCalls.IsActive()
return active_call_payload ~= nil
end
function SkyPhoneCalls.GetActive()
if not active_call_payload then
return nil
end
return copy_payload(active_call_payload)
end
function SkyPhoneCalls.Answer()
local call = active_call_payload
if not call or call.state ~= "ringing" or call.direction ~= "incoming" then
return false, "call_not_found"
end
return request_call_action("answer", call)
end
function SkyPhoneCalls.Decline()
local call = active_call_payload
if not call or call.state ~= "ringing" or call.direction ~= "incoming" then
return false, "call_not_found"
end
return request_call_action("decline", call)
end
function SkyPhoneCalls.Hangup()
local call = active_call_payload
if not call or (call.state ~= "ringing" and call.state ~= "connected") then
return false, "call_not_found"
end
return request_call_action("hangup", call)
end
function SkyPhoneCalls.Terminate()
local call = active_call_payload
if not call or (call.state ~= "ringing" and call.state ~= "connected") then
return false, "call_not_found"
end
return request_call_action("terminate", call)
end
function SkyPhoneCalls.ReleaseFocus()
SkyPhoneFocus.SetCall(false)
end
function SkyPhoneCalls.ReplayNui()
if active_call_payload then
SendNUIMessage({ type = "call:state", data = active_call_payload })
end
end
function SkyPhoneCalls.Reset()
local had_active_call = active_call_payload ~= nil
active_call_payload = nil
SkyPhoneFocus.SetCall(false)
leave_voice()
if had_active_call then
emit_call_changed(nil)
end
end
RegisterNetEvent("sky_phone:call:incoming", function(data)
if type(data) ~= "table" or type(data.id) ~= "string" or data.state ~= "ringing" then
Bridge.Debug("error", "[sky_phone] Rejected invalid incoming call data.")
return
end
active_call_payload = data
SkyPhoneFocus.SetCall(true)
emit_call_changed(data)
TriggerEvent("sky_phone:animation:call", data)
SendNUIMessage({ type = "call:incoming", data = data })
end)
RegisterNetEvent("sky_phone:call:state", function(data)
if type(data) ~= "table" or type(data.id) ~= "string" or type(data.state) ~= "string" then
Bridge.Debug("error", "[sky_phone] Rejected invalid call state data.")
return
end
if data.state == "ringing" or data.state == "connected" then
active_call_payload = data
end
if data.state ~= "ringing" then
SkyPhoneFocus.SetCall(false)
end
if data.state ~= "ringing" and data.state ~= "connected" then
active_call_payload = nil
end
if data.state == "connected" and data.channel then
if not join_voice(data.channel) then
TriggerEvent("sky_phone:device:error", "voice_unavailable")
end
elseif data.state ~= "ringing" then
leave_voice()
end
emit_call_changed(data)
TriggerEvent("sky_phone:animation:call", data)
SendNUIMessage({ type = "call:state", data = data })
end)