@cxpinsight/react-native 0.18.4 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/lib/CXPinsight.d.ts.map +1 -1
- package/lib/CXPinsight.js +2 -16
- package/lib/CXPinsight.js.map +1 -1
- package/lib/CXPinsightProvider.d.ts.map +1 -1
- package/lib/CXPinsightProvider.js +168 -5
- package/lib/CXPinsightProvider.js.map +1 -1
- package/lib/core/mobileRecorder.d.ts +0 -1
- package/lib/core/mobileRecorder.d.ts.map +1 -1
- package/lib/core/mobileRecorder.js +45 -53
- package/lib/core/mobileRecorder.js.map +1 -1
- package/lib/survey/SurveyModal.d.ts +7 -2
- package/lib/survey/SurveyModal.d.ts.map +1 -1
- package/lib/survey/SurveyModal.js +268 -51
- package/lib/survey/SurveyModal.js.map +1 -1
- package/lib/survey/questions/MultipleChoiceQuestion.d.ts.map +1 -1
- package/lib/survey/questions/MultipleChoiceQuestion.js +7 -1
- package/lib/survey/questions/MultipleChoiceQuestion.js.map +1 -1
- package/lib/types.d.ts +20 -6
- package/lib/types.d.ts.map +1 -1
- package/lib/types.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +2 -2
|
@@ -239,7 +239,7 @@ function swipeRelease(g) {
|
|
|
239
239
|
function surfaceScrolls(interactionKind) {
|
|
240
240
|
return String(interactionKind || '').toLowerCase() !== 'notify';
|
|
241
241
|
}
|
|
242
|
-
function SurveyModal({ survey, visible, onSubmit, onDismiss }) {
|
|
242
|
+
function SurveyModal({ survey, visible, onSubmit, onDismiss, reply = null }) {
|
|
243
243
|
const [currentPage, setCurrentPage] = (0, react_2.useState)(0);
|
|
244
244
|
const [responses, setResponses] = (0, react_2.useState)({});
|
|
245
245
|
const [showThankYou, setShowThankYou] = (0, react_2.useState)(false);
|
|
@@ -609,7 +609,7 @@ function SurveyModal({ survey, visible, onSubmit, onDismiss }) {
|
|
|
609
609
|
// server rejection (survey deactivated, cap hit) surfaces a retry
|
|
610
610
|
// message instead of a false thank-you.
|
|
611
611
|
const submitAndReset = async () => {
|
|
612
|
-
const ok = await onSubmit(buildSubmitPayload());
|
|
612
|
+
const ok = await onSubmit(buildSubmitPayload(), { via: viaRef.current });
|
|
613
613
|
if (ok === false) {
|
|
614
614
|
setSubmitError('We couldn’t submit your response. Please try again.');
|
|
615
615
|
return; // keep the survey open for a retry
|
|
@@ -621,6 +621,15 @@ function SurveyModal({ survey, visible, onSubmit, onDismiss }) {
|
|
|
621
621
|
if (String(survey.interactionType || '')
|
|
622
622
|
.toLowerCase() === 'notify')
|
|
623
623
|
return;
|
|
624
|
+
// AND WHEN THE AUTHOR TURNED THE ENDING OFF.
|
|
625
|
+
//
|
|
626
|
+
// showsConfirmation gated the OUTCOME panel below and not this state, so
|
|
627
|
+
// switching the ending off did not remove the ending — it fell through to
|
|
628
|
+
// the generic "Thank you" instead, which is a worse result than leaving it
|
|
629
|
+
// alone: the author lost the reward panel and gained a line they never
|
|
630
|
+
// wrote. Skipped here, so nothing draws and the 2.5s window never opens.
|
|
631
|
+
if (!(0, survey_spec_1.showsConfirmation)(survey))
|
|
632
|
+
return;
|
|
624
633
|
setShowThankYou(true);
|
|
625
634
|
const duration = survey.settings?.thankYouDuration ?? 2500;
|
|
626
635
|
setTimeout(() => {
|
|
@@ -750,7 +759,19 @@ function SurveyModal({ survey, visible, onSubmit, onDismiss }) {
|
|
|
750
759
|
* the interaction at all, because unlike web the card here was never
|
|
751
760
|
* pressable in the first place. Same defect, one step worse.
|
|
752
761
|
*/
|
|
753
|
-
|
|
762
|
+
// WHICH SURFACE WAS PRESSED — 'button' or 'card'.
|
|
763
|
+
//
|
|
764
|
+
// Both routes end in pressImplied, so by the time the answer is built the
|
|
765
|
+
// two are the same choice and nothing downstream can tell them apart. Web
|
|
766
|
+
// has carried this since bf0c4b55 (metadata.via); mobile gained whole-card
|
|
767
|
+
// press without it, so on the platform where nearly all the traffic is, the
|
|
768
|
+
// builder's "Clicked the card" outcome could never be populated.
|
|
769
|
+
//
|
|
770
|
+
// A ref, not state: it is read during the submit that the press itself
|
|
771
|
+
// triggers, and a re-render in between would be a wasted frame.
|
|
772
|
+
const viaRef = (0, react_2.useRef)('button');
|
|
773
|
+
const pressImplied = (0, react_2.useCallback)((key, a, via = 'button') => {
|
|
774
|
+
viaRef.current = via;
|
|
754
775
|
// "Remind me later" dismisses instead of answering, so the cadence records
|
|
755
776
|
// a SKIP and brings the interaction back after the author's wait.
|
|
756
777
|
// Answering would record a decision this person explicitly declined to
|
|
@@ -858,22 +879,16 @@ function SurveyModal({ survey, visible, onSubmit, onDismiss }) {
|
|
|
858
879
|
// MultipleChoiceQuestion accepts. hasOther appends a synthetic
|
|
859
880
|
// "Other" entry — full free-text follow-up UX lands in Phase 4.5.
|
|
860
881
|
function normalizeChoices(question) {
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
if (typeof c === 'string')
|
|
872
|
-
out.push({ value: c, text: c });
|
|
873
|
-
else if (c)
|
|
874
|
-
out.push({ value: String(c.value ?? ''), text: String(c.label ?? c.text ?? c.value ?? '') });
|
|
875
|
-
}
|
|
876
|
-
}
|
|
882
|
+
// WHICHEVER FIELD HAS SOMETHING IN IT — see questionChoices.
|
|
883
|
+
//
|
|
884
|
+
// This asked `Array.isArray(options)` first and took that branch. The
|
|
885
|
+
// server's schema declares `options`, so Mongoose defaults it to `[]` on
|
|
886
|
+
// every question: a question authored with `choices` arrived carrying an
|
|
887
|
+
// empty `options` beside it, this took the empty branch, and the phone
|
|
888
|
+
// drew a choice question with no choices at all. The web preview read
|
|
889
|
+
// `choices` first and was fine, so it looked like a mobile rendering bug
|
|
890
|
+
// and was a field-precedence one.
|
|
891
|
+
const out = (0, survey_spec_1.questionChoices)(question);
|
|
877
892
|
if (question.hasOther) {
|
|
878
893
|
out.push({ value: '__other__', text: question.otherLabel || 'Other' });
|
|
879
894
|
}
|
|
@@ -1074,7 +1089,7 @@ function SurveyModal({ survey, visible, onSubmit, onDismiss }) {
|
|
|
1074
1089
|
// The press is both the answer and the Next — the footer is
|
|
1075
1090
|
// hidden on this step. Its body is pressImplied, so the CARD
|
|
1076
1091
|
// can do exactly the same thing when this button is not drawn.
|
|
1077
|
-
onPress: () => pressImplied(key, a), accessibilityRole: "button", accessibilityState: { selected: val === a.value }, style: [
|
|
1092
|
+
onPress: () => pressImplied(key, a, 'button'), accessibilityRole: "button", accessibilityState: { selected: val === a.value }, style: [
|
|
1078
1093
|
styles.impliedBtn,
|
|
1079
1094
|
// BESIDE THE TEXT, THE BUTTON IS AS WIDE AS ITS LABEL.
|
|
1080
1095
|
//
|
|
@@ -1371,7 +1386,6 @@ function SurveyModal({ survey, visible, onSubmit, onDismiss }) {
|
|
|
1371
1386
|
const fs = baseScale !== 1;
|
|
1372
1387
|
const qTitleFS = fs ? { fontSize: scaled(15) } : undefined;
|
|
1373
1388
|
const qDescFS = fs ? { fontSize: scaled(13) } : undefined;
|
|
1374
|
-
const thankYouFS = fs ? { fontSize: scaled(18) } : undefined;
|
|
1375
1389
|
const buttonFS = fs ? { fontSize: scaled(15) } : undefined;
|
|
1376
1390
|
// Layout tokens (spec §24). String presets map to concrete pixel
|
|
1377
1391
|
// values; a number passes through verbatim so authors can set
|
|
@@ -1750,10 +1764,12 @@ function SurveyModal({ survey, visible, onSubmit, onDismiss }) {
|
|
|
1750
1764
|
* The alternative was a Fade control that worked in the preview and did
|
|
1751
1765
|
* nothing here, which is the thing this codebase keeps having to undo.
|
|
1752
1766
|
*/
|
|
1753
|
-
|
|
1754
|
-
|
|
1767
|
+
// `fade` is a parameter now, because there are two pictures asking: the
|
|
1768
|
+
// card's and the confirmation's, each with its own slider.
|
|
1769
|
+
const fadeSteps = (towards, fade = imgD.fade) => {
|
|
1770
|
+
if (!fade)
|
|
1755
1771
|
return null;
|
|
1756
|
-
const span = Math.max(2, Math.min(96,
|
|
1772
|
+
const span = Math.max(2, Math.min(96, fade)) / 100;
|
|
1757
1773
|
const vertical = towards === 'top' || towards === 'bottom';
|
|
1758
1774
|
// A REAL GRADIENT WHERE THE APP HAS ONE. react-native-svg is an optional
|
|
1759
1775
|
// peer, already loaded this way by the signature question, and it draws
|
|
@@ -1879,6 +1895,96 @@ function SurveyModal({ survey, visible, onSubmit, onDismiss }) {
|
|
|
1879
1895
|
const halfPadR = contract.cardPaddingRight ?? contract.cardPadding;
|
|
1880
1896
|
const halfOuter = imgD.side === 'right' ? halfPadR : halfPadL;
|
|
1881
1897
|
const halfWidth = Math.round(((cardWidth - halfPadL - halfPadR) * imgD.split) / 100) + halfOuter;
|
|
1898
|
+
/**
|
|
1899
|
+
* THE ENDING'S OWN PICTURE, which is not the card's.
|
|
1900
|
+
*
|
|
1901
|
+
* An offer's card sells the thing; its ending confirms it, and those want
|
|
1902
|
+
* different images far more often than the same one. It stands IN FOR the
|
|
1903
|
+
* ✓ glyph rather than sitting above it — a tick over a photograph is two
|
|
1904
|
+
* things saying "done", and the author chose the photograph.
|
|
1905
|
+
*/
|
|
1906
|
+
// The card's height, as it was just before the ending replaced it — a floor
|
|
1907
|
+
// for the ending, never a cap, so a taller ending can still grow.
|
|
1908
|
+
const cardHeightRef = (0, react_2.useRef)(0);
|
|
1909
|
+
// A FLOOR AND A FILL, MINUS THE CARD'S OWN PADDING.
|
|
1910
|
+
//
|
|
1911
|
+
// The floor alone left the panel at its natural height inside a taller card,
|
|
1912
|
+
// so a picture told to stretch beside the words took the confirmation's few
|
|
1913
|
+
// lines rather than the card's height. And the floor was the CARD's height
|
|
1914
|
+
// — the outer view, padding included — applied to a panel that sits inside
|
|
1915
|
+
// that padding, which asks the card to grow by twice its own inset every
|
|
1916
|
+
// time the ending appears. The web measures the CONTENT; this is the same
|
|
1917
|
+
// number, arrived at by subtraction because the card is what onLayout sees.
|
|
1918
|
+
const endPadTop = contract.cardPaddingTop ?? contract.cardPadding ?? 0;
|
|
1919
|
+
const endPadBottom = contract.cardPaddingBottom ?? contract.cardPadding ?? 0;
|
|
1920
|
+
const endingHold = cardHeightRef.current > 0
|
|
1921
|
+
? {
|
|
1922
|
+
minHeight: Math.max(0, cardHeightRef.current - endPadTop - endPadBottom),
|
|
1923
|
+
flexGrow: 1,
|
|
1924
|
+
}
|
|
1925
|
+
: null;
|
|
1926
|
+
// THE CONFIRMATION'S OWN DESIGN, inheriting the card's until it has one —
|
|
1927
|
+
// see confirmationDesign in the spec. It used to read `contract.imageHeight`
|
|
1928
|
+
// and `contract.imageRadius` directly, which tied the icon on this screen to
|
|
1929
|
+
// the photograph on the card: shrinking one shrank the other.
|
|
1930
|
+
// WHERE THE CARD'S WORDS SIT. `titleAlign` was already a survey field; the
|
|
1931
|
+
// other two are new and follow it, because a centred headline over a
|
|
1932
|
+
// left-ragged paragraph is the commonest way a card looks accidental.
|
|
1933
|
+
const cardTitleAlign = (0, survey_spec_1.alignOf)(survey.titleAlign, 'left');
|
|
1934
|
+
const cardDescAlign = (0, survey_spec_1.alignOf)(survey.descriptionAlign, cardTitleAlign);
|
|
1935
|
+
const cardQuestionAlign = (0, survey_spec_1.alignOf)(survey.questionAlign, 'left');
|
|
1936
|
+
const confirmD = (0, survey_spec_1.confirmationDesign)(survey, 'mobile');
|
|
1937
|
+
const endingImgSrc = confirmD.image.src;
|
|
1938
|
+
const endingAlign = confirmD.heading.align === 'start' ? 'flex-start'
|
|
1939
|
+
: confirmD.heading.align === 'end' ? 'flex-end' : 'center';
|
|
1940
|
+
const endingTextAlign = confirmD.heading.align === 'start' ? 'left'
|
|
1941
|
+
: confirmD.heading.align === 'end' ? 'right' : 'center';
|
|
1942
|
+
// LINE HEIGHT MOVES WITH THE SIZE. The stylesheet pins 26 and 20, so a
|
|
1943
|
+
// heading taken to 34 would have been clipped by its own leading.
|
|
1944
|
+
// scaled() FIRST, so the accessibility font scale still multiplies an
|
|
1945
|
+
// authored size — it used to scale a hard-coded 18 and the authored number
|
|
1946
|
+
// would have replaced it outright.
|
|
1947
|
+
const endingHeadSize = scaled(confirmD.heading.size);
|
|
1948
|
+
const endingSubSize = scaled(confirmD.body.size);
|
|
1949
|
+
const endingBodyAlign = confirmD.body.align === 'start' ? 'left'
|
|
1950
|
+
: confirmD.body.align === 'end' ? 'right' : 'center';
|
|
1951
|
+
const endingHeadFS = {
|
|
1952
|
+
fontSize: endingHeadSize,
|
|
1953
|
+
lineHeight: Math.round(endingHeadSize * 1.45),
|
|
1954
|
+
textAlign: endingTextAlign,
|
|
1955
|
+
};
|
|
1956
|
+
const endingSubFS = {
|
|
1957
|
+
fontSize: endingSubSize,
|
|
1958
|
+
lineHeight: Math.round(endingSubSize * confirmD.body.lineHeight),
|
|
1959
|
+
textAlign: endingBodyAlign,
|
|
1960
|
+
};
|
|
1961
|
+
// THE CARD'S WHOLE IMAGE VOCABULARY, on the second screen. It was a band and
|
|
1962
|
+
// nothing else — no Fade, no Dim, no Alignment, no Layout — because the
|
|
1963
|
+
// screen was built as a stack and the design was a hand-picked five fields
|
|
1964
|
+
// of the card's. confirmationDesign now answers with a full ImageDesign, so
|
|
1965
|
+
// there is one set of layouts and both screens draw from it.
|
|
1966
|
+
const cI = confirmD.image;
|
|
1967
|
+
const endingLayout = endingImgSrc ? cI.layout : 'none';
|
|
1968
|
+
const endingSelf = cI.align === 'start' ? 'flex-start'
|
|
1969
|
+
: cI.align === 'end' ? 'flex-end' : 'center';
|
|
1970
|
+
const endingScrim = cI.scrim ? ((0, jsx_runtime_1.jsx)(react_native_1.View, { pointerEvents: "none", style: { position: 'absolute', top: 0, right: 0, bottom: 0, left: 0,
|
|
1971
|
+
backgroundColor: contract.card, opacity: cI.scrim / 100 } })) : null;
|
|
1972
|
+
const endingPicture = (fill) => ((0, jsx_runtime_1.jsx)(react_native_1.Image, { source: { uri: endingImgSrc }, style: [{ width: '100%', height: '100%' }, fill], resizeMode: cI.fit, accessibilityIgnoresInvertColors: true }));
|
|
1973
|
+
/** The band — the picture as a strip, at its own width and alignment. */
|
|
1974
|
+
const endingImage = (endingLayout === 'above' || endingLayout === 'below') ? ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: {
|
|
1975
|
+
position: 'relative', overflow: 'hidden',
|
|
1976
|
+
width: `${cI.width}%`, height: cI.height, borderRadius: cI.radius,
|
|
1977
|
+
alignSelf: endingSelf, marginBottom: endingLayout === 'above' ? 16 : 0,
|
|
1978
|
+
marginTop: endingLayout === 'below' ? 16 : 0,
|
|
1979
|
+
}, children: [endingPicture({}), fadeSteps(endingLayout === 'below' ? 'top' : 'bottom', cI.fade), endingScrim] })) : null;
|
|
1980
|
+
// THE SECOND PICTURE, between the card's and the headline. Square, so one
|
|
1981
|
+
// number sizes it — and it replaces the ✓ rather than sitting under it.
|
|
1982
|
+
const endingIcon = confirmD.icon.src ? ((0, jsx_runtime_1.jsx)(react_native_1.Image, { source: { uri: confirmD.icon.src }, style: {
|
|
1983
|
+
width: confirmD.icon.size,
|
|
1984
|
+
height: confirmD.icon.size,
|
|
1985
|
+
borderRadius: confirmD.icon.radius,
|
|
1986
|
+
marginBottom: 16,
|
|
1987
|
+
}, resizeMode: confirmD.icon.fit, accessibilityIgnoresInvertColors: true })) : null;
|
|
1882
1988
|
const cardImageHalf = imgLayout === 'half' ? ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: {
|
|
1883
1989
|
position: 'relative',
|
|
1884
1990
|
overflow: 'hidden',
|
|
@@ -2227,7 +2333,7 @@ function SurveyModal({ survey, visible, onSubmit, onDismiss }) {
|
|
|
2227
2333
|
// uses, which is what keeps a deliberate flick from also counting as a tap.
|
|
2228
2334
|
if (!st.moved) {
|
|
2229
2335
|
if (cardTakesPress && cardImpliedList.length) {
|
|
2230
|
-
pressImplied(cardImpliedKey, cardImpliedList[0]);
|
|
2336
|
+
pressImplied(cardImpliedKey, cardImpliedList[0], 'card');
|
|
2231
2337
|
}
|
|
2232
2338
|
return;
|
|
2233
2339
|
}
|
|
@@ -2273,11 +2379,14 @@ function SurveyModal({ survey, visible, onSubmit, onDismiss }) {
|
|
|
2273
2379
|
const last = qs[qs.length - 1];
|
|
2274
2380
|
const id = last?.name ?? last?.id ?? '';
|
|
2275
2381
|
const action = id ? (responses[id] ?? null) : null;
|
|
2276
|
-
|
|
2382
|
+
// The SCREEN, not the outcome the host hears: an accepted offer with no
|
|
2383
|
+
// code is 'fulfil' to the host and the author's confirmation here — the
|
|
2384
|
+
// web renderer asks the same question.
|
|
2385
|
+
return (0, survey_spec_1.endingScreenFor)((0, survey_spec_1.outcomeFor)({
|
|
2277
2386
|
interactionType: sv.interactionType, action,
|
|
2278
2387
|
affirmative: String(action) === String(acts[0].value),
|
|
2279
2388
|
reward: sv.reward, compliance: sv.compliance,
|
|
2280
|
-
});
|
|
2389
|
+
}), sv);
|
|
2281
2390
|
}, [survey, responses]);
|
|
2282
2391
|
const dismissMs = (0, survey_spec_1.autoDismissMs)(display, survey.delivery);
|
|
2283
2392
|
const engagedRef = (0, react_2.useRef)(false);
|
|
@@ -2527,7 +2636,28 @@ function SurveyModal({ survey, visible, onSubmit, onDismiss }) {
|
|
|
2527
2636
|
// The OVERLAY, not the card — a screen-shaped image with the card in
|
|
2528
2637
|
// its real position, rather than a card-shaped one the replay player
|
|
2529
2638
|
// would stretch across the whole device frame.
|
|
2530
|
-
ref: (v) => { cardViewRef.current = v; },
|
|
2639
|
+
ref: (v) => { cardViewRef.current = v; },
|
|
2640
|
+
// WITHOUT THIS THE VIEW MAY NOT EXIST NATIVELY, AND THE CAPTURE FAILS.
|
|
2641
|
+
//
|
|
2642
|
+
// React Native collapses views that have no native side effects. For a
|
|
2643
|
+
// POPUP this overlay is exactly that: backgroundColor transparent and
|
|
2644
|
+
// pointerEvents box-none, a container that only positions its child. So
|
|
2645
|
+
// it was optimised away, findNodeHandle had nothing to resolve,
|
|
2646
|
+
// captureRef rejected, captureView returned null, and the recorder fell
|
|
2647
|
+
// back to the host captureScreen — which on iOS returns the TOPMOST
|
|
2648
|
+
// UIWindow.
|
|
2649
|
+
//
|
|
2650
|
+
// That is why every offer and notification frame came back as whatever
|
|
2651
|
+
// native alert happened to be up, floating on flat grey, with the card
|
|
2652
|
+
// nowhere in it. Measured: three survey_shown frames at 73,548 / 73,512
|
|
2653
|
+
// / 73,528 bytes — the same wrong image each time — in a session where
|
|
2654
|
+
// the respondent demonstrably swiped one card away, closed another and
|
|
2655
|
+
// completed a third.
|
|
2656
|
+
//
|
|
2657
|
+
// It fails for popups specifically BECAUSE popups are the transparent
|
|
2658
|
+
// ones; a modal paints a scrim and survives collapsing, which is why
|
|
2659
|
+
// this looked like "offers are broken" rather than "capture is".
|
|
2660
|
+
collapsable: false, pointerEvents: isPopupOverlay ? 'box-none' : 'auto', style: [
|
|
2531
2661
|
styles.overlay,
|
|
2532
2662
|
// Transparent for a popup — the app behind it is not being taken over.
|
|
2533
2663
|
isPopupOverlay && { backgroundColor: 'transparent' },
|
|
@@ -2552,10 +2682,19 @@ function SurveyModal({ survey, visible, onSubmit, onDismiss }) {
|
|
|
2552
2682
|
// Feeds the authored-coordinate maths above. Only tracked when a
|
|
2553
2683
|
// coordinate is actually in play, and only on a real change, so the
|
|
2554
2684
|
// common case costs nothing and this cannot loop.
|
|
2555
|
-
|
|
2685
|
+
// MEASURED ALWAYS, not only for a coordinate. The second reader is
|
|
2686
|
+
// the confirmation: it replaces the card's whole content, and the
|
|
2687
|
+
// two screens hold different amounts — so the card shrank under a
|
|
2688
|
+
// respondent who had just pressed a button on it, which reads as a
|
|
2689
|
+
// different card arriving rather than the same one answering. The
|
|
2690
|
+
// last height measured BEFORE the ending becomes its floor.
|
|
2691
|
+
onLayout: (e) => {
|
|
2556
2692
|
const h = Math.round(e.nativeEvent.layout.height);
|
|
2557
|
-
|
|
2558
|
-
|
|
2693
|
+
if (!showThankYou && h > 0)
|
|
2694
|
+
cardHeightRef.current = h;
|
|
2695
|
+
if (hasPopupY)
|
|
2696
|
+
setMeasuredCardH((prev) => (Math.abs(prev - h) > 1 ? h : prev));
|
|
2697
|
+
}, style: [
|
|
2559
2698
|
styles.card,
|
|
2560
2699
|
themeCard,
|
|
2561
2700
|
enterStyle,
|
|
@@ -2575,34 +2714,96 @@ function SurveyModal({ survey, visible, onSubmit, onDismiss }) {
|
|
|
2575
2714
|
], children: [cardImageBack, closeShown && ((0, jsx_runtime_1.jsx)(react_native_1.TouchableOpacity, { ...swipeExempt, style: [
|
|
2576
2715
|
styles.closeButton,
|
|
2577
2716
|
isRtl ? { left: contract.closeInset } : { right: contract.closeInset },
|
|
2578
|
-
], onPress: () => handleDismiss('close'), activeOpacity: 0.7, children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.closeGlyph, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: [styles.closeBar, { transform: [{ rotate: '45deg' }] }] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [styles.closeBar, { transform: [{ rotate: '-45deg' }] }] })] }) })), showThankYou && endingKind !== 'thanks' && (0, survey_spec_1.showsConfirmation)(survey) ? (() => {
|
|
2579
|
-
const rw = (0, survey_spec_1.rewardToShow)(survey);
|
|
2717
|
+
], onPress: () => handleDismiss('close'), activeOpacity: 0.7, children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.closeGlyph, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: [styles.closeBar, { transform: [{ rotate: '45deg' }] }] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [styles.closeBar, { transform: [{ rotate: '-45deg' }] }] })] }) })), showThankYou && endingKind !== 'thanks' && endingKind !== 'fulfil' && (0, survey_spec_1.showsConfirmation)(survey) ? (() => {
|
|
2580
2718
|
const comp = survey.compliance;
|
|
2581
|
-
if (endingKind === 'fulfil' && rw) {
|
|
2582
|
-
const exp = rw.expiresAt ? new Date(rw.expiresAt) : null;
|
|
2583
|
-
return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.thankYou, children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.thankYouTitle, fontStyle, themeTextPrimary], children: (0, survey_spec_1.confirmationText)(survey, 'It’s yours') }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { selectable: true, style: [styles.rewardCode, fontStyle, themeTextPrimary], children: rw.value }), !!rw.whereValid && ((0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.thankYouSub, fontStyle, themeTextSecondary], children: rw.whereValid })), !!exp && ((0, jsx_runtime_1.jsxs)(react_native_1.Text, { style: [styles.thankYouSub, fontStyle, themeTextSecondary], children: ["Expires ", exp.toLocaleString()] }))] }));
|
|
2584
|
-
}
|
|
2585
2719
|
if (endingKind === 'hold') {
|
|
2586
|
-
return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.thankYou, accessibilityLiveRegion: "polite", children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.thankYouTitle, fontStyle, themeTextPrimary], children: comp?.reviewRequired ? 'Under review' : 'Not completed' }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.thankYouSub, fontStyle, themeTextSecondary], children: comp?.blockedMessage
|
|
2720
|
+
return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [styles.thankYou, endingHold, { alignItems: endingAlign }], accessibilityLiveRegion: "polite", children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.thankYouTitle, fontStyle, themeTextPrimary, endingHeadFS], children: comp?.reviewRequired ? 'Under review' : 'Not completed' }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.thankYouSub, fontStyle, themeTextSecondary, endingSubFS], children: comp?.blockedMessage
|
|
2587
2721
|
?? (comp?.reviewRequired
|
|
2588
2722
|
? 'We’re checking your details. You’ll be able to continue once that’s done.'
|
|
2589
2723
|
: 'You need to accept this before you can continue.') })] }));
|
|
2590
2724
|
}
|
|
2591
2725
|
if (endingKind === 'unblock') {
|
|
2592
|
-
return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.thankYou, children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.thankYouTitle, fontStyle, themeTextPrimary], children: "Recorded" }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.thankYouSub, fontStyle, themeTextSecondary], children: "You can carry on." })] }));
|
|
2726
|
+
return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [styles.thankYou, endingHold, { alignItems: endingAlign }], children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.thankYouTitle, fontStyle, themeTextPrimary, endingHeadFS], children: "Recorded" }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.thankYouSub, fontStyle, themeTextSecondary, endingSubFS], children: "You can carry on." })] }));
|
|
2593
2727
|
}
|
|
2594
|
-
return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.thankYou, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.thankYouSub, fontStyle, themeTextSecondary], children: endingKind === 'navigate' ? 'Taking you there…' : 'Closed.' }) }));
|
|
2728
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [styles.thankYou, endingHold, { alignItems: endingAlign }], children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.thankYouSub, fontStyle, themeTextSecondary, endingSubFS], children: endingKind === 'navigate' ? 'Taking you there…' : 'Closed.' }) }));
|
|
2729
|
+
})() : (showThankYou && (reply === 'pending' || reply === 'failed' || reply === 'later')) ? (() => {
|
|
2730
|
+
// WAITING FOR THE APP, or what came of it — the author's words for
|
|
2731
|
+
// each state (appReply), in the confirmation's own header style.
|
|
2732
|
+
const ar = (0, survey_spec_1.appReply)(survey);
|
|
2733
|
+
const words = reply === 'pending' ? ar.pending : reply === 'failed' ? ar.failed : ar.later;
|
|
2734
|
+
return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [styles.thankYou, endingHold, { alignItems: endingAlign }], accessibilityLiveRegion: reply === 'failed' ? 'assertive' : 'polite', children: [reply === 'pending' && (0, jsx_runtime_1.jsx)(react_native_1.ActivityIndicator, { color: primaryColor, style: { marginBottom: 12 } }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.thankYouTitle, dirText, fontStyle, themeTextPrimary, endingHeadFS], children: (0, interpolate_1.interpolatePlain)(words, evalCtx) })] }));
|
|
2595
2735
|
})() : showThankYou ? (() => {
|
|
2596
2736
|
// The thank-you message can be rich HTML from the web builder.
|
|
2597
2737
|
// Pull out an embedded <img> (often a data: URI icon) and render
|
|
2598
2738
|
// it as a real Image — stripHtml would otherwise drop it — then
|
|
2599
2739
|
// show the remaining text. When an image is present it stands in
|
|
2600
2740
|
// for the default ✓ glyph.
|
|
2741
|
+
// THE CONFIRMATION'S COMPONENTS, under its headline and body — the
|
|
2742
|
+
// schema's list, derived for anything made before v1.2 (a code
|
|
2743
|
+
// offer gets its code). Same rule the web renderer asks.
|
|
2744
|
+
const endParts = (0, survey_spec_1.confirmationQuestions)(survey);
|
|
2745
|
+
const endReward = (0, survey_spec_1.rewardToShow)(survey);
|
|
2746
|
+
const hasCode = !!endReward && endReward.type === 'code'
|
|
2747
|
+
&& endParts.some((q) => String(q.type).toLowerCase() === 'reward');
|
|
2601
2748
|
const rawThankYou = (0, interpolate_1.interpolatePlain)((0, translationLookup_1.surveyText)(survey, currentLanguage, 'thankYouMessage', survey.settings?.thankYouMessage)
|
|
2602
|
-
??
|
|
2749
|
+
?? (0, survey_spec_1.confirmationDefaults)(survey, hasCode).title, evalCtx);
|
|
2750
|
+
const partViews = endParts.map((q) => {
|
|
2751
|
+
const t = String(q.type).toLowerCase();
|
|
2752
|
+
if (t === 'reward') {
|
|
2753
|
+
if (!hasCode || !endReward)
|
|
2754
|
+
return null;
|
|
2755
|
+
const exp = endReward.expiresAt ? new Date(endReward.expiresAt) : null;
|
|
2756
|
+
return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: { marginTop: 14, alignItems: endingAlign }, children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { selectable: true, style: [styles.rewardCode, fontStyle, themeTextPrimary], children: endReward.value }), !!endReward.whereValid && ((0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.thankYouSub, dirText, fontStyle, themeTextSecondary, endingSubFS], children: endReward.whereValid })), !!exp && ((0, jsx_runtime_1.jsxs)(react_native_1.Text, { style: [styles.thankYouSub, dirText, fontStyle, themeTextSecondary, endingSubFS], children: ["Expires ", exp.toLocaleString()] }))] }, q.id));
|
|
2757
|
+
}
|
|
2758
|
+
const words = stripHtml((0, interpolate_1.interpolatePlain)(String(q.html ?? q.text ?? ''), evalCtx));
|
|
2759
|
+
return words ? ((0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.thankYouSub, dirText, fontStyle, themeTextPrimary, endingSubFS, { marginTop: 12 }], children: words }, q.id)) : null;
|
|
2760
|
+
});
|
|
2603
2761
|
const imgSrc = firstImageSrc(rawThankYou);
|
|
2604
2762
|
const thankYouText = stripHtml(rawThankYou);
|
|
2605
|
-
|
|
2763
|
+
// THE WORDS, AS ONE BLOCK — so a layout can put them beside the
|
|
2764
|
+
// picture, after it, or on top of it without three copies. The ✓
|
|
2765
|
+
// stands in only when NEITHER picture is set; it used to stand in
|
|
2766
|
+
// for the first alone, so a badge added to a card that already had
|
|
2767
|
+
// a photograph drew the photograph and swallowed the badge.
|
|
2768
|
+
const endWords = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [endingIcon || ((endingImgSrc || imgSrc || hasCode) ? null : ((0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.thankYouEmoji, { color: primaryColor }], children: "\u2713" }))), thankYouText ? ((0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.thankYouTitle, dirText, fontStyle, themeTextPrimary, endingHeadFS], children: thankYouText })) : null, (() => {
|
|
2769
|
+
const sub = (0, survey_spec_1.confirmationSubtext)(survey, (0, survey_spec_1.confirmationDefaults)(survey, hasCode).body);
|
|
2770
|
+
return sub ? ((0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.thankYouSub, dirText, fontStyle, themeTextSecondary, endingSubFS], children: (0, interpolate_1.interpolatePlain)(sub, evalCtx) })) : null;
|
|
2771
|
+
})(), partViews] }));
|
|
2772
|
+
// THE CARD'S OWN PADDING, undone on the picture's side only. The
|
|
2773
|
+
// panel's 48 used to sit INSIDE the card's, so the same picture at
|
|
2774
|
+
// the same Height drew smaller here than on the card.
|
|
2775
|
+
const endPad = 0;
|
|
2776
|
+
if (endingLayout === 'beside') {
|
|
2777
|
+
return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [styles.thankYou, endingHold, {
|
|
2778
|
+
alignItems: 'stretch', flexDirection: 'row', gap: 12,
|
|
2779
|
+
}], children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: {
|
|
2780
|
+
position: 'relative', overflow: 'hidden', alignSelf: endingSelf,
|
|
2781
|
+
width: cI.size, height: cI.size, borderRadius: cI.radius,
|
|
2782
|
+
}, children: [endingPicture({}), endingScrim] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: { flex: 1, minWidth: 0, alignItems: endingAlign, justifyContent: 'center' }, children: endWords })] }));
|
|
2783
|
+
}
|
|
2784
|
+
if (endingLayout === 'half') {
|
|
2785
|
+
const vert = cI.side === 'top';
|
|
2786
|
+
// Negative margins undo the panel's own padding on the picture's
|
|
2787
|
+
// side only — the words keep theirs, which is what "bleeds to
|
|
2788
|
+
// the edges" means on the card too.
|
|
2789
|
+
return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [styles.thankYou, endingHold, {
|
|
2790
|
+
alignItems: 'stretch',
|
|
2791
|
+
flexDirection: vert ? 'column' : (cI.side === 'right' ? 'row-reverse' : 'row'),
|
|
2792
|
+
gap: vert ? 0 : 14,
|
|
2793
|
+
}], children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: {
|
|
2794
|
+
position: 'relative', overflow: 'hidden',
|
|
2795
|
+
...(vert
|
|
2796
|
+
? { marginTop: -endPad, marginLeft: -endPad, marginRight: -endPad,
|
|
2797
|
+
marginBottom: 16, height: cI.height }
|
|
2798
|
+
: { marginTop: -endPad, marginBottom: -endPad,
|
|
2799
|
+
[cI.side === 'right' ? 'marginRight' : 'marginLeft']: -endPad,
|
|
2800
|
+
width: `${cI.split}%` }),
|
|
2801
|
+
}, children: [endingPicture({}), fadeSteps(vert ? 'bottom' : (cI.side === 'right' ? 'left' : 'right'), cI.fade), endingScrim] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: { flex: 1, minWidth: 0, alignItems: endingAlign, justifyContent: 'center' }, children: endWords })] }));
|
|
2802
|
+
}
|
|
2803
|
+
if (endingLayout === 'background') {
|
|
2804
|
+
return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [styles.thankYou, endingHold, { alignItems: endingAlign }], children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { pointerEvents: "none", style: { position: 'absolute', top: 0, right: 0, bottom: 0, left: 0, overflow: 'hidden' }, children: [endingPicture({}), endingScrim] }), endWords] }));
|
|
2805
|
+
}
|
|
2806
|
+
return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [styles.thankYou, endingHold, { alignItems: endingAlign }], children: [endingLayout === 'above' ? endingImage : null, (!endingImgSrc && imgSrc) ? ((0, jsx_runtime_1.jsx)(react_native_1.Image, { source: { uri: imgSrc }, style: styles.thankYouImage, resizeMode: "contain" })) : null, endWords, endingLayout === 'below' ? endingImage : null] }));
|
|
2606
2807
|
})() : ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: halfSide
|
|
2607
2808
|
? { flexDirection: imgD.side === 'right' ? 'row-reverse' : 'row',
|
|
2608
2809
|
alignItems: 'stretch' }
|
|
@@ -2656,9 +2857,7 @@ function SurveyModal({ survey, visible, onSubmit, onDismiss }) {
|
|
|
2656
2857
|
: imgD.align === 'end' ? 'flex-end' : 'center',
|
|
2657
2858
|
}, resizeMode: imgD.fit })) : null, (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: (besideImage || notifyBeside) ? { flex: 1, minWidth: 0 } : undefined, children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.title, dirText, fontStyle, themeTextPrimary, titleFS, notifyTitleFS,
|
|
2658
2859
|
closeReserve,
|
|
2659
|
-
|
|
2660
|
-
? { textAlign: survey.titleAlign }
|
|
2661
|
-
: null], children: (0, interpolate_1.interpolatePlain)(
|
|
2860
|
+
{ textAlign: cardTitleAlign }], children: (0, interpolate_1.interpolatePlain)(
|
|
2662
2861
|
// Title is plain text; strip any stray rich-text markup +
|
|
2663
2862
|
// decode so it never shows literal HTML on native.
|
|
2664
2863
|
String((0, translationLookup_1.surveyText)(survey, currentLanguage, 'title', survey.title) || '')
|
|
@@ -2672,6 +2871,7 @@ function SurveyModal({ survey, visible, onSubmit, onDismiss }) {
|
|
|
2672
2871
|
const descRaw = (0, translationLookup_1.surveyText)(survey, currentLanguage, 'description', survey.description);
|
|
2673
2872
|
const desc = String(descRaw || '').replace(/<[^>]*>/g, '').replace(/ /gi, ' ').trim();
|
|
2674
2873
|
return desc ? ((0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.description, dirText, fontStyle, themeTextSecondary, descFS, notifyDescFS,
|
|
2874
|
+
{ textAlign: cardDescAlign },
|
|
2675
2875
|
// Takes the width the button leaves, and no more.
|
|
2676
2876
|
notifyBeside && { flex: 1, minWidth: 0, marginTop: 2 }], children: (0, interpolate_1.interpolatePlain)(desc, evalCtx) })) : null;
|
|
2677
2877
|
})(), notifyActionQ ? ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: { flexShrink: 0, marginLeft: 12 }, children: renderQuestion(notifyActionQ) })) : null] })] }), deadlineEl, (() => {
|
|
@@ -2727,7 +2927,7 @@ function SurveyModal({ survey, visible, onSubmit, onDismiss }) {
|
|
|
2727
2927
|
// have. Web spends 12px above the action and nothing
|
|
2728
2928
|
// below it (`.cxp-notify-action-below`); this matches.
|
|
2729
2929
|
isNotifyBanner && { marginTop: 12, marginBottom: 0 },
|
|
2730
|
-
], children: [!(0, survey_spec_1.showsQuestionLabel)(q) ? null : ((0, jsx_runtime_1.jsxs)(react_native_1.Text, { style: [styles.questionTitle, dirText, fontStyle, themeTextPrimary, qTitleFS],
|
|
2930
|
+
], children: [!(0, survey_spec_1.showsQuestionLabel)(q) ? null : ((0, jsx_runtime_1.jsxs)(react_native_1.Text, { style: [styles.questionTitle, dirText, fontStyle, themeTextPrimary, qTitleFS, { textAlign: cardQuestionAlign }],
|
|
2731
2931
|
// The asterisk below is decoration: a screen reader
|
|
2732
2932
|
// reads it as "star", or skips it, so a required
|
|
2733
2933
|
// question sounded exactly like an optional one until
|
|
@@ -2736,7 +2936,7 @@ function SurveyModal({ survey, visible, onSubmit, onDismiss }) {
|
|
|
2736
2936
|
if (isImplied)
|
|
2737
2937
|
return null;
|
|
2738
2938
|
const qDesc = (0, translationLookup_1.questionText)(survey, currentLanguage, rowKey, 'description', q.description);
|
|
2739
|
-
return qDesc ? ((0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.questionDesc, dirText, fontStyle, themeTextMuted, qDescFS], children: (0, interpolate_1.interpolatePlain)(qDesc, evalCtx) })) : null;
|
|
2939
|
+
return qDesc ? ((0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.questionDesc, dirText, fontStyle, themeTextMuted, qDescFS, { textAlign: cardQuestionAlign }], children: (0, interpolate_1.interpolatePlain)(qDesc, evalCtx) })) : null;
|
|
2740
2940
|
})(), renderQuestion(q), errors[rowKey] ? ((0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.errorText, dirText, fontStyle],
|
|
2741
2941
|
// Announced when it appears, and reachable afterwards:
|
|
2742
2942
|
// a message that is only spoken once leaves someone who
|
|
@@ -2766,7 +2966,7 @@ function SurveyModal({ survey, visible, onSubmit, onDismiss }) {
|
|
|
2766
2966
|
// Right-align parity with web: Back hugs the left edge while
|
|
2767
2967
|
// Next/Submit sits on the right (web uses margin-right:auto).
|
|
2768
2968
|
buttonAlign === 'right' && { marginRight: 'auto' },
|
|
2769
|
-
], onPress: handleBack, activeOpacity: 0.7, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.backButtonText, fontStyle, themeTextSecondary, buttonFS], children: "Back" }) })), impliedNow.length > 0 ? null : (() => {
|
|
2969
|
+
], onPress: handleBack, activeOpacity: 0.7, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.backButtonText, fontStyle, themeTextSecondary, buttonFS], children: "Back" }) })), impliedNow.length > 0 || !(0, survey_spec_1.drawsOwnSubmit)(survey.interactionType) ? null : (() => {
|
|
2770
2970
|
// Reflect the actual outcome of pressing Next: if a
|
|
2771
2971
|
// branch resolves to __end__ we're submitting even
|
|
2772
2972
|
// when we're not on the last page. Cheap to evaluate
|
|
@@ -3139,7 +3339,12 @@ const makeStyles = (c) => react_native_1.StyleSheet.create({
|
|
|
3139
3339
|
fontWeight: '700',
|
|
3140
3340
|
},
|
|
3141
3341
|
thankYou: {
|
|
3142
|
-
|
|
3342
|
+
// NO PADDING OF ITS OWN. The card already pads its content; a second inset
|
|
3343
|
+
// here made the confirmation's box narrower and shorter than the card's,
|
|
3344
|
+
// so the same picture at the same Width and Height drew smaller on the
|
|
3345
|
+
// second screen with no control to explain it. The components space
|
|
3346
|
+
// themselves with their own margins, exactly as they do on the card.
|
|
3347
|
+
paddingVertical: 8,
|
|
3143
3348
|
alignItems: 'center',
|
|
3144
3349
|
justifyContent: 'center',
|
|
3145
3350
|
},
|
|
@@ -3149,8 +3354,9 @@ const makeStyles = (c) => react_native_1.StyleSheet.create({
|
|
|
3149
3354
|
marginBottom: 16,
|
|
3150
3355
|
},
|
|
3151
3356
|
thankYouImage: {
|
|
3152
|
-
|
|
3153
|
-
|
|
3357
|
+
// FULL WIDTH, and the height comes from the contract at the render site.
|
|
3358
|
+
// 72x72 was a glyph's footprint, not a picture's.
|
|
3359
|
+
width: '100%',
|
|
3154
3360
|
marginBottom: 16,
|
|
3155
3361
|
},
|
|
3156
3362
|
thankYouTitle: {
|
|
@@ -3161,6 +3367,15 @@ const makeStyles = (c) => react_native_1.StyleSheet.create({
|
|
|
3161
3367
|
lineHeight: 26,
|
|
3162
3368
|
},
|
|
3163
3369
|
thankYouSub: {
|
|
3370
|
+
// A COLOUR OF ITS OWN, because the override beside it is LIGHT-ONLY.
|
|
3371
|
+
//
|
|
3372
|
+
// themeTextSecondary is `isLight ? {...} : undefined`, on the assumption
|
|
3373
|
+
// that every style it decorates already carries the DARK colour. This one
|
|
3374
|
+
// carried none, so on a dark card it fell through to React Native's
|
|
3375
|
+
// default Text colour — black — and the confirmation's sub-line was dark
|
|
3376
|
+
// text on a dark background. The title above it was fine because it had
|
|
3377
|
+
// `c.headingColor`; that difference is the whole bug.
|
|
3378
|
+
color: c.textSecondary,
|
|
3164
3379
|
fontSize: 14,
|
|
3165
3380
|
textAlign: 'center',
|
|
3166
3381
|
lineHeight: 20,
|
|
@@ -3170,6 +3385,8 @@ const makeStyles = (c) => react_native_1.StyleSheet.create({
|
|
|
3170
3385
|
// button here because RN's clipboard is an optional peer, and a code you can
|
|
3171
3386
|
// select is better than a button that may not exist.
|
|
3172
3387
|
rewardCode: {
|
|
3388
|
+
// Same fault, and worse: this is the discount code itself.
|
|
3389
|
+
color: c.textPrimary,
|
|
3173
3390
|
fontSize: 22,
|
|
3174
3391
|
fontWeight: '700',
|
|
3175
3392
|
letterSpacing: 1.5,
|