@tribe-nest/forge 3.34.0 → 3.36.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.
Files changed (40) hide show
  1. package/package.json +3 -2
  2. package/src/client/_tests/tenantHeaders.spec.ts +77 -0
  3. package/src/client/createForgeClient.ts +37 -0
  4. package/src/data/queries/_tests/paymentFlowReturnUrl.spec.tsx +165 -0
  5. package/src/data/queries/useFilmPlaybackSession.ts +224 -0
  6. package/src/data/queries/useFilms.ts +561 -0
  7. package/src/data/queries/usePaymentFlow.ts +28 -3
  8. package/src/i18n/de.json +130 -28
  9. package/src/i18n/en.json +130 -28
  10. package/src/index.ts +5 -0
  11. package/src/provider/ForgeAppProvider.tsx +10 -0
  12. package/src/provider/ForgeProvider.tsx +18 -3
  13. package/src/server/index.ts +76 -17
  14. package/src/ui/headless/_tests/dialogPaystackStandDown.spec.tsx +185 -0
  15. package/src/ui/headless/dialog.tsx +139 -4
  16. package/src/ui/headless/film/FilmWatermark.tsx +180 -0
  17. package/src/ui/headless/film/_tests/filmRules.spec.ts +531 -0
  18. package/src/ui/headless/film/_tests/useStageFullscreen.spec.ts +167 -0
  19. package/src/ui/headless/film/index.ts +36 -0
  20. package/src/ui/headless/film/useFilmCatalog.ts +66 -0
  21. package/src/ui/headless/film/useFilmPlayback.ts +497 -0
  22. package/src/ui/headless/film/useFilmRentalFlow.ts +277 -0
  23. package/src/ui/headless/film/useStageFullscreen.ts +156 -0
  24. package/src/ui/headless/index.ts +6 -9
  25. package/src/ui/index.ts +21 -16
  26. package/src/ui/media/CallStage.tsx +59 -1
  27. package/src/ui/media/CallWindowNotice.tsx +94 -0
  28. package/src/ui/media/_tests/CallWindowNotice.spec.tsx +83 -0
  29. package/src/ui/media/index.ts +9 -0
  30. package/src/ui/styled/AccountDashboard.tsx +101 -37
  31. package/src/ui/styled/FilmCatalog.tsx +278 -0
  32. package/src/ui/styled/FilmDetail.tsx +661 -0
  33. package/src/ui/styled/FilmLibrary.tsx +291 -0
  34. package/src/ui/styled/FilmWatch.tsx +701 -0
  35. package/src/ui/styled/_tests/AccountDashboardBookingCall.spec.tsx +28 -0
  36. package/src/ui/styled/_tests/AccountDashboardRentals.spec.tsx +200 -0
  37. package/src/ui/styled/forge-utilities.css +6 -0
  38. package/src/utils/_tests/paystackCheckout.spec.ts +82 -1
  39. package/src/utils/paystackCheckout.ts +47 -0
  40. package/src/utils/structuredData.ts +88 -17
package/src/i18n/de.json CHANGED
@@ -4,10 +4,10 @@
4
4
  "forge.account_dashboard.booking_cancel_error": "Wir konnten diese Session nicht stornieren. Bitte versuche es erneut.",
5
5
  "forge.account_dashboard.booking_duration": "{minutes} Minuten",
6
6
  "forge.account_dashboard.booking_join_label": "Teilnehmen:",
7
- "forge.account_dashboard.booking_video_call": "Videoanruf auf dieser Website",
8
7
  "forge.account_dashboard.booking_join_video": "Videoanruf beitreten",
9
- "forge.account_dashboard.booking_video_opens_at": "Videoanruf: Der Raum öffnet {datetime}",
8
+ "forge.account_dashboard.booking_video_call": "Videoanruf auf dieser Website",
10
9
  "forge.account_dashboard.booking_video_closed": "Videoanruf: Der Raum dieser Session ist geschlossen",
10
+ "forge.account_dashboard.booking_video_opens_at": "Videoanruf: Der Raum öffnet {datetime}",
11
11
  "forge.account_dashboard.booking_where_label": "Wo:",
12
12
  "forge.account_dashboard.cancel": "Abbrechen",
13
13
  "forge.account_dashboard.cancel_confirm_confirm": "Mitgliedschaft kündigen",
@@ -62,6 +62,7 @@
62
62
  "forge.account_dashboard.profile_section_title": "Profilangaben",
63
63
  "forge.account_dashboard.profile_update_error": "Profil konnte nicht aktualisiert werden.",
64
64
  "forge.account_dashboard.profile_update_success": "Profil aktualisiert.",
65
+ "forge.account_dashboard.rentals_title": "Meine Ausleihen",
65
66
  "forge.account_dashboard.request_deletion": "Kontolöschung beantragen",
66
67
  "forge.account_dashboard.reschedule": "Verschieben",
67
68
  "forge.account_dashboard.reschedule_empty": "In den nächsten zwei Wochen sind keine anderen Zeiten frei.",
@@ -80,6 +81,7 @@
80
81
  "forge.account_dashboard.tab_membership": "Mitgliedschaft",
81
82
  "forge.account_dashboard.tab_notifications": "Mitteilungen",
82
83
  "forge.account_dashboard.tab_orders": "Bestellungen",
84
+ "forge.account_dashboard.tab_rentals": "Ausleihen",
83
85
  "forge.account_dashboard.tab_saved": "Gespeichert",
84
86
  "forge.account_dashboard.tab_tickets": "Tickets",
85
87
  "forge.account_dashboard.tab_waitlist": "Warteliste",
