Skip to content

Commit 91d489c

Browse files
fadi-georgeclaude
andcommitted
docs: [SDK-5300] document push status, Opted In, and language picker in the demo spec
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> docs: [SDK-5300] right-align the language picker value in the demo spec Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> docs: reset demo language picker on login without setLanguage, cap menu at 320 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> docs: reset demo language picker on logout without setLanguage Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 6709fc9 commit 91d489c

1 file changed

Lines changed: 25 additions & 18 deletions

File tree

‎demo/build.md‎

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,15 @@ Reference the OneSignal SDK from the parent repo using a local path/file depende
4747

4848
The state container (see Prompt 1.1) calls the OneSignal SDK directly and exposes the operations below as actions/methods:
4949

50-
- **User**: loginUser(externalUserId) -> async, logoutUser() -> async
50+
- **User**: loginUser(externalUserId) -> async, logoutUser() -> async, setLanguage(code)
5151
- **Aliases**: addAlias(label, id), addAliases(map)
5252
- **Email**: addEmail(email), removeEmail(email)
5353
- **SMS**: addSms(number), removeSms(number)
5454
- **Tags**: addTag(key, value), addTags(map), removeTag(key) if SDK supports, removeTags(keys), getTags() -> async map if SDK supports
5555
- **Triggers** (via OneSignal.InAppMessages): addTrigger(key, value), addTriggers(map), removeTrigger(key) if SDK supports, removeTriggers(keys), clearTriggers()
5656
- **Outcomes** (via OneSignal.Session): sendOutcome(name), sendUniqueOutcome(name), sendOutcomeWithValue(name, value)
5757
- **Track Event**: trackEvent(name, properties?: map)
58-
- **Push subscription**: getPushSubscriptionId() -> nullable, isPushOptedIn() -> nullable bool, optInPush(), optOutPush()
58+
- **Push subscription**: getPushSubscriptionId() -> nullable, getPushSubscriptionToken() -> nullable, isPushOptedIn() -> nullable bool, optInPush(), optOutPush()
5959
- **Notifications**: hasPermission() -> bool, requestPermission(fallbackToSettings) -> async bool, clearAll()
6060
- **In-App Messages**: setPaused(bool)
6161
- **Location**: setLocationShared(bool), isLocationShared() -> async bool, requestLocationPermission()
@@ -152,8 +152,8 @@ Clean up listeners on teardown if the platform requires it.
152152

153153
0. **Log View** (sticky under the app bar, outside the scroll view; see Prompt 2.0)
154154
1. **App Section** (App ID, Guidance Banner, Consent Toggle)
155-
2. **User Section** (Status, External ID, Login/Logout)
156-
3. **Push Section** (Push ID, Enabled Toggle, Auto-prompts on load)
155+
2. **User Section** (Status, External ID, Language, Login/Logout)
156+
3. **Push Section** (Push ID, Status, Opted In Toggle, Auto-prompts on load)
157157
4. **Send Push Notification Section** (Simple, With Image, With Sound, Custom, Clear All)
158158
5. **In-App Messaging Section** (Pause toggle)
159159
6. **Send In-App Message Section** (Top Banner, Bottom Banner, Center Modal, Full Screen)
@@ -201,17 +201,22 @@ Styling in styles.md (Log View).
201201
Separate SectionCard titled "User":
202202

