> For the complete documentation index, see [llms.txt](https://docs.gkshop.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.gkshop.org/gksphone-v2/custom-app.md).

# Custom App

## Adding the app <a href="#adding-the-app" id="adding-the-app"></a>

To add the app, use the [AddCustomApp](/gksphone-v2/exports-and-events/client-exports.md#add-custom-app) export.

{% hint style="info" %}
You can access the custom app template on [github](<https://github.com/Xenknight61/gksphone-app >) and start making the app you want.
{% endhint %}

## Open App

Add this client and this will be triggered every time you enter the application.

```lua
RegisterNUICallback('getInfo', function(data, cb)
    print("The function that will run when it enters the application")
    cb('ok')
end)
```

## Exports - Client

### InputChange

To prevent walking while filling in input fields

```lua
local data = true -- true or false
exports["gksphone"]:InputChange(data)
```

```html
<input type="text" onfocus="inputFocused(false)" onblur="inputFocused(true)">

<script>
   async function inputFocused(value) {
       //await NuiFetch("input", value);
   }
</script>
```

### NuiSendMessage

You have to use this export instead of SendNUIMessage

```lua
exports["gksphone"]:NuiSendMessage({event = 'showMessage', hello = "world"})
```

```javascript
window.addEventListener('message', (event) => {
    if (!event.data) return;
    const e = event.data
    if (e.event === "showMessage") {
        console.log(`Hello ${e.hello}!`)
    }
})
```

## Exports - Server - Client

### CustomApp

```lua
local appData = {
	name = "mdt", --- A unique name
	icons = "/html/img/icons/mdt.png",  -- logo url
	description = ""  -- App description that will appear in the app store
        appurl = "https://cfx-nui-gksphone-app/ui/index.html",  -- custom app url
	url = "/customapp",   -- do not touch this part
	blockedjobs = {},
	allowjob = {},
	signal = true,
	show = true,
	labelLangs = {   -- App name by languages
		af = "MDT",
		ar = "MDT",
		cs = "MDT",
		de = "MDT",
		en = "MDT",
		es = "MDT",
		fr = "MDT",
		id = "MDT",
		nl = "MDT",
		["pt-PT"] = "MDT",
		ro = "MDT",
		sv = "MDT",
		th = "MDT",
		tr = "MDT",
		uk = "MDT",
		["zh-TW"] = "MDT"
	}
}
exports["gksphone"]:AddCustomApp(appData)
```

## Javascript UI

```javascript
document.addEventListener('DOMContentLoaded', () => {
    setTimeout(() => {
        if (window.gksphone) {
            console.log(`GKSPHONE functions available`)
        }
    }, 100);
});
```

### Dark Mode Check

```javascript
const isDarkMode = window.gksphone.isDarkMode();
console.log(isDarkMode)  // true or false
```

### Loading Poup

```javascript
window.gksphone.loadingPopup("Loading...");
setTimeout(() => {
   window.gksphone.closeLoadingPopup();
}, 1000)
```

### Notify

```javascript
var timeout = 2000
window.gksphone.notify("This is a notification message", timeout);
```

### Call

```javascript
const phoneNumber = "55555"  // Number to call
const anonymous = false
window.gksphone.calling(phoneNumber, anonymous);
```

### Video Call

```javascript
const phoneNumber = "55555"  // Number to call
window.gksphone.videoCall(phoneNumber);
```

### Camera Open

<pre class="language-javascript"><code class="lang-javascript"><strong>const PhotoActive = true  // Photo option on or off
</strong>const VideoActive = false  // Video option on or off
const photourl = window.gksphone.CameraOpen(PhotoActive, VideoActive);
console.log(photourl) // Link to the photo or video taken
</code></pre>

### Get Gallery Poup

<pre class="language-javascript"><code class="lang-javascript"><strong>const onlyVideo = true // Show video in options?
</strong>const onlyPicture = true // Show photo in options?
const multiple = false // Select multiple images or videos
const camera = false // Camera shooting option on/off
const photourl = window.gksphone.GetGallery(onlyVideo, onlyPicture, multiple, camera);
console.log(photourl) // { data = link, opencamera = true/false } 
// When the camera icon is clicked, "opencamera" returns true
// The data section contains the selected image or video link. 
// If the camera option is clicked, it will return false.
</code></pre>

### Image or Video Viewer

<pre class="language-javascript"><code class="lang-javascript"><strong>const url = "photo link"
</strong>window.gksphone.FullScreenImage(url);
</code></pre>

### Emoji Poup Open

<pre class="language-javascript"><code class="lang-javascript"><strong>const url = "photo link"
</strong>window.gksphone.SelectEmoji(url);

window.addEventListener('message', (event) => {
    const e = event.data
    if (e.type === 'emojiSelected') {
        gksphoneFunctions?.notify(`Emoji selected: ${e.eventData}`, 2000)
    }
})
</code></pre>

### setStatusBarColor

<pre class="language-javascript"><code class="lang-javascript"><strong>const color = "#000000'
</strong>window.gksphone.setStatusBarColor(color);
</code></pre>

### **GameMap**

```js
const el = document.getElementById('map')
const map = gksphone.createMap(el, {
  zoom: 3,
  center: { x: 428.9, y: -984.5 },
  map: 'losSantos',      // | 'cayoPerico'
  style: 'atlas',        // | 'satellite' | 'grid'
  allowMoving: false     // true = drag/zoom
})
await map.ready

map.setZoom(4)
map.getZoom()
map.setPosition({ x: 100, y: 200 }, 5)   // or [y, x]
map.getMaps()                            // ['losSantos','cayoPerico']
map.setMap('cayoPerico')
map.cycleMap()
map.getStyles()                          // ['atlas','satellite','grid']
map.setStyle('satellite')
map.cycleStyle()
await map.setShowSelf(true)              // Live player PIN (~3-second poll)

const loc = map.addLocation({
  title: 'LSPD',
  image: 'https://…/icon.png',
  coords: { x: 428.9, y: -984.5 }
})
map.removeLocation(loc)

map.destroy()
```
