# State bags

## Player

### phoneOpen

#### Returns boolean - whether the phone is open for the player

This state bag tracks whether the phone UI is currently open for a player.

**Client side:**

```lua
-- Read state bag
local isOpen = LocalPlayer.state.phoneOpen
```

**Server side:**

```lua
-- Read state bag
local isOpen = Player(source).state.phoneOpen
```

**State bag change handler (client side):**

```lua
AddStateBagChangeHandler('phoneOpen', ('player:%s'):format(cache.serverId), function(_, _, value)
    -- State bag handler automatically syncs PhoneOpen property

    print('[StateBag] phoneOpen changed:', value)
end)
```

***

### softOpen

#### Returns boolean - whether the phone is soft open for the player

This state bag tracks whether the phone is in "soft open" mode (visible but only for notifications/calls, not fully opened).

**Client side:**

```lua
-- Read state bag
local isSoftOpen = LocalPlayer.state.softOpen
```

**Server side:**

```lua
-- Read state bag
local isSoftOpen = Player(source).state.softOpen
```

**State bag change handler (client side):**

```lua
AddStateBagChangeHandler('softOpen', ('player:%s'):format(cache.serverId), function(_, _, value)
    -- State bag handler automatically syncs SoftOpen property

    print('[StateBag] softOpen changed:', value)
end)
```

***

### phoneDisabled

#### Returns boolean - whether the phone is disabled for the player

This state bag is used to globally disable phone functionality for a player. When set to `true`, the phone will:

* Disconnect any active calls (both regular and video calls)
* Close the phone UI if it's open
* Prevent phone usage until disabled is set to `false`

**Client side:**

```lua
-- Read state bag
local isDisabled = LocalPlayer.state.phoneDisabled

-- Set state bag (triggers server event internally)
exports.yseries:ToggleDisabled(true)  -- Disable phone
exports.yseries:ToggleDisabled(false) -- Enable phone
```

**Server side:**

```lua
-- Read state bag
local isDisabled = Player(source).state.phoneDisabled

-- Or use the export
exports.yseries:SetPhoneDisabled(source, true)  -- Disable phone
exports.yseries:SetPhoneDisabled(source, false) -- Enable phone
```

**State bag change handler (client side):**

```lua
AddStateBagChangeHandler('phoneDisabled', ('player:%s'):format(cache.serverId), function(_, _, value)
    -- Handle phone disabled state change
    -- Calls are automatically disconnected and UI is closed

    print('[StateBag] phoneDisabled changed:', value)
end)
```

***

### airplaneMode

#### Returns boolean - whether airplane mode is enabled for the player

This state bag tracks the airplane mode setting for a player. When set to `true`, the phone will:

* Disconnect any active calls (both regular and video calls)
* Prevent making or receiving calls
* Update signal availability in the UI

**Client side:**

```lua
-- Read state bag
local isAirplaneMode = LocalPlayer.state.airplaneMode

-- State bag is automatically set when airplane mode is toggled via UI
-- Or can be set manually (triggers server event internally)
TriggerServerEvent('yseries:server:settings:update-airplane-mode', true, phoneImei)
```

**Server side:**

```lua
-- Read state bag
local isAirplaneMode = Player(source).state.airplaneMode
```

**State bag change handler (client side):**

```lua
AddStateBagChangeHandler('airplaneMode', ('player:%s'):format(cache.serverId), function(_, _, value)
    -- Handle airplane mode state change
    -- Calls are automatically disconnected and camera is closed if active

    print('[StateBag] airplaneMode changed:', value)
end)
```

***

### inCall

#### Returns boolean - whether the player is currently in a call

This state bag tracks whether a player is currently in an active call.

**Client side:**

```lua
-- Read state bag
local inCall = LocalPlayer.state.inCall
```

**Server side:**

```lua
-- Read state bag
local inCall = Player(source).state.inCall
```

**State bag change handler (client side):**

```lua
AddStateBagChangeHandler('inCall', ('player:%s'):format(cache.serverId), function(_, _, value)
    -- Normalize nil values to false
    if value == nil then
        value = false
    end

    print('[StateBag] inCall changed:', value)

    -- State bag is read-only on client side - just for tracking
    -- LocalPlayer.state.inCall can be read directly by other code
end)
```

