mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-08-29 01:01:31 +00:00
FIX - repair company actions and key rebindings (#32)
* FIX - repair company detail actions Constrain compact navbar titles to their grid column so long company names cannot overlap the back action. Allow configured public emergency companies to accept normal service requests, and migrate existing police profile and Phone Configurator defaults exactly once. * FIX - preserve phone key rebindings Use the stable sky_phone_toggle command as the RegisterKeyMapping identifier. The previous revisioned command names detached FiveM's persisted keyboard settings from the active handler whenever the mapping identity changed.
This commit is contained in:
@@ -1181,7 +1181,7 @@ if IsDuplicityVersion() then
|
||||
LogoUrl = "https://picsum.photos/seed/companies-police-logo/180/180",
|
||||
Description = "Public safety, emergency response, and police services.",
|
||||
DefaultAvailability = "closed",
|
||||
AcceptsRequests = false,
|
||||
AcceptsRequests = true,
|
||||
District = "Mission Row",
|
||||
LocationLabel = "Mission Row Police Station",
|
||||
Address = "Mission Row Police Station",
|
||||
@@ -1203,7 +1203,15 @@ if IsDuplicityVersion() then
|
||||
Services = 3,
|
||||
Announcement = 3,
|
||||
},
|
||||
Services = {},
|
||||
Services = {
|
||||
{
|
||||
Id = "police-assistance",
|
||||
Title = "Police assistance",
|
||||
Description = "Request non-emergency police assistance.",
|
||||
Price = "",
|
||||
RequestsEnabled = true,
|
||||
},
|
||||
},
|
||||
},
|
||||
ambulance = {
|
||||
Job = "ambulance",
|
||||
|
||||
@@ -13,9 +13,7 @@ local suggested_admin_command = nil
|
||||
local suggested_test_data_command = nil
|
||||
local active_development_command = nil
|
||||
local registered_development_commands = {}
|
||||
local active_key_mapping_command = nil
|
||||
local active_key_mapping_key = nil
|
||||
local key_mapping_revision = 0
|
||||
local phone_key_mapping_registered = false
|
||||
local refresh_development_command
|
||||
local refresh_phone_key_mapping
|
||||
local refresh_test_data_command_suggestion
|
||||
@@ -312,32 +310,30 @@ RegisterCommand("sky_phone_live_activity_open", function()
|
||||
end
|
||||
end, false)
|
||||
|
||||
RegisterCommand("sky_phone_toggle", run_phone_toggle, false)
|
||||
RegisterCommand("sky_phone_toggle", function()
|
||||
if not Config.Phone.Keybind then
|
||||
return
|
||||
end
|
||||
run_phone_toggle()
|
||||
end, false)
|
||||
|
||||
refresh_phone_key_mapping = function()
|
||||
local key_name = Config.Phone.Keybind
|
||||
if key_name ~= false and key_name ~= nil and (type(key_name) ~= "string" or key_name == "") then
|
||||
error("[sky_phone] Config.Phone.Keybind must be a non-empty keyboard key name or false.")
|
||||
end
|
||||
if key_name == active_key_mapping_key then
|
||||
if phone_key_mapping_registered or not key_name then
|
||||
return
|
||||
end
|
||||
|
||||
active_key_mapping_key = key_name
|
||||
active_key_mapping_command = nil
|
||||
if not key_name then
|
||||
return
|
||||
end
|
||||
|
||||
key_mapping_revision = key_mapping_revision + 1
|
||||
local command_name = "sky_phone_toggle_config_" .. key_mapping_revision
|
||||
active_key_mapping_command = command_name
|
||||
RegisterCommand(command_name, function()
|
||||
if active_key_mapping_command == command_name then
|
||||
run_phone_toggle()
|
||||
end
|
||||
end, false)
|
||||
RegisterKeyMapping(command_name, locale.Controls.OpenPhone, "keyboard", key_name)
|
||||
-- FiveM persists player rebindings by command name, so this identifier must remain stable.
|
||||
phone_key_mapping_registered = true
|
||||
RegisterKeyMapping(
|
||||
"sky_phone_toggle",
|
||||
locale.Controls.OpenPhone,
|
||||
"keyboard",
|
||||
key_name
|
||||
)
|
||||
end
|
||||
|
||||
refresh_phone_key_mapping()
|
||||
|
||||
@@ -279,7 +279,6 @@ local function validate_configuration()
|
||||
or not Config.Companies.AvailabilityStatuses[definition.DefaultAvailability]
|
||||
or not valid_text(definition.Icon, 64, false)
|
||||
or not logo_url or not logo_url:match("^https://[^%s]+$")
|
||||
or (definition.Emergency and definition.AcceptsRequests)
|
||||
then
|
||||
error(("[sky_phone] Company definition '%s' has invalid public profile defaults."):format(company_id))
|
||||
end
|
||||
@@ -466,6 +465,48 @@ local function seed_companies()
|
||||
|
||||
end
|
||||
|
||||
local function migrate_requestable_emergency_companies()
|
||||
local migration_name = "sky-phone:companies:requestable-emergency:v1"
|
||||
local completed = Bridge.Database.Query(
|
||||
"SELECT 1 FROM `sky_phone_migrations` WHERE `name` = ? LIMIT 1",
|
||||
{ migration_name }
|
||||
)
|
||||
if completed[1] then
|
||||
return
|
||||
end
|
||||
|
||||
local statements = {}
|
||||
local migrated_companies = {}
|
||||
for _, company_id in ipairs(definition_ids) do
|
||||
local definition = definitions[company_id]
|
||||
if definition.Emergency and definition.AcceptsRequests then
|
||||
statements[#statements + 1] = {
|
||||
query = [[
|
||||
UPDATE `sky_phone_company_profiles`
|
||||
SET `accepts_requests` = 1, `revision` = `revision` + 1
|
||||
WHERE `company_id` = ? AND `accepts_requests` = 0
|
||||
]],
|
||||
params = { company_id },
|
||||
}
|
||||
migrated_companies[#migrated_companies + 1] = company_id
|
||||
end
|
||||
end
|
||||
statements[#statements + 1] = {
|
||||
query = [[
|
||||
INSERT IGNORE INTO `sky_phone_migrations` (`name`, `source`, `stats`)
|
||||
VALUES (?, ?, ?)
|
||||
]],
|
||||
params = {
|
||||
migration_name,
|
||||
"sky-phone",
|
||||
json.encode({ companies = migrated_companies }),
|
||||
},
|
||||
}
|
||||
if not Bridge.Database.Transaction(statements) then
|
||||
error("[sky_phone] Could not migrate requestable emergency company profiles.")
|
||||
end
|
||||
end
|
||||
|
||||
local function tombstone_removed_companies()
|
||||
local profiles = Bridge.Database.Query([[
|
||||
SELECT DISTINCT profile.`company_id`
|
||||
@@ -826,7 +867,7 @@ local function company_payload(company_id, include_inactive_services)
|
||||
availability = availability,
|
||||
availabilityUpdatedAt = iso_time(row.availability_updated_at_unix)
|
||||
or iso_time(row.updated_at_unix),
|
||||
acceptsRequests = tonumber(row.accepts_requests) == 1 and not definition.Emergency,
|
||||
acceptsRequests = tonumber(row.accepts_requests) == 1,
|
||||
phoneNumber = line and line.Number or nil,
|
||||
canCall = line and line.CanCall == true or false,
|
||||
canMessage = line and line.CanMessage == true or false,
|
||||
@@ -912,6 +953,7 @@ local function refresh_runtime_configuration()
|
||||
service_lines_by_number = {}
|
||||
validate_configuration()
|
||||
seed_companies()
|
||||
migrate_requestable_emergency_companies()
|
||||
tombstone_removed_companies()
|
||||
end
|
||||
|
||||
@@ -1839,7 +1881,7 @@ Bridge.Callbacks.Register("sky_phone:companies:create-request", function(source,
|
||||
end
|
||||
local company_id = data.companyId
|
||||
local definition = type(company_id) == "string" and definitions[company_id] or nil
|
||||
if not definition or not definition.Public or definition.Emergency then
|
||||
if not definition or not definition.Public then
|
||||
return { success = false, error = "company_not_found" }
|
||||
end
|
||||
local subject = valid_text(data.subject, Config.Companies.SubjectMaxLength, false)
|
||||
@@ -2636,7 +2678,6 @@ Bridge.Callbacks.Register("sky_phone:companies:update-profile", function(source,
|
||||
local address = valid_text(data.address, Config.Companies.AddressMaxLength, true)
|
||||
if not revision or not description or not district or not location_label or not address
|
||||
or type(data.acceptsRequests) ~= "boolean"
|
||||
or (member.definition.Emergency and data.acceptsRequests)
|
||||
then
|
||||
return { success = false, error = "invalid_profile" }
|
||||
end
|
||||
|
||||
@@ -1075,6 +1075,69 @@ local function apply_stored_row(row)
|
||||
updated_by_name = row.updated_by_name
|
||||
end
|
||||
|
||||
local function migrate_police_request_defaults()
|
||||
local migration_name = "sky-phone:configurator:police-requests:v1"
|
||||
local completed = Bridge.Database.Query(
|
||||
"SELECT 1 FROM `sky_phone_migrations` WHERE `name` = ? LIMIT 1",
|
||||
{ migration_name }
|
||||
)
|
||||
if completed[1] then
|
||||
return
|
||||
end
|
||||
|
||||
local row = read_stored_row()
|
||||
local config_payload = decode_payload(row.config_payload, "config")
|
||||
local police = config_payload.Companies
|
||||
and config_payload.Companies.Definitions
|
||||
and config_payload.Companies.Definitions.police
|
||||
local migrated = type(police) == "table"
|
||||
and police.Emergency == true
|
||||
and police.AcceptsRequests == false
|
||||
and type(police.Services) == "table"
|
||||
and next(police.Services) == nil
|
||||
local statements = {}
|
||||
if migrated then
|
||||
local defaults = default_config.Companies.Definitions.police
|
||||
police.AcceptsRequests = defaults.AcceptsRequests
|
||||
police.Services = copy_value(defaults.Services)
|
||||
statements[#statements + 1] = {
|
||||
query = ([[
|
||||
UPDATE `%s`
|
||||
SET `config_payload` = ?, `revision` = `revision` + 1
|
||||
WHERE `id` = ?
|
||||
]]):format(TABLE_NAME),
|
||||
params = { encode_payload(config_payload, "config"), CONFIG_ROW_ID },
|
||||
}
|
||||
end
|
||||
statements[#statements + 1] = {
|
||||
query = [[
|
||||
INSERT IGNORE INTO `sky_phone_migrations` (`name`, `source`, `stats`)
|
||||
VALUES (?, ?, ?)
|
||||
]],
|
||||
params = {
|
||||
migration_name,
|
||||
"sky-phone",
|
||||
json.encode({ migrated = migrated }),
|
||||
},
|
||||
}
|
||||
if not Bridge.Database.Transaction(statements) then
|
||||
error("[sky_phone] Could not migrate Phone Configurator police request defaults.")
|
||||
end
|
||||
if not migrated then
|
||||
return
|
||||
end
|
||||
|
||||
apply_stored_row(read_stored_row())
|
||||
apply_runtime_configuration()
|
||||
TriggerEvent("sky_phone:configurator:serverUpdated", revision)
|
||||
SkyPhoneConfigurator.Broadcast(-1)
|
||||
Bridge.Debug(
|
||||
"info",
|
||||
"[sky_phone] Migrated Phone Configurator police request defaults.",
|
||||
{ always = true }
|
||||
)
|
||||
end
|
||||
|
||||
default_config = {}
|
||||
for key, value in pairs(ConfigDefaults) do
|
||||
if key ~= "Media" and key ~= "PhoneConfigurator" and key ~= "CommandPermissions" then
|
||||
@@ -1095,6 +1158,7 @@ Bridge.Database.Query(([[
|
||||
|
||||
apply_stored_row(read_stored_row())
|
||||
apply_runtime_configuration()
|
||||
Bridge.Database.AfterMigration("sky_phone", migrate_police_request_defaults)
|
||||
|
||||
function SkyPhoneConfigurator.GetAdminData()
|
||||
local data = build_admin_data()
|
||||
|
||||
@@ -1166,7 +1166,7 @@ if IsDuplicityVersion() then
|
||||
LogoUrl = "https://picsum.photos/seed/companies-police-logo/180/180",
|
||||
Description = "Public safety, emergency response, and police services.",
|
||||
DefaultAvailability = "closed",
|
||||
AcceptsRequests = false,
|
||||
AcceptsRequests = true,
|
||||
District = "Mission Row",
|
||||
LocationLabel = "Mission Row Police Station",
|
||||
Address = "Mission Row Police Station",
|
||||
@@ -1188,7 +1188,15 @@ if IsDuplicityVersion() then
|
||||
Services = 3,
|
||||
Announcement = 3,
|
||||
},
|
||||
Services = {},
|
||||
Services = {
|
||||
{
|
||||
Id = "police-assistance",
|
||||
Title = "Police assistance",
|
||||
Description = "Request non-emergency police assistance.",
|
||||
Price = "",
|
||||
RequestsEnabled = true,
|
||||
},
|
||||
},
|
||||
},
|
||||
ambulance = {
|
||||
Job = "ambulance",
|
||||
|
||||
Reference in New Issue
Block a user