mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-08-29 00:01:29 +00:00
5f8c835685
This reverts commit edd9677f43.
385 lines
11 KiB
Lua
385 lines
11 KiB
Lua
SkyPhoneClient = {}
|
|
|
|
local is_open = false
|
|
local open_requested = false
|
|
local open_without_focus = false
|
|
local device_payload = nil
|
|
local equipped_phone_number = nil
|
|
local nui_generation = 0
|
|
local live_activity_active = false
|
|
local open_home_requested = false
|
|
|
|
local function get_equipped_phone_number()
|
|
if not device_payload or not device_payload.device.sim then
|
|
return nil
|
|
end
|
|
|
|
return device_payload.device.sim.number
|
|
end
|
|
|
|
local function set_equipped_phone_number(next_number)
|
|
if next_number ~= nil and type(next_number) ~= "string" then
|
|
next_number = nil
|
|
end
|
|
if next_number == equipped_phone_number then
|
|
return
|
|
end
|
|
|
|
equipped_phone_number = next_number
|
|
TriggerEvent("sky_phone:client:phoneNumberChanged", next_number)
|
|
end
|
|
|
|
local function update_equipped_phone_number(data)
|
|
set_equipped_phone_number(type(data) == "table"
|
|
and type(data.device) == "table"
|
|
and type(data.device.sim) == "table"
|
|
and data.device.sim.number
|
|
or nil)
|
|
end
|
|
|
|
local function get_authoritative_phone_number()
|
|
local result = Bridge.Callbacks.Trigger("sky_phone:device:equipped-number", {})
|
|
if type(result) ~= "table" or not result.success or type(result.data) ~= "table" then
|
|
return nil
|
|
end
|
|
|
|
local phone_number = result.data.phoneNumber
|
|
set_equipped_phone_number(phone_number)
|
|
return phone_number
|
|
end
|
|
|
|
local function get_phone_state()
|
|
return {
|
|
inCall = SkyPhoneCalls.IsActive(),
|
|
onScreen = is_open,
|
|
open = is_open,
|
|
phoneNumber = get_equipped_phone_number(),
|
|
}
|
|
end
|
|
|
|
SkyPhoneClient.GetState = get_phone_state
|
|
SkyPhoneClient.GetEquippedPhoneNumber = get_authoritative_phone_number
|
|
|
|
Bridge.Debug("debug", "[sky_phone] Client script initialized.", { always = true })
|
|
|
|
local locale, locale_name = SkyPhoneLocales.Resolve(Config.Bridge.Locale)
|
|
|
|
local function send_open_message()
|
|
if not device_payload then
|
|
return
|
|
end
|
|
|
|
local payload = device_payload
|
|
payload.lang = locale_name
|
|
payload.locales = locale.Nui
|
|
payload.fallbackLocales = Locales.en.Nui
|
|
SkyPhoneApps.SendCatalog()
|
|
SendNUIMessage({
|
|
type = "app:open",
|
|
data = payload,
|
|
openHome = open_home_requested,
|
|
})
|
|
open_home_requested = false
|
|
end
|
|
|
|
local function open_phone()
|
|
if is_open or not device_payload then
|
|
return
|
|
end
|
|
|
|
send_open_message()
|
|
end
|
|
|
|
local function close_phone(close_device_session)
|
|
local was_requested = open_requested
|
|
local was_open = is_open
|
|
open_requested = false
|
|
open_without_focus = false
|
|
TriggerEvent("sky_phone:animation:phone", false)
|
|
is_open = false
|
|
if was_open then
|
|
SkyPhoneApps.SetPhoneOpen(false)
|
|
TriggerEvent("sky_phone:nuiClosed")
|
|
TriggerEvent("sky_phone:client:phoneToggled", false)
|
|
end
|
|
SkyPhoneFocus.SetPhone(false)
|
|
if was_requested or was_open then
|
|
SendNUIMessage({ type = "app:close" })
|
|
if close_device_session ~= false then
|
|
Bridge.Callbacks.Trigger("sky_phone:device:close", {})
|
|
end
|
|
end
|
|
end
|
|
|
|
local function toggle_phone(open, no_focus)
|
|
if open ~= nil and type(open) ~= "boolean" then
|
|
Bridge.Debug("error", "[sky_phone] Rejected invalid phone toggle state.")
|
|
return false
|
|
end
|
|
if no_focus ~= nil and type(no_focus) ~= "boolean" then
|
|
Bridge.Debug("error", "[sky_phone] Rejected invalid phone toggle focus state.")
|
|
return false
|
|
end
|
|
|
|
local should_open = open
|
|
if should_open == nil then
|
|
should_open = not (is_open or open_requested)
|
|
end
|
|
if not should_open then
|
|
close_phone()
|
|
return true
|
|
end
|
|
|
|
open_without_focus = no_focus == true
|
|
if is_open or open_requested then
|
|
if is_open then
|
|
SkyPhoneFocus.SetPhone(true, open_without_focus)
|
|
end
|
|
return true
|
|
end
|
|
|
|
local result = Bridge.Callbacks.Trigger("sky_phone:device:open-request", {})
|
|
if type(result) ~= "table" or result.success ~= true then
|
|
open_without_focus = false
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
|
|
SkyPhoneClient.Toggle = toggle_phone
|
|
|
|
AddEventHandler("sky_phone:client:forceClose", function()
|
|
close_phone()
|
|
end)
|
|
|
|
if Config.Phone.DevelopmentCommand then
|
|
RegisterCommand(Config.Command, function()
|
|
if is_open or open_requested then
|
|
close_phone()
|
|
return
|
|
end
|
|
open_without_focus = false
|
|
Bridge.Callbacks.Trigger("sky_phone:device:development-open", {})
|
|
end, false)
|
|
end
|
|
|
|
RegisterCommand("sky_phone_toggle", function()
|
|
if is_open or open_requested then
|
|
close_phone()
|
|
return
|
|
end
|
|
|
|
open_without_focus = false
|
|
Bridge.Callbacks.Trigger("sky_phone:device:open-request", {})
|
|
end, false)
|
|
|
|
RegisterCommand("sky_phone_live_activity_open", function()
|
|
if not live_activity_active or is_open or open_requested then
|
|
return
|
|
end
|
|
|
|
open_home_requested = true
|
|
local result = Bridge.Callbacks.Trigger("sky_phone:device:open-request", {})
|
|
if not result or not result.success then
|
|
open_home_requested = false
|
|
end
|
|
end, false)
|
|
|
|
RegisterKeyMapping(
|
|
"sky_phone_live_activity_open",
|
|
locale.Controls.OpenPhone,
|
|
"keyboard",
|
|
"SPACE"
|
|
)
|
|
|
|
if Config.Phone.Keybind then
|
|
if type(Config.Phone.Keybind) ~= "string" or Config.Phone.Keybind == "" then
|
|
error("[sky_phone] Config.Phone.Keybind must be a non-empty keyboard key name or false.")
|
|
end
|
|
|
|
RegisterKeyMapping("sky_phone_toggle", locale.Controls.OpenPhone, "keyboard", Config.Phone.Keybind)
|
|
end
|
|
|
|
RegisterNetEvent("sky_phone:testdata:feedback", function(success, detail)
|
|
local test_data_locale = locale.TestData
|
|
local message = success and test_data_locale.Success or test_data_locale.Failed
|
|
if success and type(detail) == "string" and detail ~= "" then
|
|
message = message:gsub("{email}", detail)
|
|
end
|
|
Bridge.Framework.Notify("iFruit", message, success and "success" or "error", 7000)
|
|
end)
|
|
|
|
RegisterNUICallback("ui:ready", function(data, cb)
|
|
if type(data) ~= "table" or data.protocolVersion ~= 1 then
|
|
cb({ success = false, error = "unsupported_protocol" })
|
|
return
|
|
|
|
end
|
|
nui_generation = nui_generation + 1
|
|
-- Browser state is recreated on a CEF reload. Browser-owned focus claims
|
|
-- cannot survive unless their UI is replayed as part of this handshake.
|
|
SkyPhoneFocus.BeginNuiHydration()
|
|
Bridge.Debug("debug", "[sky_phone] NUI reported ready.", { always = true })
|
|
SkyPhoneApps.SendCatalog()
|
|
if open_requested and device_payload then
|
|
send_open_message()
|
|
end
|
|
SkyPhoneCalls.ReplayNui()
|
|
SkyPhoneSimPicker.ReplayNui()
|
|
SkyPhoneFocus.Reapply()
|
|
TriggerEvent("sky_phone:client:nuiReady", {
|
|
generation = nui_generation,
|
|
protocolVersion = 1,
|
|
})
|
|
|
|
cb({
|
|
success = true,
|
|
data = {
|
|
generation = nui_generation,
|
|
protocolVersion = 1,
|
|
},
|
|
})
|
|
end)
|
|
|
|
RegisterNUICallback("ui:opened", function(data, cb)
|
|
if type(data) ~= "table" then
|
|
cb({ success = false, error = "invalid_request" })
|
|
return
|
|
end
|
|
if not open_requested or not device_payload then
|
|
Bridge.Debug(
|
|
"warn",
|
|
"[sky_phone] Ignored a NUI open confirmation without a pending device open.",
|
|
{ always = true }
|
|
)
|
|
SendNUIMessage({ type = "app:close" })
|
|
SkyPhoneFocus.Reapply()
|
|
cb({ success = false, error = "open_not_requested" })
|
|
return
|
|
end
|
|
|
|
local was_open = is_open
|
|
is_open = true
|
|
SkyPhoneApps.SetPhoneOpen(true)
|
|
if not was_open then
|
|
TriggerEvent("sky_phone:client:phoneToggled", true)
|
|
end
|
|
SkyPhoneFocus.SetPhone(true, open_without_focus)
|
|
TriggerEvent("sky_phone:animation:phone", true)
|
|
cb({ success = true })
|
|
end)
|
|
|
|
RegisterNUICallback("ui:input-focus", function(data, cb)
|
|
if type(data) ~= "table" or type(data.active) ~= "boolean" then
|
|
cb({ success = false, error = "invalid_request" })
|
|
return
|
|
end
|
|
SkyPhoneFocus.SetTextInputFocused(data.active and (is_open or open_requested))
|
|
cb({ success = true })
|
|
end)
|
|
|
|
RegisterNUICallback("ui:live-activity", function(data, cb)
|
|
if type(data) ~= "table" or type(data.active) ~= "boolean" then
|
|
cb({ success = false, error = "invalid_request" })
|
|
return
|
|
end
|
|
live_activity_active = data.active
|
|
cb({ success = true })
|
|
end)
|
|
|
|
RegisterNUICallback("close", function(data, cb)
|
|
if type(data) ~= "table" then
|
|
cb({ success = false, error = "invalid_request" })
|
|
return
|
|
end
|
|
close_phone()
|
|
cb({ success = true })
|
|
end)
|
|
|
|
RegisterNetEvent("sky_phone:device:open", function(data)
|
|
if type(data) ~= "table" or type(data.device) ~= "table" or type(data.device.imei) ~= "string" then
|
|
Bridge.Debug("error", "[sky_phone] Rejected invalid device open data.")
|
|
return
|
|
end
|
|
Bridge.Debug(
|
|
"debug",
|
|
"[sky_phone] Client received device open for IMEI %s account_linked=%s.",
|
|
tostring(data.device.imei),
|
|
tostring(data.account ~= nil),
|
|
{ always = true }
|
|
)
|
|
device_payload = data
|
|
update_equipped_phone_number(data)
|
|
open_requested = true
|
|
if is_open then
|
|
SkyPhoneApps.SendCatalog()
|
|
SendNUIMessage({ type = "device:updated", data = data })
|
|
return
|
|
end
|
|
open_phone()
|
|
end)
|
|
|
|
RegisterNetEvent("sky_phone:device:updated", function(data)
|
|
if type(data) ~= "table" or type(data.device) ~= "table" or type(data.device.imei) ~= "string" then
|
|
Bridge.Debug("error", "[sky_phone] Rejected invalid device update data.")
|
|
return
|
|
end
|
|
device_payload = data
|
|
update_equipped_phone_number(data)
|
|
SendNUIMessage({ type = "device:updated", data = data })
|
|
end)
|
|
|
|
RegisterNetEvent("sky_phone:device:invalidated", function()
|
|
TriggerEvent("sky_phone:animation:reset")
|
|
close_phone(false)
|
|
device_payload = nil
|
|
update_equipped_phone_number(nil)
|
|
live_activity_active = false
|
|
open_home_requested = false
|
|
end)
|
|
|
|
RegisterNetEvent("sky_phone:device:error", function(error_code)
|
|
if not is_open and not open_requested then
|
|
open_without_focus = false
|
|
end
|
|
Bridge.Debug(
|
|
"debug",
|
|
"[sky_phone] Client received device error: %s.",
|
|
tostring(error_code),
|
|
{ always = true }
|
|
)
|
|
local message = locale.DeviceErrors[error_code] or locale.DeviceErrors.default
|
|
Bridge.Framework.Notify("iFruit", message, "error", 5000)
|
|
end)
|
|
|
|
CreateThread(function()
|
|
if Config.Phone.DevelopmentCommand then
|
|
TriggerEvent("chat:addSuggestion", "/" .. Config.Command, locale.CommandDescription)
|
|
end
|
|
if Config.TestData.Enabled then
|
|
TriggerEvent("chat:addSuggestion", "/" .. Config.TestData.Command, locale.TestData.CommandDescription)
|
|
end
|
|
end)
|
|
|
|
AddEventHandler("onResourceStop", function(resource_name)
|
|
if resource_name ~= GetCurrentResourceName() then
|
|
return
|
|
end
|
|
|
|
is_open = false
|
|
open_requested = false
|
|
open_without_focus = false
|
|
|
|
TriggerEvent("sky_phone:animation:reset")
|
|
SkyPhoneCalls.Reset()
|
|
SkyPhoneSimPicker.Reset()
|
|
SkyPhoneFocus.Reset()
|
|
|
|
if Config.Phone.DevelopmentCommand then
|
|
TriggerEvent("chat:removeSuggestion", "/" .. Config.Command)
|
|
end
|
|
if Config.TestData.Enabled then
|
|
TriggerEvent("chat:removeSuggestion", "/" .. Config.TestData.Command)
|
|
end
|
|
end)
|