@@ -187,41 +189,45 @@
187
189
  "forge.bundle_confirmation.tickets_emailed": "Tickets per E-Mail an dich unterwegs",
188
190
  "forge.bundle_confirmation.title": "Alles erledigt",
189
191
  "forge.bundle_confirmation.view_orders": "Bestellungen ansehen",
190
- "forge.call_stage.status_ended": "Der Anruf ist beendet",
192
+ "forge.call_stage.camera_start": "Kamera starten",
193
+ "forge.call_stage.camera_stop": "Kamera stoppen",
194
+ "forge.call_stage.head_count": "In diesem Anruf: {count}",
195
+ "forge.call_stage.help_camera_stop": "Dein Bild wird nicht mehr gesendet: Die anderen sehen stattdessen deinen Namen. Der Browser hält die Kamera geöffnet, damit das erneute Starten sofort geht und nicht ein zweites Mal um Erlaubnis fragt. Deshalb kann die Kameraleuchte anbleiben, bis du den Anruf verlässt.",
196
+ "forge.call_stage.help_camera_stop_label": "Was macht „Kamera stoppen“?",
197
+ "forge.call_stage.help_hear_not_see": "Eine Kachel mit Namen, aber ohne Bild bedeutet, dass diese Person ihre Kamera ausgeschaltet hat oder dass der Anruf so voll ist, dass nur die gerade sprechenden Personen in voller Größe übertragen werden. Beides ist normal, und der Ton ist davon nicht betroffen.",
198
+ "forge.call_stage.help_hear_not_see_label": "Warum höre ich jemanden, sehe ihn aber nicht?",
199
+ "forge.call_stage.help_screen": "Was auch immer du im Browser-Dialog auswählst, live an alle im Anruf. Deine Kamera läuft dabei weiter, statt ersetzt zu werden, sodass dich die anderen weiterhin sehen können, während du präsentierst.",
200
+ "forge.call_stage.help_screen_label": "Was wird beim Teilen meines Bildschirms gesendet?",
201
+ "forge.call_stage.leave": "Verlassen",
202
+ "forge.call_stage.listening": "(hört zu)",
203
+ "forge.call_stage.lost_camera": "Deine Kamera wurde ausgeschaltet, als die Verbindung abbrach. Starte sie erneut, um wieder gesehen zu werden.",
204
+ "forge.call_stage.lost_microphone": "Dein Mikrofon wurde ausgeschaltet, als die Verbindung abbrach. Hebe die Stummschaltung auf, um wieder gehört zu werden.",
205
+ "forge.call_stage.lost_screen": "Deine Bildschirmfreigabe wurde beendet, als die Verbindung abbrach. Teile ihn erneut, um fortzufahren.",
206
+ "forge.call_stage.mute": "Stummschalten",
207
+ "forge.call_stage.recording": "Dieser Anruf wird aufgezeichnet",
208
+ "forge.call_stage.retry": "Erneut versuchen",
209
+ "forge.call_stage.screen_start": "Bildschirm teilen",
210
+ "forge.call_stage.screen_stop": "Teilen beenden",
191
211
  "forge.call_stage.status_connected": "Verbunden",
192
212
  "forge.call_stage.status_connecting": "Verbindung zum Anruf wird hergestellt",
213
+ "forge.call_stage.status_ended": "Der Anruf ist beendet",
193
214
  "forge.call_stage.status_left": "Du hast den Anruf verlassen",
194
- "forge.call_stage.status_refused": "Beitritt zu diesem Anruf nicht möglich",
195
- "forge.call_stage.status_moving": "Du wirst auf einen anderen Server verschoben",
196
- "forge.call_stage.status_moving_detail": "Das dauert ein paar Sekunden, und niemand sonst wird getrennt.",
215
+ "forge.call_stage.status_lost": "Verbindung unterbrochen",
216
+ "forge.call_stage.status_lost_detail": "Alle anderen sind weiterhin im Anruf. Versuche es erneut, um wieder beizutreten.",
197
217
  "forge.call_stage.status_move_failed": "Du konntest nicht auf einen anderen Server verschoben werden",
198
218
  "forge.call_stage.status_move_failed_detail": "Niemand sonst wurde getrennt. Versuche es erneut, um wieder beizutreten.",
219
+ "forge.call_stage.status_moving": "Du wirst auf einen anderen Server verschoben",
220
+ "forge.call_stage.status_moving_detail": "Das dauert ein paar Sekunden, und niemand sonst wird getrennt.",
199
221
  "forge.call_stage.status_reconnecting": "Verbindung unterbrochen, wird wiederhergestellt",
200
222
  "forge.call_stage.status_reconnecting_detail": "Alle anderen sind weiterhin im Anruf.",
201
- "forge.call_stage.status_lost": "Verbindung unterbrochen",
202
- "forge.call_stage.status_lost_detail": "Alle anderen sind weiterhin im Anruf. Versuche es erneut, um wieder beizutreten.",
203
- "forge.call_stage.retry": "Erneut versuchen",
204
- "forge.call_stage.recording": "Dieser Anruf wird aufgezeichnet",
205
- "forge.call_stage.head_count": "In diesem Anruf: {count}",
206
- "forge.call_stage.help_hear_not_see_label": "Warum höre ich jemanden, sehe ihn aber nicht?",
207
- "forge.call_stage.help_hear_not_see": "Eine Kachel mit Namen, aber ohne Bild bedeutet, dass diese Person ihre Kamera ausgeschaltet hat oder dass der Anruf so voll ist, dass nur die gerade sprechenden Personen in voller Größe übertragen werden. Beides ist normal, und der Ton ist davon nicht betroffen.",
208
- "forge.call_stage.listening": "(hört zu)",
223
+ "forge.call_stage.status_refused": "Beitritt zu diesem Anruf nicht möglich",
224
+ "forge.call_stage.unmute": "Stummschaltung aufheben",
209
225
  "forge.call_stage.you": "Du",
