# External Apps System - codem-phone This system allows external FiveM resources to add custom apps inside the phone. ## Quick Start ### 1. Client-side: Register Your App ```lua -- client/main.lua CreateThread(function() -- Wait for codem-phone to start while GetResourceState('codem-phone') ~= 'started' do Wait(100) end Wait(1000) -- Small delay -- Read HTML content local htmlContent = LoadResourceFile(GetCurrentResourceName(), 'ui/index.html') -- Register the app exports['codem-phone']:AddCustomApp({ identifier = 'myapp', -- Unique ID (required) name = 'My Custom App', -- Display name (required) icon = 'nui://myresource/icon.png', -- Icon URL (optional) ui = htmlContent, -- HTML content (required) description = 'An awesome app', -- Description (optional) defaultApp = false, -- Is default app? (optional) notification = true, -- Show notifications? (optional) onOpen = function() -- Called when app opens (optional) print('App opened!') end, onClose = function() -- Called when app closes (optional) print('App closed!') end }) end) ``` ### 2. HTML/JS: Communicate with Phone **⚠️ IMPORTANT CSS RULES:** - **DO NOT** use `100vh`! Use `100%` instead - **DO NOT** use `px` or `rem` units! Use `%` and `em` ONLY - Add `html, body { width: 100%; height: 100%; overflow: hidden; }` - Your app will be displayed inside the phone screen, not fullscreen > **WARNING:** Using `px`, `rem`, or `vh` units will cause your app to display incorrectly on the phone screen. Always use `%` for dimensions and `em` for font sizes, padding, margins, etc. ```html
Phone Number: -
``` ### 3. Listen for Callback Events #### Client-side Event (server: false) ```lua -- client/main.lua -- Event format: codem-phone:customApp:{identifier}:{action} -- Note: Client events do NOT have source parameter! AddEventHandler('codem-phone:customApp:myapp:getLocalData', function(payload, cb) -- payload: data from app -- cb: response function local localData = { playerPed = PlayerPedId(), coords = GetEntityCoords(PlayerPedId()) } cb({ success = true, data = localData }) end) -- Example: Get vehicle list AddEventHandler('codem-phone:customApp:myapp:getVehicles', function(payload, cb) local vehicles = GetGamePool('CVehicle') cb({ success = true, count = #vehicles }) end) ``` #### Server-side Event (server: true) ```lua -- server/main.lua -- Event format: codem-phone:customApp:{identifier}:{action} -- Note: Server events HAVE source parameter! AddEventHandler('codem-phone:customApp:myapp:getPlayerStats', function(source, payload, cb) -- source: player server ID -- payload: data from app -- cb: response function local playerName = GetPlayerName(source) cb({ success = true, name = playerName, id = source }) end) -- Example: Save to database AddEventHandler('codem-phone:customApp:myapp:saveData', function(source, payload, cb) MySQL.insert('INSERT INTO app_data (player_id, data) VALUES (?, ?)', { source, json.encode(payload) }, function(insertId) cb({ success = true, id = insertId }) end ) end) -- Example: Read from database AddEventHandler('codem-phone:customApp:myapp:loadData', function(source, payload, cb) MySQL.query('SELECT * FROM app_data WHERE player_id = ?', { source }, function(results) cb({ success = true, data = results }) end ) end) ``` ### 4. Send Message to App ```lua -- client/main.lua -- Send message to app (when app is open) exports['codem-phone']:SendCustomAppMessage('myapp', { type = 'customEvent', data = { key = 'value' } }) ``` ## server Parameter Summary | `server` Value | Event Runs Where | source Parameter | Use Case | |----------------|------------------|------------------|----------| | `false` (default) | Client (client/main.lua) | NO | Local data, ped info, coordinates | | `true` | Server (server/main.lua) | YES | Database, player validation, money operations | ## API Reference ### Client Exports ```lua -- Add app exports['codem-phone']:AddCustomApp({ identifier = string, -- Unique ID (required) name = string, -- Display name (required) ui = string, -- HTML content (required) icon = string, -- Icon URL (optional, default: 'apps/default.png') description = string, -- Description (optional) defaultApp = boolean, -- Is default app? (optional, default: false) notification = boolean, -- Notification support (optional, default: true) onOpen = function, -- Called when opened (optional) onClose = function -- Called when closed (optional) }) -- Remove app exports['codem-phone']:RemoveCustomApp(identifier) -- Send message to app exports['codem-phone']:SendCustomAppMessage(identifier, message) -- Get app info local app = exports['codem-phone']:GetCustomApp(identifier) -- Check if phone is open local isOpen = exports['codem-phone']:IsPhoneOpen() ``` ### JavaScript Message Types (Sent to Parent) | Type | Description | Parameters | |------|-------------|------------| | `mphone:close` | Close the app | - | | `mphone:notification` | Show notification | `header`, `message` | | `mphone:callback` | Send callback | `action`, `payload`, `callbackId`, `server` | | `mphone:navigate` | Navigate to another app | `path` | | `mphone:setWaypoint` | Set waypoint on map | `x`, `y` | | `mphone:getPlayer` | Request player data | - | ### JavaScript Message Types (Sent to App) | Type | Description | Content | |------|-------------|---------| | `mphone:init` | When app opens | `identifier`, `player`, `theme`, `language` | | `mphone:callback:response` | Callback response | `callbackId`, `result` | | `mphone:playerData` | Player data | `player` | | Custom types | Messages from Lua | Variable | ## Example Resource Structure ``` my-phone-app/ ├── fxmanifest.lua ├── client/ │ └── main.lua ├── server/ │ └── main.lua (optional - for server callbacks) └── ui/ └── index.html ``` ### fxmanifest.lua ```lua fx_version 'cerulean' game 'gta5' author 'Your Name' description 'Custom phone app' version '1.0.0' client_scripts { 'client/main.lua' } -- Add if using server callbacks server_scripts { 'server/main.lua' } files { 'ui/**/*' } dependency 'codem-phone' ``` ## Full Example - Counter App ### client/main.lua ```lua local appRegistered = false CreateThread(function() while GetResourceState('codem-phone') ~= 'started' do Wait(100) end Wait(1000) local htmlContent = LoadResourceFile(GetCurrentResourceName(), 'ui/index.html') local success, err = exports['codem-phone']:AddCustomApp({ identifier = 'counter', name = 'Counter', icon = 'nui://' .. GetCurrentResourceName() .. '/ui/icon.png', ui = htmlContent, onOpen = function() print('[Counter] App opened') end, onClose = function() print('[Counter] App closed') end }) if success then appRegistered = true end end) ``` ### server/main.lua ```lua local PlayerCounters = {} -- Get counter AddEventHandler('codem-phone:customApp:counter:getCounter', function(source, payload, cb) cb({ success = true, count = PlayerCounters[source] or 0 }) end) -- Increment counter AddEventHandler('codem-phone:customApp:counter:increment', function(source, payload, cb) PlayerCounters[source] = (PlayerCounters[source] or 0) + 1 cb({ success = true, count = PlayerCounters[source] }) end) -- Decrement counter AddEventHandler('codem-phone:customApp:counter:decrement', function(source, payload, cb) PlayerCounters[source] = math.max(0, (PlayerCounters[source] or 0) - 1) cb({ success = true, count = PlayerCounters[source] }) end) ``` ### ui/index.html ```html