# Splash screen (شاشة البداية) — app contract

Rules and map: `App\Services\SplashScreens`. Flutter handoff: `docs/api/splash-screen-app-handoff.md`. Staff set it in Admin → Marketing → Splash screen (permissions `view_splash_screens` / `manage_splash_screens`).

A splash is a picture (jpg / png / webp), an animated GIF or a short mp4 video that the app shows while it opens. **Display only**: no tap, no link, no skip button.

There are two kinds:

* **Default**: shown every day. Only one is switched on at a time.
* **Scheduled**: has a start and an end (Eid, White Friday). It replaces the default between those two dates. Two scheduled splashes that are switched on never overlap.

## Why the app shows it from the next launch

The splash shows before the app has the network. So the app downloads every splash it is given, keeps the files, and at each launch picks one **from what is already on the phone**, by the phone's clock. A splash the app learns about now is first shown at the next launch. Staff are told to publish a scheduled splash at least a day before it starts.

## Endpoint

`GET api/v2/splash-screen` takes no token. It is the same answer for everyone and every language. It needs the usual `System-Key` header.

It is also the group `splash_screen` of `POST api/v2/app/data` (`docs/api/app-data-sync.md`): unchanged = `null`, so asking at every launch costs a few bytes.

```json
{
  "result": true,
  "data": {
    "current": { "id": 7, "kind": "scheduled", "...": "..." },
    "default": {
      "id": 3,
      "kind": "default",
      "media_type": "image",
      "media_url": "https://kapitano.shop/public/uploads/all/abc.png",
      "media_version": "4f1c0a9e2b7d",
      "file_size": 412345,
      "fit": "cover",
      "background_color": "#10164C",
      "duration_seconds": 3,
      "starts_at": null,
      "ends_at": null
    },
    "scheduled": [
      {
        "id": 7,
        "kind": "scheduled",
        "media_type": "video",
        "media_url": "https://kapitano.shop/public/uploads/all/eid.mp4",
        "media_version": "9a03be11c4d2",
        "file_size": 3145728,
        "fit": "contain",
        "background_color": "#FFFFFF",
        "duration_seconds": 5,
        "starts_at": "2027-05-16T00:00:00+03:00",
        "ends_at": "2027-05-20T00:00:00+03:00"
      }
    ]
  }
}
```

### Top-level fields

| Field | Meaning |
|---|---|
| `current` | The server's pick right now: the scheduled splash running now, else `default`, else `null`. Handy, but the app should pick at launch itself (below). |
| `default` | The default splash switched on, or `null` (no default: use the splash built into the app). |
| `scheduled` | Every scheduled splash switched on that has not ended and starts within the next **30 days**. Sorted by `starts_at`, at most 10. The one running now is included. |

### Fields of one splash

| Field | Meaning |
|---|---|
| `media_type` | `image`, `gif` or `video` (mp4). |
| `media_url` | The file to download. |
| `media_version` | Changes only when the file changes. Keep the downloaded file under `id` + `media_version`; download again only when it changes. |
| `file_size` | Bytes. Pictures and GIFs are at most 5 MB, videos at most 15 MB. |
| `fit` | `cover`: fill the screen, cutting the edges if needed (`BoxFit.cover`). `contain`: show the whole picture, with `background_color` around it (`BoxFit.contain`). |
| `background_color` | `#RRGGBB` or `null`. Paint it first, behind the picture, and while the file opens. `null` = the app's own splash background. |
| `duration_seconds` | 1 – 15. A picture or GIF stays this long. A video plays **once, muted**, and stops at its end or after this many seconds, whichever comes first. |
| `starts_at` / `ends_at` | ISO-8601 with offset. `null` on the default. Shown while `starts_at <= now < ends_at`. |

## What the app does

1. **At launch, with no network**, pick from the files on the phone:
   1. a scheduled splash whose window holds the phone's time now;
   2. else the default;
   3. else the splash built into the app.
2. Show it. Keep the app's own loading going underneath: never make the customer wait longer than `duration_seconds`.
3. **After launch**, call the endpoint (or the `splash_screen` group of `app/data`) and sync the files:
   * download every splash in the answer whose `id` + `media_version` is not on the phone yet;
   * delete the files of splashes that are no longer in the answer;
   * a download that failed or is incomplete is never shown: the app falls back as in step 1.
4. If the answer has `default: null` and nothing scheduled, delete every splash file: the app goes back to its own.

Prefer Wi-Fi for videos when possible. Never show a half-downloaded file.

## Notes

* A splash whose file staff deleted from the uploads is left out of the answer.
* A splash staff switched off or deleted disappears from the next answer, so it stops showing from the launch after the next sync.
* The answer is cached on the server for at most 10 minutes. Every change in the admin clears that cache at once.