***

### callId

#### Returns number | nil - the current call ID if player is in a call, nil otherwise

This state bag tracks the current call ID for a player. The call ID is a unique identifier for each call session.

**Client side:**

```lua
-- Read state bag
local callId = LocalPlayer.state.callId
```

**Server side:**

```lua
-- Read state bag
local callId = Player(source).state.callId
```

**State bag change handler (client side):**

```lua
AddStateBagChangeHandler('callId', ('player:%s'):format(cache.serverId), function(_, _, value)
    print('[StateBag] callId changed:', value)

    -- State bag is read-only on client side - just for tracking
    -- LocalPlayer.state.callId can be read directly by other code
end)
```

***

### callStatus

#### Returns table | nil - object containing full call details, nil if not in call

This state bag contains detailed information about the player's current call status.

**Structure:**

```lua
{
    callType = "incoming" | "outgoing" | "ongoing",
    answered = boolean,
    callerNumber = string,      -- Phone number of the caller
    targetNumber = string,      -- Phone number of the target/recipient
    callId = number,
    callStartTime = number | nil, -- Timestamp when call was answered/started
    anonymousCall = boolean | nil -- Whether the call is anonymous
}
```

**Client side:**

```lua
-- Read state bag
local callStatus = LocalPlayer.state.callStatus

-- Example usage
if callStatus then
    print("Call Type:", callStatus.callType)
    print("Answered:", callStatus.answered)
    print("Caller:", callStatus.callerNumber)
    print("Target:", callStatus.targetNumber)
end
```

**Server side:**

```lua
-- Read state bag
local callStatus = Player(source).state.callStatus
```

**State bag change handler (client side):**

```lua
AddStateBagChangeHandler('callStatus', ('player:%s'):format(cache.serverId), function(_, _, value)
    print('[StateBag] callStatus changed:', json.encode(value, { indent = true }))

    -- State bag is read-only on client side - just for tracking
    -- LocalPlayer.state.callStatus can be read directly by other code
end)
```

***

### batteryLevel

#### Returns number - the current battery level (0-100) for the player's phone

This state bag tracks the phone's battery level. When battery reaches 0% (drained), the phone will:

* Disconnect any active calls (both regular and video calls)
* Block incoming calls (caller will receive an error)
* Block notifications from being sent/received
* Disable signal (phone appears offline)

**Note:** This state bag is only available when `Config.Battery.Enabled` is set to `true`. When battery is disabled, this state bag will not be set or updated.

**Client side:**

```lua
-- Read state bag
local batteryLevel = LocalPlayer.state.batteryLevel

-- Example usage
if batteryLevel then
    if batteryLevel <= 0 then
        print("Battery is drained - phone is dead")
    elseif batteryLevel <= 20 then
        print("Battery is low")
    end
end
```

**Server side:**

```lua
-- Read state bag
local batteryLevel = Player(source).state.batteryLevel

-- Example usage - check if player can receive calls
if Config.Battery.Enabled then
    local batteryLevel = Player(source).state.batteryLevel
    if (batteryLevel or 100) <= 0 then
        print("Player cannot receive calls - battery is drained")
    end
end
```

**State bag change handler (client side):**

```lua
AddStateBagChangeHandler('batteryLevel', ('player:%s'):format(cache.serverId), function(_, _, value)
    -- Normalize nil values to 100
    if value == nil then
        value = 100
    end

    -- Handle battery level change
    -- Calls are automatically disconnected when battery drains to 0%

    print('[StateBag] batteryLevel changed:', value)
end)
```

***

## Notes

* **State bags set on the server automatically replicate to all clients**
* **State bags set on the client do NOT replicate to the server** - use server events/exports to set them
* All state bags are initialized when phone data is loaded
* State bag change handlers automatically handle cleanup (disconnect calls, close UI, etc.)
* These state bags can be used by external resources (e.g., handcuffing scripts) to disable phone functionality globally
* **Call status state bags (inCall, callId, callStatus) are set automatically by the server when calls are initiated, answered, or ended**
* **Call status state bags are read-only on the client side** - they are purely for tracking and modifying them won't change the call status at all
