Skip to content

Commit b6db976

Browse files
committed
chore(demo): adjust log view styes/logic
1 parent 55a3662 commit b6db976

File tree

3 files changed

+161
-155
lines changed

3 files changed

+161
-155
lines changed

examples/build.md

Lines changed: 80 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -13,71 +13,75 @@ reference OneSignal demo app installed. These screenshots are the source
1313
of truth for the UI you are building. Do NOT proceed to Phase 1 without them.
1414

1515
Check for connected emulators:
16-
adb devices
16+
adb devices
1717

1818
If no device is listed, stop and ask the user to start one.
1919

2020
Identify which emulator has com.onesignal.sdktest installed by checking each listed device, e.g.:
21-
adb -s emulator-5554 shell pm list packages 2>/dev/null | grep -i onesignal
22-
adb -s emulator-5556 shell pm list packages 2>/dev/null | grep -i onesignal
21+
adb -s emulator-5554 shell pm list packages 2>/dev/null | grep -i onesignal
22+
adb -s emulator-5556 shell pm list packages 2>/dev/null | grep -i onesignal
2323

2424
Use that emulator's serial (e.g. emulator-5556) for all subsequent adb commands via the -s flag.
2525

2626
Launch the reference app:
27-
adb -s <emulator-serial> shell am start -n com.onesignal.sdktest/.ui.main.MainActivity
27+
adb -s <emulator-serial> shell am start -n com.onesignal.sdktest/.ui.main.MainActivity
2828

2929
Dismiss any in-app messages that appear on launch. Tap the X or
3030
click-through button on each IAM until the main UI is fully visible
3131
with no overlays.
3232

3333
Create an output directory:
34-
mkdir -p /tmp/onesignal_reference
34+
mkdir -p /tmp/onesignal_reference
3535

3636
Capture screenshots by scrolling through the full UI:
37+
3738
1. Take a screenshot from the top of the screen:
38-
adb shell screencap -p /sdcard/ref_01.png && adb pull /sdcard/ref_01.png /tmp/onesignal_reference/ref_01.png
39+
adb shell screencap -p /sdcard/ref_01.png && adb pull /sdcard/ref_01.png /tmp/onesignal_reference/ref_01.png
3940
2. Scroll down by roughly one viewport height:
40-
adb shell input swipe 500 1500 500 500
41+
adb shell input swipe 500 1500 500 500
4142
3. Take the next screenshot (ref_02.png, ref_03.png, etc.)
4243
4. Repeat until you've reached the bottom of the scrollable content
4344

4445
You MUST read each captured screenshot image so you can see the actual UI.
4546
These images define the visual target for every section you build later.
4647
Pay close attention to:
47-
- Section header style and casing
48-
- Card vs non-card content grouping
49-
- Button placement (inside vs outside cards)
50-
- List item layout (stacked vs inline key-value)
51-
- Icon choices (delete, close, info, etc.)
52-
- Typography, spacing, and colors
48+
49+
- Section header style and casing
50+
- Card vs non-card content grouping
51+
- Button placement (inside vs outside cards)
52+
- List item layout (stacked vs inline key-value)
53+
- Icon choices (delete, close, info, etc.)
54+
- Typography, spacing, and colors
5355

5456
You can also interact with the reference app to observe specific flows:
5557

5658
Dump the UI hierarchy to find elements by resource-id, text, or content-desc:
57-
adb shell uiautomator dump /sdcard/ui.xml && adb pull /sdcard/ui.xml /tmp/onesignal_reference/ui.xml
59+
adb shell uiautomator dump /sdcard/ui.xml && adb pull /sdcard/ui.xml /tmp/onesignal_reference/ui.xml
5860

5961
Parse the XML to find an element's bounds, then tap it:
60-
adb shell input tap <centerX> <centerY>
62+
adb shell input tap <centerX> <centerY>
6163

6264
Type into a focused text field:
63-
adb shell input text "test"
65+
adb shell input text "test"
6466

6567
Example flow to observe "Add Tag" behavior:
66-
1. Dump UI -> find the ADD button bounds -> tap it
67-
2. Dump UI -> find the Key and Value fields -> tap and type into them
68-
3. Tap the confirm button -> screenshot the result
69-
4. Compare the tag list state before and after
68+
69+
1. Dump UI -> find the ADD button bounds -> tap it
70+
2. Dump UI -> find the Key and Value fields -> tap and type into them
71+
3. Tap the confirm button -> screenshot the result
72+
4. Compare the tag list state before and after
7073