210
226
  "forge.call_stage.you_not_connected": "Du (noch nicht verbunden)",
211
- "forge.call_stage.mute": "Stummschalten",
212
- "forge.call_stage.unmute": "Stummschaltung aufheben",
213
- "forge.call_stage.camera_start": "Kamera starten",
214
- "forge.call_stage.camera_stop": "Kamera stoppen",
215
- "forge.call_stage.help_camera_stop_label": "Was macht „Kamera stoppen“?",
216
- "forge.call_stage.help_camera_stop": "Dein Bild wird nicht mehr gesendet: Die anderen sehen stattdessen deinen Namen. Der Browser hält die Kamera geöffnet, damit das erneute Starten sofort geht und nicht ein zweites Mal um Erlaubnis fragt. Deshalb kann die Kameraleuchte anbleiben, bis du den Anruf verlässt.",
217
- "forge.call_stage.screen_start": "Bildschirm teilen",
218
- "forge.call_stage.screen_stop": "Teilen beenden",
219
- "forge.call_stage.help_screen_label": "Was wird beim Teilen meines Bildschirms gesendet?",
220
- "forge.call_stage.help_screen": "Was auch immer du im Browser-Dialog auswählst, live an alle im Anruf. Deine Kamera läuft dabei weiter, statt ersetzt zu werden, sodass dich die anderen weiterhin sehen können, während du präsentierst.",
221
- "forge.call_stage.leave": "Verlassen",
222
- "forge.call_stage.lost_microphone": "Dein Mikrofon wurde ausgeschaltet, als die Verbindung abbrach. Hebe die Stummschaltung auf, um wieder gehört zu werden.",
223
- "forge.call_stage.lost_camera": "Deine Kamera wurde ausgeschaltet, als die Verbindung abbrach. Starte sie erneut, um wieder gesehen zu werden.",
224
- "forge.call_stage.lost_screen": "Deine Bildschirmfreigabe wurde beendet, als die Verbindung abbrach. Teile ihn erneut, um fortzufahren.",
227
+ "forge.call_window.ended": "Dieser Anruf ist beendet",
228
+ "forge.call_window.ended_detail": "Der Raum für diese Session ist geschlossen. Wenn das nicht stimmt, wenden Sie sich an die Person, bei der Sie gebucht haben.",
229
+ "forge.call_window.not_started": "Dieser Anruf hat noch nicht begonnen",
230
+ "forge.call_window.opens_at": "Sie können ab {datetime} beitreten. Kommen Sie dann zurück, und diese Seite lässt Sie hinein.",
225
231
  "forge.cancellation_terms.title": "Stornobedingungen",
226
232
  "forge.cart.aria_close": "Warenkorb schließen",
227
233
  "forge.cart.aria_open": "Warenkorb öffnen",
@@ -728,6 +734,102 @@
728
734
  "forge.event_waitlist.title": "Warteliste",
729
735
  "forge.events_list.empty_title": "Keine kommenden Events.",
730
736
  "forge.events_list.virtual": "Online",
