Skip to content

Tablet Architecture

Data Flow

UI → Server (Save)

React Component
    ↓ calls saveSettingsToDB() / saveAppDataToDB()
lib/dbStorage.js
    ↓ fetch('https://wtf_group/tablet:saveSettings', { settings })
client/tablet.lua
    ↓ RegisterNUICallback → TriggerServerEvent
server/tablet.lua
    ↓ event handler → DB.SaveSettings() / DB.SetAppData()
server/database.lua
    ↓ MySQL.insert.await(...)
MySQL Database

Server → UI (Load)

React Component (on mount)
    ↓ calls loadSettingsFromDBAsync() / loadAppDataFromDB()
lib/dbStorage.js
    ↓ fetch('https://wtf_group/tablet:loadSettings')
client/tablet.lua
    ↓ RegisterNUICallback → TriggerServerEvent
server/tablet.lua
    ↓ DB.LoadSettings() → TriggerClientEvent('wtf_group:client:tablet:settingsLoaded')
client/tablet.lua
    ↓ RegisterNetEvent → SendNUIMessage({ action: 'tablet:settingsLoaded', settings })
React window.addEventListener('message')
    ↓ dbStorage.js waitForEvent() resolves
React Component
    ↓ setState with DB data

Context Provider Hierarchy

jsx
<GroupProvider>           // GroupContext — legacy group state
  <TabletProvider>        // TabletContext — settings, theme, open/close
    <AppProvider>         // AppContext — app registry, window management
      <AppContent />      // Root component, NUI message handler
    </AppProvider>
  </TabletProvider>
</GroupProvider>

TabletContext (Settings)

State: All settings (theme, colors, font, scale, etc.) + isOpen flag

Provides:

  • isOpen, openTablet(), closeTablet(), toggleTablet()
  • All setting values (themeColors, accentColor, fontFamily, etc.)
  • updateSetting(key, value) — single setting change
  • updateSettings({...}) — batch update
  • resetSettings() — reset to defaults
  • themeColors — derived from base theme + user custom colors

Persistence:

  • Load: loadSettings() sync from localStorage, loadSettingsFromDBAsync() async from DB
  • Save: saveSettings() writes to both localStorage and DB

AppContext (Windows)

State: registeredApps, windows[], focusedWindowId, zIndexCounter

Provides:

  • registerApp(app) / unregisterApp(appId) — app registration
  • openApp(appId, options) — creates window
  • closeWindow(id), focusWindow(id), minimizeWindow(id), maximizeWindow(id)
  • moveWindow(id, x, y), resizeWindow(id, w, h)
  • updateWindowProps(id, props) — update window component props
  • minimizeAll() — minimize all windows

GroupContext (Legacy)

State: Group data, members, tasks, cooldowns, invites, notifications

Provides: Group CRUD, member management, task operations, real-time sync

NUI Message Protocol

Lua → React (via SendNUIMessage)

ActionDataPurpose
tablet:openOpen tablet shell
tablet:closeClose tablet shell
tablet:settingsLoadedsettingsSettings loaded from DB
tablet:installedAppsLoadedappsInstalled apps loaded from DB
tablet:appDataLoadedappId, key, valueApp data loaded from DB
tablet:allAppDataLoadedappId, dataAll app data loaded
tablet:notificationappId, title, message, typeShow notification
openUIdata, data2Legacy group open
closeUILegacy group close
groupUpdateddataGroup state sync
showNotificationtitle, description, typeShow in-UI toast

React → Lua (via fetch NUI callback)

CallbackDataPurpose
tablet:loadSettingsRequest settings from DB
tablet:saveSettingssettingsSave settings to DB
tablet:loadInstalledAppsRequest installed apps from DB
tablet:saveInstalledAppsappsSave installed apps to DB
tablet:setAppDataappId, key, valueSave app data to DB
tablet:getAppDataappId, keyRequest app data from DB
tablet:getAllAppDataappIdRequest all data for an app
tablet:deleteAppDataappId, keyDelete app data from DB
tablet:openAppappIdNotify server app opened
tablet:closeAppwindowIdNotify server app closed
tablet:sendMessageappId, messageType, dataSend message to server app
closeUIClose tablet from UI

Window Lifecycle

  1. Open: openApp(appId) → AppContext creates window with unique ID, position, size
  2. Focus: Click window or taskbar item → sets isFocused: true, increments z-index
  3. Minimize: Click minimize button → sets isMinimized: true, removes focus
  4. Maximize: Click maximize button → toggles isMaximized, restores if minimized
  5. Drag: Mouse down on titlebar → updates x/y on mousemove
  6. Resize: Mouse down on edge/corner handle → updates width/height on mousemove
  7. Close: Click close button → removes window from array

Persistence Strategy

  • localStorage: Instant UI load, acts as cache
  • MySQL: Durable storage, loaded async on mount
  • Dual Write: Every save writes to both localStorage and DB
  • DB Override: On mount, if DB has data, it overrides localStorage

This ensures:

  • UI loads instantly from localStorage (no flash)
  • Data survives server restarts and player reconnects
  • If DB is unavailable, localStorage provides fallback

AIFAZI — FiveM Resources