7174
Also capture screenshots of key dialogs to match their layout:
72-
- Add Alias (single pair input)
73-
- Add Multiple Aliases/Tags (dynamic rows with add/remove)
74-
- Remove Selected Tags (checkbox multi-select)
75-
- Login User
76-
- Send Outcome (radio options)
77-
- Track Event (with JSON properties field)
78-
- Custom Notification (title + body)
79-
These dialog screenshots are important for matching field layout,
80-
button placement, spacing, and validation behavior.
75+
76+
- Add Alias (single pair input)
77+
- Add Multiple Aliases/Tags (dynamic rows with add/remove)
78+
- Remove Selected Tags (checkbox multi-select)
79+
- Login User
80+
- Send Outcome (radio options)
81+
- Track Event (with JSON properties field)
82+
- Custom Notification (title + body)
83+
These dialog screenshots are important for matching field layout,
84+
button placement, spacing, and validation behavior.
8185

8286
Refer back to these screenshots throughout all remaining phases whenever
8387
you need to decide on layout, spacing, section order, dialog flows, or
@@ -696,22 +700,23 @@ interface TooltipOption {
696700
### Prompt 4.3 - Tooltip UI Integration
697701

698702
For each section, pass an onInfoTap callback to SectionCard:
703+
699704
- SectionCard has an optional info icon that calls onInfoTap when tapped
700705
- In HomeScreen, wire onInfoTap to show a TooltipModal
701706
- TooltipModal displays title, description, and options (if present)
702707

703708
Example in HomeScreen:
704709
<AliasesSection
705-
...
706-
onInfoTap={() => showTooltipModal('aliases')}
710+
...
711+
onInfoTap={() => showTooltipModal('aliases')}
707712
/>
708713

709714
function showTooltipModal(key: string) {
710-
const tooltip = TooltipHelper.getInstance().getTooltip(key);
711-
if (tooltip) {
712-
setActiveTooltip(tooltip);
713-
setTooltipVisible(true);
714-
}
715+
const tooltip = TooltipHelper.getInstance().getTooltip(key);
716+
if (tooltip) {
717+
setActiveTooltip(tooltip);
718+
setTooltipVisible(true);
719+
}
715720
}
716721

717722
---
@@ -721,6 +726,7 @@ function showTooltipModal(key: string) {
721726
### What IS Persisted (AsyncStorage)
722727

723728
PreferencesService stores:
729+
724730
- OneSignal App ID
725731
- Consent required status
726732
- Privacy consent status
@@ -736,10 +742,10 @@ On app startup, state is restored in two layers:
736742
- OneSignal.setConsentRequired(cachedConsentRequired)
737743
- OneSignal.setConsentGiven(cachedPrivacyConsent)
738744
- OneSignal.initialize(appId)
739-
Then AFTER initialize, restores remaining SDK state:
745+
Then AFTER initialize, restores remaining SDK state:
740746
- OneSignal.InAppMessages.setPaused(cachedPausedStatus)
741747
- OneSignal.Location.setShared(cachedLocationShared)
742-
This ensures consent settings are in place before the SDK initializes.
748+
This ensures consent settings are in place before the SDK initializes.
743749

744750
2. AppContextProvider initialization restores UI state:
745751
- consentRequired from cached prefs (no SDK getter)
@@ -750,13 +756,15 @@ On app startup, state is restored in two layers:
750756
- appId from PreferencesService (app-level config)
751757

752758
This two-layer approach ensures:
759+
753760
- The SDK is configured with the user's last preferences before anything else runs
754761
- AppContextProvider exposes one state object and action API for screens
755762
- Reducer transitions keep state updates predictable
756763

757764
### What is NOT Persisted (In-Memory Only)
758765

759766
App state holds in memory:
767+
760768
- triggersList: [string, string][]
761769
- Triggers are session-only
762770
- Cleared on app restart
@@ -814,6 +822,7 @@ Aliases are managed with a hybrid approach:
814822
### Notification Permission
815823

816824
Notification permission is automatically requested when the home screen loads:
825+
817826
- Call appContext.promptPush() in a useEffect with an empty dependency array in HomeScreen
818827
- This ensures prompt appears after user sees the app UI
819828
- PROMPT PUSH button remains as fallback if user initially denied
@@ -829,11 +838,13 @@ Notification permission is automatically requested when the home screen loads:
829838
Use React Context for dependency injection and useReducer for state management.
830839

831840
App.tsx:
841+
832842
- AppContext.Provider at the root of the component tree
833843
- Initialize OneSignal SDK before rendering (outside component or in early useEffect)
834844
- Fetch tooltips in the background (non-blocking)
835845

836846
AppContextProvider:
847+
837848
- Holds all UI state with useReducer
838849
- Exposes state and action functions through useAppContext
839850
- Uses OneSignalRepository and PreferencesService internally
@@ -844,44 +855,39 @@ AppContextProvider:
844855
Create reusable components in src/components/:
845856

846857
SectionCard.tsx:
858+
847859
- Card View with title Text and optional info TouchableOpacity
848860
- Children slot
849861
- onInfoTap callback for tooltips
850862
- Uses theme styles
851863

852864
ToggleRow.tsx:
865+
853866
- Label, optional description, Switch
854867
- Row layout with justifyContent: 'space-between'
855868

856869
ActionButton.tsx:
870+
857871
- PrimaryButton (filled, TouchableOpacity)
858872
- DestructiveButton (outlined, TouchableOpacity)
859873
- Full-width buttons with width: '100%'
860874

861875
ListWidgets.tsx:
876+
862877
- PairItem (key-value with optional X-icon remove TouchableOpacity)
863878
- SingleItem (single value with optional X-icon remove TouchableOpacity)
864879
- EmptyState (centered "No items" Text)
865880
- CollapsibleList (shows 5 items, expandable)
866881
- PairList (simple list of key-value pairs)
867882

868883
LoadingOverlay.tsx:
884+
869885
- Semi-transparent full-screen overlay using absolute positioned View + StyleSheet
870886
- Centered ActivityIndicator
871887
- Shown via isLoading state from app context
872888

873-
LogView.tsx:
874-
- Sticky at the top of the screen (always visible while ScrollView content scrolls below)
875-
- Full width, no horizontal margin, no rounded corners, no top margin (touches header)
876-
- Single horizontal ScrollView on the entire log list (not per-row), no text truncation
877-
- Use onLayout + minWidth so content is at least screen-wide
878-
- Vertical ScrollView + mapped entries instead of FlatList (100dp container is small)
879-
- Fixed 100dp height
880-
- Default expanded
881-
- Trash icon button (delete icon from react-native-vector-icons) for clearing logs
882-
- Auto-scroll to newest using scrollToEnd on ScrollView ref
883-
884889
Modals (src/components/modals/):
890+
885891
- All modals use a full-width Modal component with padding: 16 and width: '100%' inner container
886892
- SingleInputModal (one TextInput)
887893
- PairInputModal (key-value TextInputs on the same row, single pair)
@@ -896,6 +902,7 @@ Tags, Aliases, and Triggers all share a reusable MultiPairInputModal component
896902
for adding multiple key-value pairs at once.
897903

898904
Behavior:
905+
899906
- Modal opens full-width (width: '100%' with horizontal padding 16)
900907
- Starts with one empty key-value row (Key and Value TextInputs side by side)
901908
- "Add Row" TextButton below the rows adds another empty row
@@ -908,6 +915,7 @@ Behavior:
908915
- State is managed with useState inside the modal component
909916

910917
Used by:
918+
911919
- ADD MULTIPLE button (Aliases section) -> calls viewModel.addAliases(pairs)
912920
- ADD MULTIPLE button (Tags section) -> calls viewModel.addTags(pairs)
913921
- ADD MULTIPLE button (Triggers section) -> calls viewModel.addTriggers(pairs)
@@ -918,6 +926,7 @@ Tags and Triggers share a reusable MultiSelectRemoveModal component
918926
for selectively removing items from the current list.
919927

920928
Behavior:
929+
921930
- Accepts the current list of items as [string, string][]
922931
- Renders one checkbox per item on the left with just the key as the label (not "key: value")
923932
- Use a custom checkbox row with TouchableOpacity + icon since RN has no built-in Checkbox on both platforms
@@ -926,6 +935,7 @@ Behavior:
926935
- On confirm, checked items' keys are collected as string[] and passed to the callback
927936

928937
Used by:
938+
929939
- REMOVE SELECTED button (Tags section) -> calls viewModel.removeSelectedTags(keys)
930940
- REMOVE SELECTED button (Triggers section) -> calls viewModel.removeSelectedTriggers(keys)
931941

@@ -935,7 +945,7 @@ Create OneSignal theme in src/theme.ts.
935945

936946
All colors, spacing, typography, button styles, card styles, and component
937947
specs are defined in the shared style reference:
938-
https://raw.githubusercontent.com/OneSignal/sdk-shared/refs/heads/main/demo/styles.md
948+
https://raw.githubusercontent.com/OneSignal/sdk-shared/refs/heads/main/demo/styles.md
939949

940950
Export AppColors and AppSpacing objects that expose the tokens from styles.md
941951
as typed constants. Export an AppTheme object with reusable StyleSheet base
@@ -947,38 +957,37 @@ values for use throughout the app.
947957
Add collapsible log view at top of screen for debugging and Appium testing.
948958

949959
Files:
960+
950961
- src/services/LogManager.ts - Singleton logger with listener callbacks
951962
- src/components/LogView.tsx - Log viewer component with testID labels
952963

953964
LogManager Features:
965+
954966
- Singleton with subscriber callbacks for reactive UI updates
955967
- API: LogManager.getInstance().d(tag, message), .i(), .w(), .e() mimics debugPrint levels
956968
- Also prints to console via console.log/warn/error for development
969+
- Notifies listeners with new entry only (null on clear)
957970

958971
LogView Features:
959-
- STICKY at the top of the screen (always visible while ScrollView content scrolls below)
960-
- Full width, no horizontal margin, no rounded corners, no top margin (touches header)
961-
- Single horizontal ScrollView on the entire log list (not per-row), no text truncation
962-
- Use onLayout + minWidth so content is at least screen-wide
963-
- Vertical ScrollView + mapped entries instead of FlatList (100dp container is small)
964-
- Fixed 100dp height
965-
- Default expanded
966-
- Trash icon button (delete icon) for clearing logs, not a text button
967-
- Auto-scroll to newest using scrollToEnd on ScrollView ref
972+
973+
- Refer to the Logs View section of the shared style reference for layout, colors, and typography
974+
- Header sits above the list; 100dp height applies to the list area only
975+
- Newest entries at the top (prepend to state on each log)
976+
- Trash icon only visible when entries exist
968977

969978
Appium testID Labels:
970-
| testID | Description |
979+
| testID | Description |
971980
|-------------------------|------------------------------------|
972-
| log_view_container | Main container View |
973-
| log_view_header | Tappable expand/collapse row |
974-
| log_view_count | Shows "(N)" log count Text |
975-
| log_view_clear_button | Clear all logs TouchableOpacity |
976-
| log_view_list | Scrollable ScrollView |
977-
| log_view_empty | "No logs yet" Text |
978-
| log_entry_{N} | Each log row View (N=index) |
979-
| log_entry_{N}_timestamp | Timestamp Text |
980-
| log_entry_{N}_level | D/I/W/E indicator Text |
981-
| log_entry_{N}_message | Log message Text |
981+
| log*view_container | Main container View |
982+
| log_view_header | Tappable expand/collapse row |
983+
| log_view_count | Shows "(N)" log count Text |
984+
| log_view_clear_button | Clear all logs TouchableOpacity |
985+
| log_view_list | Scrollable ScrollView |
986+
| log_view_empty | "No logs yet" Text |
987+
| log_entry*{N} | Each log row View (N=index) |
988+
| log*entry*{N}_timestamp | Timestamp Text |
989+
| log_entry_{N}_level | D/I/W/E indicator Text |
990+
| log_entry_{N}\_message | Log message Text |
982991

983992
Use the testID prop for Appium accessibility:
984993
<Text testID={`log_entry_${index}_message`}>{entry.message}</Text>
@@ -1000,6 +1009,7 @@ All user actions should display Toast messages via react-native-toast-message:
10001009
- Push: "Push enabled/disabled"
10011010

10021011
Implementation:
1012+
10031013
- Place <Toast /> at the root of App.tsx (outside NavigationContainer children)
10041014
- Show at the bottom of the screen: <Toast position="bottom" bottomOffset={20} />
10051015
- Call Toast.show({ type: 'info', text1: message }) from action handlers

0 commit comments

Comments
 (0)