737
+ "forge.film_catalog.all_kinds": "Filme und Serien",
738
+ "forge.film_catalog.badge_film": "Film",
739
+ "forge.film_catalog.badge_series": "Serie",
740
+ "forge.film_catalog.empty": "Hier gibt es noch nichts zu sehen.",
741
+ "forge.film_catalog.episodes_available": "{count} Folgen verfügbar",
742
+ "forge.film_catalog.filter_kind": "Nach Art filtern",
743
+ "forge.film_catalog.free_preview": "Gratis-Folge",
744
+ "forge.film_catalog.kind_films": "Filme",
745
+ "forge.film_catalog.kind_series": "Serien",
746
+ "forge.film_catalog.load_error": "Diese Titel konnten nicht geladen werden. Bitte versuche es erneut.",
747
+ "forge.film_catalog.next": "Weiter",
748
+ "forge.film_catalog.not_enabled": "Leihen ist auf dieser Seite nicht aktiviert.",
749
+ "forge.film_catalog.not_on_sale": "Nicht im Angebot",
750
+ "forge.film_catalog.page_of": "Seite {page} von {total}",
751
+ "forge.film_catalog.previous": "Zurück",
752
+ "forge.film_catalog.rent_from_price": "Leihen ab {price}",
753
+ "forge.film_catalog.rent_price": "Leihen {price}",
754
+ "forge.film_catalog.search_placeholder": "Titel suchen",
755
+ "forge.film_detail.cancel_payment": "Abbrechen",
756
+ "forge.film_detail.choose_window": "Wähle deine Leihdauer",
757
+ "forge.film_detail.clock_help_film": "Die Zeit läuft ab dem ersten Mal, wenn du auf Play drückst, nicht ab dem Kauf. Du kannst heute kaufen und am Wochenende schauen.",
758
+ "forge.film_detail.clock_help_label": "Wann startet meine Leihdauer?",
759
+ "forge.film_detail.clock_help_series": "Eine Leihdauer gilt für die ganze Serie. Sie startet, sobald du eine beliebige Folge zum ersten Mal abspielst, und beginnt bei der nächsten Folge nicht von vorn.",
760
+ "forge.film_detail.confirming_body": "Das dauert ein paar Sekunden. Bitte schließe oder lade diese Seite nicht neu.",
761
+ "forge.film_detail.confirming_title": "Zahlung wird bestätigt",
762
+ "forge.film_detail.episode_unreleased": "Ab {date}",
763
+ "forge.film_detail.episodes": "Folgen",
764
+ "forge.film_detail.free_preview": "Gratis",
765
+ "forge.film_detail.not_found": "Dieser Titel ist nicht verfügbar.",
766
+ "forge.film_detail.not_on_sale": "Dieser Titel ist derzeit nicht im Angebot.",
767
+ "forge.film_detail.rent_film_cta": "Leihen - {price}, {window}",
768
+ "forge.film_detail.rent_series_cta": "Ganze Serie leihen - {price}, {window}",
769
+ "forge.film_detail.rental_expired": "Dein Zeitfenster ist abgelaufen.",
770
+ "forge.film_detail.rental_not_started": "Dein Zeitfenster beginnt, sobald du auf Play drückst.",
771
+ "forge.film_detail.rental_owned": "Du hast das geliehen.",
772
+ "forge.film_detail.rental_remaining_days": "Noch {days} T {hours} Std.",
773
+ "forge.film_detail.rental_remaining_hours": "Noch {hours} Std. {minutes} Min.",
774
+ "forge.film_detail.rental_remaining_minutes": "Noch {minutes} Min.",
775
+ "forge.film_detail.runtime_hm": "{hours} Std. {minutes} Min.",
776
+ "forge.film_detail.runtime_m": "{minutes} Min.",
777
+ "forge.film_detail.season": "Staffel {number}",
778
+ "forge.film_detail.sign_in_action": "Anmelden",
779
+ "forge.film_detail.sign_in_lead": "Zum Leihen brauchst du ein Konto.",
780
+ "forge.film_detail.trailer": "Trailer",
781
+ "forge.film_detail.untitled_episode": "Folge {number}",
782
+ "forge.film_detail.watch": "Ansehen",
783
+ "forge.film_detail.watch_free": "Gratis ansehen",
784
+ "forge.film_detail.watch_now": "Jetzt ansehen",
785
+ "forge.film_detail.window_days": "Leihdauer {days} Tage",
786
+ "forge.film_detail.window_hours": "Leihdauer {hours} Stunden",
787
+ "forge.film_library.browse": "Titel ansehen",
788
+ "forge.film_library.empty": "Du hast noch nichts geliehen.",
789
+ "forge.film_library.heading": "Meine Ausleihen",
790
+ "forge.film_library.rent_again": "Erneut leihen",
791
+ "forge.film_library.resume": "Fortsetzen",
792
+ "forge.film_library.watch": "Ansehen",
793
+ "forge.film_library.window_days_left": "Noch {days} T {hours} Std.",
794
+ "forge.film_library.window_expired": "Dein Zeitfenster ist abgelaufen.",
795
+ "forge.film_library.window_hours_left": "Noch {hours} Std. {minutes} Min.",
796
+ "forge.film_library.window_minutes_left": "Noch {minutes} Min.",
797
+ "forge.film_library.window_not_started": "Noch nicht gestartet. Deine {window} beginnt, sobald du auf Play drückst.",
798
+ "forge.film_library.window_refunded": "Erstattet.",
799
+ "forge.film_watch.clock_started": "Dein Zeitfenster von {hours} Stunden läuft. Es gilt für alle Folgen, bis es abläuft.",
800
+ "forge.film_watch.enter_fullscreen": "Vollbild",
801
+ "forge.film_watch.episode_label": "Staffel {season}, Folge {number}",
802
+ "forge.film_watch.episode_short": "F {number}",
803
+ "forge.film_watch.episodes": "Folgen",
804
+ "forge.film_watch.exit_fullscreen": "Vollbild beenden",
805
+ "forge.film_watch.free": "Gratis",
806
+ "forge.film_watch.gate_expired_action": "Erneut leihen",
807
+ "forge.film_watch.gate_expired_body": "Diese Ausleihe ist beendet. Du kannst sie auf der Titelseite erneut leihen.",
808
+ "forge.film_watch.gate_expired_title": "Dein Zeitfenster ist abgelaufen",
809
+ "forge.film_watch.gate_no_rental_action": "Leihoptionen ansehen",
810
+ "forge.film_watch.gate_no_rental_body": "Nichts, was du besitzt, deckt diese Folge ab. Sie ist möglicherweise auch noch nicht erschienen.",
811
+ "forge.film_watch.gate_no_rental_title": "Du hast das nicht geliehen",
812
+ "forge.film_watch.gate_sign_in_action": "Anmelden",
813
+ "forge.film_watch.gate_sign_in_body": "Ausleihen sind an dein Konto gebunden. Melde dich an, danach landest du direkt wieder hier.",
814
+ "forge.film_watch.gate_sign_in_title": "Zum Ansehen anmelden",
815
+ "forge.film_watch.gate_unavailable_action": "Zurück zum Titel",
816
+ "forge.film_watch.gate_unavailable_body": "Auf dem Weg zum Stream ist etwas schiefgelaufen. Bitte versuche es gleich noch einmal.",
817
+ "forge.film_watch.gate_unavailable_title": "Das kann gerade nicht abgespielt werden",
818
+ "forge.film_watch.gate_wrong_account_action": "Abmelden und Konto wechseln",
819
+ "forge.film_watch.gate_wrong_account_body": "Melde dich ab und wieder mit dem Konto an, das diesen Titel geliehen hat.",
820
+ "forge.film_watch.gate_wrong_account_title": "Diese Ausleihe gehört zu einem anderen Konto",
821
+ "forge.film_watch.play_next": "Nächste Folge abspielen",
822
+ "forge.film_watch.season": "Staffel {number}",
823
+ "forge.film_watch.signed_in_as": "Angemeldet als",
824
+ "forge.film_watch.taken_over_action": "Stattdessen hier ansehen",
825
+ "forge.film_watch.taken_over_body": "Diese Ausleihe wird gerade auf einem anderen Gerät oder Tab angesehen. Nur ein Stream gleichzeitig.",
826
+ "forge.film_watch.taken_over_title": "Läuft woanders",
827
+ "forge.film_watch.time_left_days": "Noch {days} T {hours} Std.",
828
+ "forge.film_watch.time_left_hours": "Noch {hours} Std. {minutes} Min.",
829
+ "forge.film_watch.time_left_minutes": "Noch {minutes} Min.",
830
+ "forge.film_watch.unavailable": "Diese Folge kann gerade nicht abgespielt werden.",
831
+ "forge.film_watch.up_next": "Als Nächstes: {title}",
832
+ "forge.film_watch.window_not_started": "Dein Zeitfenster startet jetzt.",
731
833
  "forge.forgot_password_form.back_to_login": "Zurück zur Anmeldung",