203203
1. Status card (always visible, ABOVE buttons):
204-
- Two rows separated by a divider: "Status" and "External ID"
204+
- Three rows separated by dividers: "Status", "External ID" and "Language"
205205
- Logged out: Status = "Anonymous", External ID = "—"
206206
- Logged in: Status = "Logged In" (success/green), External ID = actual value
207-
2. LOGIN USER button ("SWITCH USER" when logged in) -> dialog with empty "External User Id" field
208-
3. LOGOUT USER button (only when logged in, outlined style)
207+
- Language: picker showing the selected language name. Options are "Device Default" (code `""`) followed by the language list below, in this order. Selecting an option calls `OneSignal.User.setLanguage(code)`. `""` tells the native SDK to use the device language.
208+
- Use the platform's dropdown/picker where it is Appium-friendly; otherwise a single-select list dialog. Cap the open list's height at 320 so it doesn't cover the screen. Right-align the selected value so it sits next to the dropdown arrow, like the other value rows.
209+
2. LOGIN USER button ("SWITCH USER" when logged in) -> dialog with empty "External User Id" field. Login resets the Language picker to Device Default locally (clear the cached value only). Do NOT call `setLanguage` on login: `""` resolves to the device language and would overwrite the new user's language on the backend.
210+
3. LOGOUT USER button (only when logged in, outlined style). Logout also resets the Language picker to Device Default locally, without calling `setLanguage`. The SDK starts the new anonymous user with no language override, and `logout()` isn't awaited, so a `setLanguage('')` could land on the logged-out user.
211+
212+
Language list, after Device Default, in this order: English (`en`), Arabic (`ar`), Azerbaijani (`az`), Bosnian (`bs`), Catalan (`ca`), Chinese (Simplified) (`zh-Hans`), Chinese (Traditional) (`zh-Hant`), Croatian (`hr`), Czech (`cs`), Danish (`da`), Dutch (`nl`), Estonian (`et`), Finnish (`fi`), French (`fr`), Georgian (`ka`), Bulgarian (`bg`), German (`de`), Greek (`el`), Hindi (`hi`), Hebrew (`he`), Hungarian (`hu`), Indonesian (`id`), Italian (`it`), Japanese (`ja`), Korean (`ko`), Latvian (`lv`), Lithuanian (`lt`), Malay (`ms`), Norwegian (`nb`), Persian (`fa`), Polish (`pl`), Portuguese (`pt`), Punjabi (`pa`), Romanian (`ro`), Russian (`ru`), Serbian (`sr`), Slovak (`sk`), Spanish (`es`), Swedish (`sv`), Thai (`th`), Turkish (`tr`), Ukrainian (`uk`), Vietnamese (`vi`).
209213

210214
### Prompt 2.2 - Push Section
211215

212216
- Title: "Push" with info icon
213217
- Push Subscription ID (readonly).
214-
- Enabled toggle (optIn/optOut), disabled when permission NOT granted
218+
- Status (readonly), directly above the toggle: "Subscribed" when opted in AND notification permission is granted AND the push token is non-empty; otherwise "Unsubscribed". Recompute on push subscription and permission changes.
219+
- "Opted In" toggle (optIn/optOut), disabled when permission NOT granted
215220
- Auto-request permission when home screen loads
216221
- PROMPT PUSH button: only visible when permission NOT granted, hidden once granted
217222

@@ -372,8 +377,8 @@ This keeps the UI stable: rows don't flicker/re-order when the fetch completes,
372377
State transitions:
373378

