@tribe-nest/forge 3.26.0 → 3.31.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/package.json +6 -3
- package/src/_tests/publishedResolvability.spec.ts +184 -0
- package/src/_tests/specsRunWorkspaceSource.spec.ts +116 -0
- package/src/_tests/workspaceAliases.ts +40 -0
- package/src/client/_tests/localeHeader.spec.tsx +98 -0
- package/src/client/activeLocale.ts +34 -0
- package/src/client/createForgeClient.ts +13 -0
- package/src/contexts/CartContext.tsx +76 -0
- package/src/contexts/PublicAuthContext.tsx +34 -5
- package/src/contexts/_tests/CartContext.spec.tsx +210 -0
- package/src/contexts/_tests/PublicAuthRefetch.spec.tsx +147 -0
- package/src/data/queries/useBroadcasts.ts +151 -0
- package/src/data/queries/useCheckouts.ts +83 -0
- package/src/data/queries/useMyBookings.ts +9 -1
- package/src/data/queries/useWebsite.ts +7 -0
- package/src/i18n/CONVENTION.md +184 -0
- package/src/i18n/_tests/translationKeys.spec.ts +153 -0
- package/src/i18n/de.json +1019 -0
- package/src/i18n/en.json +1019 -0
- package/src/i18n/index.ts +125 -0
- package/src/index.ts +21 -0
- package/src/provider/ForgeAppProvider.tsx +2 -1
- package/src/provider/SiteConfigProvider.tsx +11 -0
- package/src/ui/format/_tests/membershipPwyw.spec.ts +185 -0
- package/src/ui/format/_tests/pwyw.spec.ts +65 -8
- package/src/ui/format/membershipPwyw.ts +164 -0
- package/src/ui/format/pwyw.ts +37 -0
- package/src/ui/headless/broadcast/_tests/broadcastState.spec.ts +235 -0
- package/src/ui/headless/broadcast/broadcastState.ts +158 -0
- package/src/ui/headless/broadcast/useBroadcastWatch.ts +174 -21
- package/src/ui/headless/checkout/_tests/cartResume.spec.ts +291 -0
- package/src/ui/headless/checkout/_tests/useCartResume.spec.tsx +294 -0
- package/src/ui/headless/checkout/resumedCoupon.ts +61 -0
- package/src/ui/headless/checkout/useCartResume.ts +340 -0
- package/src/ui/headless/checkout/useCheckout.ts +9 -2
- package/src/ui/headless/event/useEventCheckout.ts +8 -13
- package/src/ui/headless/index.ts +32 -0
- package/src/ui/headless/membership/useMembershipCheckout.ts +160 -32
- package/src/ui/index.ts +38 -0
- package/src/ui/media/CallHelpHint.tsx +87 -0
- package/src/ui/media/CallStage.tsx +542 -0
- package/src/ui/media/_tests/CallStage.spec.tsx +685 -0
- package/src/ui/media/_tests/bookingSession.spec.tsx +179 -0
- package/src/ui/media/_tests/callState.spec.ts +452 -0
- package/src/ui/media/_tests/fakeNode.ts +178 -0
- package/src/ui/media/bookingSession.tsx +194 -0
- package/src/ui/media/callState.ts +341 -0
- package/src/ui/media/index.ts +135 -0
- package/src/ui/shell/TribeNestApp.tsx +36 -27
- package/src/ui/styled/AccountDashboard.tsx +289 -96
- package/src/ui/styled/AiAgentWidget.tsx +10 -6
- package/src/ui/styled/AudioPlayer.tsx +14 -6
- package/src/ui/styled/BlogCategory.tsx +4 -2
- package/src/ui/styled/BlogComments.tsx +27 -20
- package/src/ui/styled/BlogList.tsx +5 -3
- package/src/ui/styled/BlogPost.tsx +14 -11
- package/src/ui/styled/BroadcastWatch.tsx +107 -0
- package/src/ui/styled/BundleConfirmation.tsx +32 -23
- package/src/ui/styled/CancellationTerms.tsx +3 -1
- package/src/ui/styled/Cart.tsx +32 -14
- package/src/ui/styled/ChatRoom.tsx +65 -20
- package/src/ui/styled/Checkout.tsx +151 -89
- package/src/ui/styled/CheckoutConfirmation.tsx +66 -35
- package/src/ui/styled/CoachingBooking.tsx +71 -44
- package/src/ui/styled/CoachingConfirmation.tsx +56 -19
- package/src/ui/styled/CoachingDetail.tsx +15 -11
- package/src/ui/styled/CohortPage.tsx +10 -8
- package/src/ui/styled/ConfirmSubscription.tsx +14 -8
- package/src/ui/styled/ContactForm.tsx +31 -12
- package/src/ui/styled/CookieConsent.tsx +19 -15
- package/src/ui/styled/CourseAccess.tsx +54 -47
- package/src/ui/styled/CourseCheckout.tsx +56 -38
- package/src/ui/styled/CourseConfirmation.tsx +39 -17
- package/src/ui/styled/CourseDetail.tsx +21 -15
- package/src/ui/styled/CoursesGrid.tsx +4 -2
- package/src/ui/styled/CurrencySwitcher.tsx +4 -1
- package/src/ui/styled/DiscountCode.tsx +22 -18
- package/src/ui/styled/DonationButton.tsx +52 -17
- package/src/ui/styled/DonationPage.tsx +4 -4
- package/src/ui/styled/EmailListForm.tsx +22 -9
- package/src/ui/styled/EventConfirmation.tsx +37 -14
- package/src/ui/styled/EventCountdown.tsx +10 -6
- package/src/ui/styled/EventDetail.tsx +8 -6
- package/src/ui/styled/EventSeriesDetail.tsx +18 -13
- package/src/ui/styled/EventTickets.tsx +232 -108
- package/src/ui/styled/EventWaitlist.tsx +42 -33
- package/src/ui/styled/EventsList.tsx +15 -13
- package/src/ui/styled/ForgotPasswordForm.tsx +17 -12
- package/src/ui/styled/FormRenderer.tsx +12 -7
- package/src/ui/styled/InstallBanner.tsx +21 -18
- package/src/ui/styled/InvoiceConfirmation.tsx +13 -5
- package/src/ui/styled/InvoicePayment.tsx +37 -16
- package/src/ui/styled/LeadMagnet.tsx +9 -7
- package/src/ui/styled/LiveBroadcastList.tsx +171 -0
- package/src/ui/styled/Loading.tsx +7 -1
- package/src/ui/styled/LoginForm.tsx +32 -14
- package/src/ui/styled/MembershipCheckout.tsx +341 -49
- package/src/ui/styled/MembershipTiers.tsx +34 -18
- package/src/ui/styled/OfferButton.tsx +33 -13
- package/src/ui/styled/PaymentLinkConfirmation.tsx +11 -5
- package/src/ui/styled/PaymentLinkPayment.tsx +30 -10
- package/src/ui/styled/PodcastEpisode.tsx +20 -9
- package/src/ui/styled/PodcastList.tsx +9 -3
- package/src/ui/styled/PodcastShow.tsx +29 -7
- package/src/ui/styled/PostsFeed.tsx +10 -8
- package/src/ui/styled/PresaleCode.tsx +8 -11
- package/src/ui/styled/ProductBrowseNav.tsx +21 -11
- package/src/ui/styled/ProductDetail.tsx +58 -28
- package/src/ui/styled/ReactionBar.tsx +4 -2
- package/src/ui/styled/ReplayList.tsx +24 -13
- package/src/ui/styled/ResetPasswordForm.tsx +32 -15
- package/src/ui/styled/ResumeCart.tsx +289 -0
- package/src/ui/styled/ReviewForm.tsx +52 -58
- package/src/ui/styled/SignupForm.tsx +34 -16
- package/src/ui/styled/TicketTransfer.tsx +55 -54
- package/src/ui/styled/UserMenu.tsx +16 -14
- package/src/ui/styled/_tests/AccountDashboardCommunity.spec.tsx +134 -0
- package/src/ui/styled/_tests/BroadcastPassValidation.spec.tsx +125 -0
- package/src/ui/styled/_tests/MembershipCheckout.spec.tsx +364 -0
- package/src/ui/styled/_tests/ResumeCart.spec.tsx +183 -0
- package/src/ui/styled/broadcast/BroadcastPassValidation.tsx +187 -0
- package/src/ui/styled/broadcast/BroadcastPlayer.tsx +536 -0
- package/src/ui/styled/broadcast/BroadcastTicketPurchase.tsx +74 -0
- package/src/ui/styled/broadcast/EndedBroadcast.tsx +103 -0
- package/src/ui/styled/community/CommunityComposer.tsx +182 -3
- package/src/ui/styled/community/CommunityFeed.tsx +36 -51
- package/src/ui/styled/community/CommunityPostDetail.tsx +16 -2
- package/src/ui/styled/community/_tests/CommunityComposer.spec.tsx +281 -0
- package/src/ui/styled/community/_tests/CommunityPostDetail.spec.tsx +175 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { useState, type CSSProperties, type ReactNode } from "react";
|
|
2
|
+
import type { ILiveBroadcast } from "../../../types/models";
|
|
3
|
+
import { useForgeT } from "../../../i18n";
|
|
4
|
+
import { useForgeTheme, type ForgeTheme } from "../../theme/ForgeThemeProvider";
|
|
5
|
+
import { buttonStyle } from "../Button";
|
|
6
|
+
|
|
7
|
+
/** Append an alpha to a 6-digit hex color (mirrors frontend-shared addAlphaToHexCode). */
|
|
8
|
+
const a = (hex: string, alpha: number) => hex + Math.round(alpha * 255).toString(16).padStart(2, "0");
|
|
9
|
+
|
|
10
|
+
const inputStyle = (t: ForgeTheme): CSSProperties => ({
|
|
11
|
+
width: "100%",
|
|
12
|
+
padding: 10,
|
|
13
|
+
border: `1px solid ${a(t.colors.primary, 0.25)}`,
|
|
14
|
+
borderRadius: t.cornerRadius,
|
|
15
|
+
background: t.colors.background,
|
|
16
|
+
color: t.colors.text,
|
|
17
|
+
fontFamily: t.fontFamily,
|
|
18
|
+
boxSizing: "border-box",
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export interface BroadcastPassValidationProps {
|
|
22
|
+
broadcast: ILiveBroadcast;
|
|
23
|
+
/** Present a ticket code. Rejection comes back through `error`, not a throw the caller sees. */
|
|
24
|
+
onValidate: (ticketCode: string) => void | Promise<unknown>;
|
|
25
|
+
/** True while the code is in flight. */
|
|
26
|
+
isValidating?: boolean;
|
|
27
|
+
/** Why the last code was refused. Stays visible: it is a validation error. */
|
|
28
|
+
error?: string | null;
|
|
29
|
+
/**
|
|
30
|
+
* The buy-a-ticket panel, supplied by the caller so this component stays free
|
|
31
|
+
* of data hooks and can be rendered (and asserted on) without a provider tree.
|
|
32
|
+
*/
|
|
33
|
+
buyTickets?: ReactNode;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* THE PAYWALL. A broadcast with a linked event does not play a frame until a
|
|
38
|
+
* viewer has presented a ticket code that the server accepted.
|
|
39
|
+
*
|
|
40
|
+
* Everything a viewer can see before that point is deliberately worthless: the
|
|
41
|
+
* title and the thumbnail, which are already public on the list page. The video
|
|
42
|
+
* element is not mounted, the playback URL is not in the markup, and the chat
|
|
43
|
+
* socket is not opened, because the component that does all three is not
|
|
44
|
+
* rendered until `useBroadcastWatch` reports a validated pass.
|
|
45
|
+
*
|
|
46
|
+
* The refusal message says only that the code was not accepted. It never says
|
|
47
|
+
* which event a code IS valid for, which would turn this box into a way to test
|
|
48
|
+
* ticket ids against every broadcast on the site.
|
|
49
|
+
*/
|
|
50
|
+
export function BroadcastPassValidation({
|
|
51
|
+
broadcast,
|
|
52
|
+
onValidate,
|
|
53
|
+
isValidating,
|
|
54
|
+
error,
|
|
55
|
+
buyTickets,
|
|
56
|
+
}: BroadcastPassValidationProps) {
|
|
57
|
+
const t = useForgeT();
|
|
58
|
+
const theme = useForgeTheme();
|
|
59
|
+
const [ticketCode, setTicketCode] = useState("");
|
|
60
|
+
const thumb = broadcast.thumbnailUrl || broadcast.generatedThumbnailUrl;
|
|
61
|
+
|
|
62
|
+
// Trimmed, because a ticket code is nearly always pasted and a pasted code
|
|
63
|
+
// nearly always brings a trailing space with it.
|
|
64
|
+
const trimmed = ticketCode.trim();
|
|
65
|
+
const canSubmit = !!trimmed && !isValidating;
|
|
66
|
+
|
|
67
|
+
const submit = () => {
|
|
68
|
+
if (!canSubmit || !broadcast.id) return;
|
|
69
|
+
void onValidate(trimmed);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<div
|
|
74
|
+
data-testid="broadcast-pass-validation"
|
|
75
|
+
style={{
|
|
76
|
+
display: "flex",
|
|
77
|
+
flexDirection: "column",
|
|
78
|
+
gap: 16,
|
|
79
|
+
alignItems: "center",
|
|
80
|
+
padding: "0 16px",
|
|
81
|
+
color: theme.colors.text,
|
|
82
|
+
fontFamily: theme.fontFamily,
|
|
83
|
+
}}
|
|
84
|
+
>
|
|
85
|
+
<div
|
|
86
|
+
style={{
|
|
87
|
+
marginTop: 16,
|
|
88
|
+
maxWidth: 500,
|
|
89
|
+
width: "100%",
|
|
90
|
+
marginInline: "auto",
|
|
91
|
+
backgroundColor: theme.colors.background,
|
|
92
|
+
borderRadius: theme.cornerRadius,
|
|
93
|
+
boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
|
|
94
|
+
border: `1px solid ${theme.colors.primary}30`,
|
|
95
|
+
overflow: "hidden",
|
|
96
|
+
}}
|
|
97
|
+
>
|
|
98
|
+
<div
|
|
99
|
+
style={{
|
|
100
|
+
aspectRatio: "16/9",
|
|
101
|
+
position: "relative",
|
|
102
|
+
display: "flex",
|
|
103
|
+
alignItems: "center",
|
|
104
|
+
justifyContent: "center",
|
|
105
|
+
background: `linear-gradient(135deg, ${theme.colors.primary}20, ${theme.colors.primary}10)`,
|
|
106
|
+
}}
|
|
107
|
+
>
|
|
108
|
+
{thumb && (
|
|
109
|
+
<img
|
|
110
|
+
src={thumb}
|
|
111
|
+
alt={broadcast.title}
|
|
112
|
+
style={{ width: "100%", height: "100%", objectFit: "cover", position: "absolute", top: 0, left: 0 }}
|
|
113
|
+
/>
|
|
114
|
+
)}
|
|
115
|
+
</div>
|
|
116
|
+
<div style={{ padding: 16 }}>
|
|
117
|
+
<h3 style={{ fontWeight: 600 }}>{broadcast.title}</h3>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<div
|
|
122
|
+
style={{
|
|
123
|
+
marginTop: 16,
|
|
124
|
+
maxWidth: 500,
|
|
125
|
+
width: "100%",
|
|
126
|
+
marginInline: "auto",
|
|
127
|
+
display: "flex",
|
|
128
|
+
flexDirection: "column",
|
|
129
|
+
gap: 16,
|
|
130
|
+
alignItems: "center",
|
|
131
|
+
}}
|
|
132
|
+
>
|
|
133
|
+
<label htmlFor="broadcast-ticket-code" style={{ textAlign: "center" }}>
|
|
134
|
+
{t("forge.broadcast_pass_validation.prompt")}
|
|
135
|
+
</label>
|
|
136
|
+
<input
|
|
137
|
+
id="broadcast-ticket-code"
|
|
138
|
+
data-testid="broadcast-ticket-code"
|
|
139
|
+
placeholder={t("forge.broadcast_pass_validation.input_placeholder")}
|
|
140
|
+
value={ticketCode}
|
|
141
|
+
onChange={(e) => setTicketCode(e.target.value)}
|
|
142
|
+
onKeyDown={(e) => {
|
|
143
|
+
if (e.key !== "Enter") return;
|
|
144
|
+
e.preventDefault();
|
|
145
|
+
submit();
|
|
146
|
+
}}
|
|
147
|
+
aria-invalid={error ? true : undefined}
|
|
148
|
+
aria-describedby={error ? "broadcast-ticket-code-error" : undefined}
|
|
149
|
+
style={inputStyle(theme)}
|
|
150
|
+
/>
|
|
151
|
+
{error && (
|
|
152
|
+
<p
|
|
153
|
+
id="broadcast-ticket-code-error"
|
|
154
|
+
data-testid="broadcast-pass-error"
|
|
155
|
+
role="alert"
|
|
156
|
+
// Refusal is the one place a non-theme colour is right: red is the
|
|
157
|
+
// convention for "this input is wrong", and the artist's primary
|
|
158
|
+
// could be any hue including a green.
|
|
159
|
+
style={{ color: "#ef4444", fontSize: 13, margin: 0, alignSelf: "flex-start" }}
|
|
160
|
+
>
|
|
161
|
+
{error}
|
|
162
|
+
</p>
|
|
163
|
+
)}
|
|
164
|
+
<button
|
|
165
|
+
type="button"
|
|
166
|
+
data-testid="broadcast-pass-submit"
|
|
167
|
+
onClick={submit}
|
|
168
|
+
disabled={!canSubmit}
|
|
169
|
+
style={buttonStyle(theme, { fullWidth: true, disabled: !canSubmit })}
|
|
170
|
+
>
|
|
171
|
+
{isValidating
|
|
172
|
+
? t("forge.broadcast_pass_validation.validating")
|
|
173
|
+
: t("forge.broadcast_pass_validation.join")}
|
|
174
|
+
</button>
|
|
175
|
+
|
|
176
|
+
{buyTickets && (
|
|
177
|
+
<>
|
|
178
|
+
<p style={{ marginBlock: 16 }}>{t("forge.broadcast_pass_validation.or")}</p>
|
|
179
|
+
{buyTickets}
|
|
180
|
+
</>
|
|
181
|
+
)}
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export default BroadcastPassValidation;
|