732
834
  "forge.forgot_password_form.email_label": "E-Mail",
733
835
  "forge.forgot_password_form.email_placeholder": "E-Mail",
package/src/i18n/en.json CHANGED
@@ -4,10 +4,10 @@
4
4
  "forge.account_dashboard.booking_cancel_error": "We could not cancel this session. Please try again.",
5
5
  "forge.account_dashboard.booking_duration": "{minutes} minutes",
6
6
  "forge.account_dashboard.booking_join_label": "Join:",
7
- "forge.account_dashboard.booking_video_call": "Video call on this site",
8
7
  "forge.account_dashboard.booking_join_video": "Join video call",
9
- "forge.account_dashboard.booking_video_opens_at": "Video call: the room opens {datetime}",
8
+ "forge.account_dashboard.booking_video_call": "Video call on this site",
10
9
  "forge.account_dashboard.booking_video_closed": "Video call: this session's room has closed",
10
+ "forge.account_dashboard.booking_video_opens_at": "Video call: the room opens {datetime}",
11
11
  "forge.account_dashboard.booking_where_label": "Where:",
12
12
  "forge.account_dashboard.cancel": "Cancel",
13
13
  "forge.account_dashboard.cancel_confirm_confirm": "Cancel membership",
@@ -62,6 +62,7 @@
62
62
  "forge.account_dashboard.profile_section_title": "Profile Information",
63
63
  "forge.account_dashboard.profile_update_error": "Failed to update profile.",
64
64
  "forge.account_dashboard.profile_update_success": "Profile updated.",
65
+ "forge.account_dashboard.rentals_title": "My Rentals",
65
66
  "forge.account_dashboard.request_deletion": "Request Account Deletion",
66
67
  "forge.account_dashboard.reschedule": "Reschedule",
67
68
  "forge.account_dashboard.reschedule_empty": "No other times are open in the next two weeks.",
@@ -80,6 +81,7 @@
80
81
  "forge.account_dashboard.tab_membership": "Membership",
81
82
  "forge.account_dashboard.tab_notifications": "Notifications",
82
83
  "forge.account_dashboard.tab_orders": "Orders",
84
+ "forge.account_dashboard.tab_rentals": "Rentals",
83
85
  "forge.account_dashboard.tab_saved": "Saved",
84
86
  "forge.account_dashboard.tab_tickets": "Tickets",
85
87
  "forge.account_dashboard.tab_waitlist": "Waitlist",
@@ -187,41 +189,45 @@
187
189
  "forge.bundle_confirmation.tickets_emailed": "Tickets emailed to you",
188
190
  "forge.bundle_confirmation.title": "You're all set",
189
191
  "forge.bundle_confirmation.view_orders": "View your orders",
190
- "forge.call_stage.status_ended": "The call has ended",
192
+ "forge.call_stage.camera_start": "Start camera",
193
+ "forge.call_stage.camera_stop": "Stop camera",
194
+ "forge.call_stage.head_count": "In this call: {count}",
195
+ "forge.call_stage.help_camera_stop": "It stops sending your picture: the others see your name instead. The browser keeps the camera open so that starting it again is instant and does not ask for permission a second time, which means its indicator light can stay on until you leave the call.",
196
+ "forge.call_stage.help_camera_stop_label": "What does Stop camera do?",
197
+ "forge.call_stage.help_hear_not_see": "A tile with a name and no picture means that person has their camera off, or the call is busy enough that only the people currently speaking are sent in full. Both are normal, and their audio is unaffected.",
198
+ "forge.call_stage.help_hear_not_see_label": "Why can I hear someone but not see them?",
199
+ "forge.call_stage.help_screen": "Whatever you pick in the browser prompt, live, to everyone in the call. Your camera keeps running alongside it rather than being replaced, so people can still see you while you present.",
200
+ "forge.call_stage.help_screen_label": "What does sharing my screen send?",
201
+ "forge.call_stage.leave": "Leave",
202
+ "forge.call_stage.listening": "(listening)",
203
+ "forge.call_stage.lost_camera": "Your camera was turned off when the connection dropped. Start it again to be seen.",
204
+ "forge.call_stage.lost_microphone": "Your microphone was turned off when the connection dropped. Unmute to be heard again.",
205
+ "forge.call_stage.lost_screen": "Your screen share ended when the connection dropped. Share it again to continue.",
206
+ "forge.call_stage.mute": "Mute",
207
+ "forge.call_stage.recording": "This call is being recorded",
208
+ "forge.call_stage.retry": "Try again",
209
+ "forge.call_stage.screen_start": "Share screen",
210
+ "forge.call_stage.screen_stop": "Stop sharing",
191
211
  "forge.call_stage.status_connected": "Connected",