374379
- **Cold start**: if `onesignalId` exists, await `fetchUserDataFromApi()` (which manages its own `isLoading`). If `onesignalId` is null, leave `isLoading=false`; sections show their normal empty state. The `OneSignal.login(storedExternalUserId)` call inside cold start may also trigger the user-state observer, which fires its own fetch; the request-sequence guard collapses the race.
375-
- **Login**: clear aliases/tags/emails/sms/triggers lists immediately, set the optimistic external user id, and set `isLoading=true` so the four sections show the spinner without delay. Then call `OneSignal.login()` and persist the external user id. Do NOT call `fetchUserDataFromApi` from `loginUser` — the user-state observer drives it once the SDK assigns a new `onesignalId`, and the fetch's finally block clears `isLoading`. If `OneSignal.login()` itself throws synchronously, clear `isLoading` in the catch so the UI doesn't get stuck.
376-
- **Logout**: clear aliases/tags/emails/sms/triggers lists synchronously -> `OneSignal.logout()` -> clear persisted external user id. Do NOT toggle `isLoading`; logout is local-only. The user-state observer will fire with `onesignalId=null` and is a no-op in that case.
380+
- **Login**: clear aliases/tags/emails/sms/triggers lists immediately, set the optimistic external user id, and set `isLoading=true` so the four sections show the spinner without delay. Then call `OneSignal.login()`, persist the external user id, and clear the cached language (no `setLanguage` call). Do NOT call `fetchUserDataFromApi` from `loginUser` — the user-state observer drives it once the SDK assigns a new `onesignalId`, and the fetch's finally block clears `isLoading`. If `OneSignal.login()` itself throws synchronously, clear `isLoading` in the catch so the UI doesn't get stuck.
381+
- **Logout**: clear aliases/tags/emails/sms/triggers lists synchronously -> `OneSignal.logout()` -> clear persisted external user id -> clear the cached language (no `setLanguage` call). Do NOT toggle `isLoading`; logout is local-only. The user-state observer will fire with `onesignalId=null` and is a no-op in that case.
377382
- **onUserStateChange**: log the new `onesignalId`/`externalId`. When `onesignalId` is non-null, call `fetchUserDataFromApi()` — this is what drives the post-login refresh (the SDK only assigns the new `onesignalId` after `OneSignal.login()` queues its identify operation, so a synchronous fetch right after `login()` would race the assignment and return null). When `onesignalId` is null (logout), do not fetch — `logoutUser` has already cleared local lists.
378383

379384
Caveat: if `OneSignal.login(sameExternalId)` does not emit a change event (e.g., logging in with the already-active external id), `isLoading` will stay true. Acceptable for the demo; per-action handlers still work via optimistic updates.
@@ -419,7 +424,7 @@ SectionCard has optional info icon -> onInfoTap callback -> shows TooltipDialog
419424

420425
### Persisted (local storage)
421426

422-
PreferencesService: consent required, privacy consent, external user ID, location shared, IAM paused. App ID is read from `.env` (not persisted in preferences).
427+
PreferencesService: consent required, privacy consent, external user ID, location shared, IAM paused, language. The language is cached only so the picker shows the selection after a restart; don't re-send it on launch, since the native SDK persists its own value and there is no SDK getter. App ID is read from `.env` (not persisted in preferences).
423428

424429
### Initialization Flow
425430

@@ -489,12 +494,14 @@ Section keys: `app`, `user`, `push`, `send_push`, `iam`, `send_iam`, `aliases`,
489494

490495
**Value displays**:
491496

492-
| Identifier | Element |
493-
| ------------------------ | --------------------------------- |
494-
| `app_id_value` | App ID text |
495-
| `push_id_value` | Push Subscription ID text |
496-
| `user_status_value` | User status (Anonymous/Logged In) |
497-
| `user_external_id_value` | External ID text |
497+
| Identifier | Element |
498+
| -------------------------- | ------------------------------------- |
499+
| `app_id_value` | App ID text |
500+
| `push_id_value` | Push Subscription ID text |
501+
| `push_subscription_status` | Push Status (Subscribed/Unsubscribed) |
502+
| `user_language_dropdown` | Language picker (User section) |
503+
| `user_status_value` | User status (Anonymous/Logged In) |
504+
| `user_external_id_value` | External ID text |
498505

499506
**Buttons**:
500507

@@ -526,7 +533,7 @@ Section keys: `app`, `user`, `push`, `send_push`, `iam`, `send_iam`, `aliases`,
526533
| ------------------------- | ------------------------------------------------------- |
527534
| `consent_required_toggle` | Consent Required (App section) |
528535
| `privacy_consent_toggle` | Privacy Consent (only visible when consent required on) |
529-
| `push_enabled_toggle` | Push Enabled (Push section) |
536+
| `push_enabled_toggle` | Opted In (Push section) |
530537
| `pause_iam_toggle` | Pause In-App Messages |
531538
| `location_shared_toggle` | Location Shared |
532539

0 commit comments

Comments
 (0)