192
212
  "forge.call_stage.status_connecting": "Connecting to the call",
213
+ "forge.call_stage.status_ended": "The call has ended",
193
214
  "forge.call_stage.status_left": "You have left the call",
194
- "forge.call_stage.status_refused": "Could not join this call",
195
- "forge.call_stage.status_moving": "Moving you to another server",
196
- "forge.call_stage.status_moving_detail": "This takes a few seconds and nobody else is dropped.",
215
+ "forge.call_stage.status_lost": "Connection lost",
216
+ "forge.call_stage.status_lost_detail": "Everyone else is still in the call. Try again to rejoin.",
197
217
  "forge.call_stage.status_move_failed": "Could not move you to another server",
198
218
  "forge.call_stage.status_move_failed_detail": "Nobody else was dropped. Try again to rejoin.",
219
+ "forge.call_stage.status_moving": "Moving you to another server",
220
+ "forge.call_stage.status_moving_detail": "This takes a few seconds and nobody else is dropped.",
199
221
  "forge.call_stage.status_reconnecting": "Connection lost, reconnecting",
200
222
  "forge.call_stage.status_reconnecting_detail": "Everyone else is still in the call.",
201
- "forge.call_stage.status_lost": "Connection lost",
202
- "forge.call_stage.status_lost_detail": "Everyone else is still in the call. Try again to rejoin.",
203
- "forge.call_stage.retry": "Try again",
204
- "forge.call_stage.recording": "This call is being recorded",
205
- "forge.call_stage.head_count": "In this call: {count}",
206
- "forge.call_stage.help_hear_not_see_label": "Why can I hear someone but not see them?",
207
- "forge.call_stage.help_hear_not_see": "A tile with a name and no picture means that person has their camera off, or the call is busy enough that only the people currently speaking are sent in full. Both are normal, and their audio is unaffected.",
208
- "forge.call_stage.listening": "(listening)",
223
+ "forge.call_stage.status_refused": "Could not join this call",
224
+ "forge.call_stage.unmute": "Unmute",
209
225
  "forge.call_stage.you": "You",
210
226
  "forge.call_stage.you_not_connected": "You (not connected yet)",
211
- "forge.call_stage.mute": "Mute",
212
- "forge.call_stage.unmute": "Unmute",
213
- "forge.call_stage.camera_start": "Start camera",
214
- "forge.call_stage.camera_stop": "Stop camera",
215
- "forge.call_stage.help_camera_stop_label": "What does Stop camera do?",
216
- "forge.call_stage.help_camera_stop": "It stops sending your picture: the others see your name instead. The browser keeps the camera open so that starting it again is instant and does not ask for permission a second time, which means its indicator light can stay on until you leave the call.",
217
- "forge.call_stage.screen_start": "Share screen",
218
- "forge.call_stage.screen_stop": "Stop sharing",
219
- "forge.call_stage.help_screen_label": "What does sharing my screen send?",
220
- "forge.call_stage.help_screen": "Whatever you pick in the browser prompt, live, to everyone in the call. Your camera keeps running alongside it rather than being replaced, so people can still see you while you present.",
221
- "forge.call_stage.leave": "Leave",
222
- "forge.call_stage.lost_microphone": "Your microphone was turned off when the connection dropped. Unmute to be heard again.",
223
- "forge.call_stage.lost_camera": "Your camera was turned off when the connection dropped. Start it again to be seen.",
224
- "forge.call_stage.lost_screen": "Your screen share ended when the connection dropped. Share it again to continue.",
227
+ "forge.call_window.ended": "This call has ended",
228
+ "forge.call_window.ended_detail": "The room for this session is closed. If you think that is wrong, contact whoever you booked with.",
229
+ "forge.call_window.not_started": "This call has not started yet",
230
+ "forge.call_window.opens_at": "You can join from {datetime}. Come back then and this page will let you in.",
225
231
  "forge.cancellation_terms.title": "Cancellation policy",
226
232
  "forge.cart.aria_close": "Close cart",
227
233
  "forge.cart.aria_open": "Open cart",
@@ -728,6 +734,102 @@
728
734
  "forge.event_waitlist.title": "Waitlist",
729
735
  "forge.events_list.empty_title": "No upcoming events.",
730
736
  "forge.events_list.virtual": "Virtual",
737
+ "forge.film_catalog.all_kinds": "Films and series",
738
+ "forge.film_catalog.badge_film": "Film",
739
+ "forge.film_catalog.badge_series": "Series",
740
+ "forge.film_catalog.empty": "Nothing to watch here yet.",
741
+ "forge.film_catalog.episodes_available": "{count} episodes available",
742
+ "forge.film_catalog.filter_kind": "Filter by type",
743
+ "forge.film_catalog.free_preview": "Free episode",
744
+ "forge.film_catalog.kind_films": "Films",
745
+ "forge.film_catalog.kind_series": "Series",
746
+ "forge.film_catalog.load_error": "These titles could not be loaded. Please try again.",
747
+ "forge.film_catalog.next": "Next",
748
+ "forge.film_catalog.not_enabled": "Rentals are not switched on for this site.",
749
+ "forge.film_catalog.not_on_sale": "Not on sale",
750
+ "forge.film_catalog.page_of": "Page {page} of {total}",
751
+ "forge.film_catalog.previous": "Previous",
752
+ "forge.film_catalog.rent_from_price": "Rent from {price}",
753
+ "forge.film_catalog.rent_price": "Rent {price}",
754
+ "forge.film_catalog.search_placeholder": "Search titles",
755
+ "forge.film_detail.cancel_payment": "Cancel",
756
+ "forge.film_detail.choose_window": "Choose your rental window",
757
+ "forge.film_detail.clock_help_film": "The clock starts the first time you press play, not when you pay. You can buy today and watch at the weekend.",
758
+ "forge.film_detail.clock_help_label": "When does my rental clock start?",
759
+ "forge.film_detail.clock_help_series": "One clock covers the whole series. It starts the first time you press play on any episode and it does not restart when you move to the next one.",
760
+ "forge.film_detail.confirming_body": "This takes a few seconds. Please do not close or reload this page.",
761
+ "forge.film_detail.confirming_title": "Confirming your payment",
762
+ "forge.film_detail.episode_unreleased": "Out {date}",
763
+ "forge.film_detail.episodes": "Episodes",
764
+ "forge.film_detail.free_preview": "Free",
765
+ "forge.film_detail.not_found": "This title is not available.",
766
+ "forge.film_detail.not_on_sale": "This title is not on sale right now.",
767
+ "forge.film_detail.rent_film_cta": "Rent - {price}, {window}",
768
+ "forge.film_detail.rent_series_cta": "Rent the whole series - {price}, {window}",
769
+ "forge.film_detail.rental_expired": "Your window has closed.",
770
+ "forge.film_detail.rental_not_started": "Your window starts when you press play.",
771
+ "forge.film_detail.rental_owned": "You rented this.",
772
+ "forge.film_detail.rental_remaining_days": "{days}d {hours}h left.",
773
+ "forge.film_detail.rental_remaining_hours": "{hours}h {minutes}m left.",
774
+ "forge.film_detail.rental_remaining_minutes": "{minutes}m left.",
775
+ "forge.film_detail.runtime_hm": "{hours}h {minutes}m",
776
+ "forge.film_detail.runtime_m": "{minutes}m",
777
+ "forge.film_detail.season": "Season {number}",
778
+ "forge.film_detail.sign_in_action": "Sign in",
779
+ "forge.film_detail.sign_in_lead": "Renting needs an account.",
780
+ "forge.film_detail.trailer": "Trailer",
781
+ "forge.film_detail.untitled_episode": "Episode {number}",
782
+ "forge.film_detail.watch": "Watch",
783
+ "forge.film_detail.watch_free": "Watch free",
784
+ "forge.film_detail.watch_now": "Watch now",
785
+ "forge.film_detail.window_days": "{days}-day rental",
786
+ "forge.film_detail.window_hours": "{hours}-hour rental",
787
+ "forge.film_library.browse": "Browse titles",
788
+ "forge.film_library.empty": "You have not rented anything yet.",
789
+ "forge.film_library.heading": "My rentals",
790
+ "forge.film_library.rent_again": "Rent again",
791
+ "forge.film_library.resume": "Resume",
792
+ "forge.film_library.watch": "Watch",
793
+ "forge.film_library.window_days_left": "{days}d {hours}h left",
794
+ "forge.film_library.window_expired": "Your window has closed.",
795
+ "forge.film_library.window_hours_left": "{hours}h {minutes}m left",
796
+ "forge.film_library.window_minutes_left": "{minutes}m left",
797
+ "forge.film_library.window_not_started": "Not started. Your {window} begins when you press play.",
798
+ "forge.film_library.window_refunded": "Refunded.",
799
+ "forge.film_watch.clock_started": "Your {hours}-hour window has started. It covers every episode until it runs out.",
800
+ "forge.film_watch.enter_fullscreen": "Fullscreen",
801
+ "forge.film_watch.episode_label": "Season {season}, episode {number}",
802
+ "forge.film_watch.episode_short": "Ep {number}",
803
+ "forge.film_watch.episodes": "Episodes",
804
+ "forge.film_watch.exit_fullscreen": "Exit fullscreen",
805
+ "forge.film_watch.free": "Free",
806
+ "forge.film_watch.gate_expired_action": "Rent again",
807
+ "forge.film_watch.gate_expired_body": "This rental has run out. You can rent it again from the title page.",
808
+ "forge.film_watch.gate_expired_title": "Your window has closed",
809
+ "forge.film_watch.gate_no_rental_action": "See rental options",
810
+ "forge.film_watch.gate_no_rental_body": "Nothing you hold covers this episode. It may also not have been released yet.",
811
+ "forge.film_watch.gate_no_rental_title": "You have not rented this",
812
+ "forge.film_watch.gate_sign_in_action": "Sign in",
813
+ "forge.film_watch.gate_sign_in_body": "Rentals are tied to your account. Sign in and you will come straight back here.",
814
+ "forge.film_watch.gate_sign_in_title": "Sign in to watch",
815
+ "forge.film_watch.gate_unavailable_action": "Back to the title",
816
+ "forge.film_watch.gate_unavailable_body": "Something went wrong on the way to the stream. Please try again in a moment.",
817
+ "forge.film_watch.gate_unavailable_title": "This cannot be played right now",
818
+ "forge.film_watch.gate_wrong_account_action": "Sign out and switch account",
819
+ "forge.film_watch.gate_wrong_account_body": "Sign out and sign back in with the account that rented this.",
820
+ "forge.film_watch.gate_wrong_account_title": "This rental belongs to a different account",
821
+ "forge.film_watch.play_next": "Play next episode",
822
+ "forge.film_watch.season": "Season {number}",
823
+ "forge.film_watch.signed_in_as": "Signed in as",
824
+ "forge.film_watch.taken_over_action": "Watch here instead",
825
+ "forge.film_watch.taken_over_body": "This rental is being watched on another device or tab. One stream at a time.",
826
+ "forge.film_watch.taken_over_title": "Playing somewhere else",
827
+ "forge.film_watch.time_left_days": "{days}d {hours}h left",
828
+ "forge.film_watch.time_left_hours": "{hours}h {minutes}m left",
829
+ "forge.film_watch.time_left_minutes": "{minutes}m left",
830
+ "forge.film_watch.unavailable": "This episode cannot be played right now.",
831
+ "forge.film_watch.up_next": "Up next: {title}",
832
+ "forge.film_watch.window_not_started": "Your window starts now.",
731
833
  "forge.forgot_password_form.back_to_login": "Back to login",
732
834
  "forge.forgot_password_form.email_label": "Email",
733
835
  "forge.forgot_password_form.email_placeholder": "Email",
package/src/index.ts CHANGED
@@ -110,6 +110,11 @@ export * from "./data/queries/useReplay";
110
110
  export * from "./data/queries/useCoachingAvailability";
111
111
  export * from "./data/queries/useCohortPage";
112
112
  export * from "./data/queries/useCourseAccess";
113
+ // Films and series: the catalogue, one title, the buyer's library, the rental
114
+ // money legs, and the three playback-session calls. Everything a custom viewing
115
+ // experience needs, so nobody has to invent an HTTP call to build one.
116
+ export * from "./data/queries/useFilms";
117
+ export * from "./data/queries/useFilmPlaybackSession";
113
118
  export * from "./data/queries/useBroadcasts";
114
119
  export * from "./data/queries/useAccount";
115
120
  export * from "./data/queries/useMyTickets";
@@ -63,6 +63,14 @@ export interface ForgeProviderProps {
63
63
  apiUrl: string;
64
64
  /** The tenant this deploy is pinned to. */
65
65
  profileId?: string;
66
+ /**
67
+ * WHICH of the tenant's websites this deploy is.
68
+ *
69
+ * A profile can own several, so `profileId` cannot answer "which site am I",
70
+ * and `subdomain` is the profile's primary rather than this deploy's. Rides
71
+ * every request as `x-website-id`.
72
+ */
73
+ websiteId?: string;
66
74
  /** The app this deploy is the frontend for (mini-apps only; baked at build). */
67
75
  appId?: string;
68
76
  /** The tenant subdomain (also used to fetch site config). */
@@ -104,6 +112,7 @@ export interface ForgeProviderProps {
104
112
  export function ForgeProvider({
105
113
  apiUrl,
106
114
  profileId,
115
+ websiteId,
107
116
  appId,
108
117
  subdomain,
109
118
  rootDomain,
@@ -125,6 +134,7 @@ export function ForgeProvider({
125
134
  apiUrl={apiUrl}
126
135
  rootDomain={rootDomain}
127
136
  profileId={profileId}
137
+ websiteId={websiteId}
128
138
  appId={appId}
129
139
  subdomain={subdomain}
130
140
  publishableKey={publishableKey}
@@ -16,6 +16,14 @@ export interface ForgeContextValue {
16
16
  rootDomain: string;
17
17
  /** The tenant this site is pinned to (one deploy = one profileId). */
18
18
  profileId?: string;
19
+ /**
20
+ * WHICH of the tenant's websites this deploy is.
21
+ *
22
+ * A profile can own several, so `profileId` alone cannot answer "which site
23
+ * am I". Anything a site derives about ITSELF - a canonical URL, a share
24
+ * link - needs this rather than the profile's primary subdomain.
25
+ */
26
+ websiteId?: string;
19
27
  /** The app this deploy is the frontend for (mini-apps only; baked at build). */
20
28
  appId?: string;
21
29
  /** The tenant subdomain, when resolved. */
@@ -36,6 +44,7 @@ export interface ForgeClientProviderProps {
36
44
  apiUrl: string;
37
45
  rootDomain?: string;
38
46
  profileId?: string;
47
+ websiteId?: string;
39
48
  appId?: string;
40
49
  subdomain?: string;
41
50
  publishableKey?: string;
@@ -57,6 +66,7 @@ export const ForgeClientProvider = ({
57
66
  apiUrl,
58
67
  rootDomain = "",
59
68
  profileId,
69
+ websiteId,
60
70
  appId,
61
71
  subdomain,
62
72
  publishableKey,
@@ -80,6 +90,8 @@ export const ForgeClientProvider = ({
80
90
  createForgeClient({
81
91
  baseURL: apiUrl,
82
92
  publishableKey,
93
+ websiteId,
94
+ profileId,
83
95
  getToken: () => tokenRef.current,
84
96
  // Fan/member lane (backend §7): 15-minute access tokens, refresh token
85
97
  // rotated per exchange. On a dead chain the stored pair is cleared so
@@ -99,12 +111,15 @@ export const ForgeClientProvider = ({
99
111
  },
100
112
  },
101
113
  }),
102
- [apiUrl, publishableKey],
114
+ // `websiteId` and `profileId` are baked at build and never change at
115
+ // runtime, but they are READ inside the factory, so leaving them out is a
116
+ // stale closure the first time that stops being true.
117
+ [apiUrl, publishableKey, websiteId, profileId],
103
118
  );
104
119
 
105
120
  const value = useMemo<ForgeContextValue>(
106
- () => ({ apiUrl, rootDomain, profileId, appId, subdomain, publishableKey, client, token, setToken }),
107
- [apiUrl, rootDomain, profileId, appId, subdomain, publishableKey, client, token, setToken],
121
+ () => ({ apiUrl, rootDomain, profileId, websiteId, appId, subdomain, publishableKey, client, token, setToken }),
122
+ [apiUrl, rootDomain, profileId, websiteId, appId, subdomain, publishableKey, client, token, setToken],
108
123
  );
109
124
 
110
125
  return <ForgeContext.Provider value={value}>{children}</ForgeContext.Provider>;