@innspel/react-native 0.2.2 → 0.3.1

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/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { MaskRect, InnspelClient, OpenFeedbackOptions, InnspelStorage, SubmissionStatus, SubmissionType } from '@innspel/core';
2
- export { AttachmentInput, AttachmentOutcome, CaptureInput, ContextInput, InnspelClient, InnspelOptions, InnspelStorage, MySubmission, OpenFeedbackOptions, SubmissionStatusView } from '@innspel/core';
1
+ import { MaskRect, InnspelClient, OpenFeedbackOptions, ParticipationView, InnspelStorage, SubmissionStatus, SubmissionType } from '@innspel/core';
2
+ export { AttachmentInput, AttachmentOutcome, CaptureInput, ContextInput, InnspelClient, InnspelOptions, InnspelStorage, InvitationView, MyPrograms, MySubmission, OpenFeedbackOptions, ParticipationView, ProgramTask, ProgramView, SubmissionStatusView } from '@innspel/core';
3
3
  import React, { RefObject, ComponentRef } from 'react';
4
4
  import { View, StyleProp, ViewStyle } from 'react-native';
5
5
 
@@ -364,6 +364,32 @@ declare function InnspelPrivate({ children, style }: {
364
364
  style?: StyleProp<ViewStyle>;
365
365
  }): React.JSX.Element;
366
366
 
367
+ /**
368
+ * Betaprogrammet i vertsappen: 3a banneret, 3b aksepten og deltakelsen med
369
+ * «Gå ut» (DD-36). Sett den der vertsappen vil ha invitasjonen — typisk øverst
370
+ * på hjemskjermen. Uten invitasjon og uten deltakelse rendrer den INGENTING;
371
+ * det er wireframens «tom → fanen skjules helt», ikke en tom tilstand.
372
+ *
373
+ * **Tokenet kommer fra verten** (DD-12). Plattformen sender aldri invitasjonen:
374
+ * leietakeren får `program.invitation_created` med tokenet, løser pseudonymet til
375
+ * sin egen bruker og sender lenken i sin egen kanal. Verten fanger tokenet fra
376
+ * dyplenken og gir det hit. Uten det viser banneret fortsatt invitasjonen — men
377
+ * «Bli med» er inaktiv, med lenken forklart, i stedet for å feile ved trykk.
378
+ *
379
+ * Oppgavene, «Hvordan gikk det?» og oppfølgingene er M3.2.
380
+ *
381
+ * @example
382
+ * ```tsx
383
+ * <InnspelProgram invitationToken={tokenFraDyplenke} />
384
+ * ```
385
+ */
386
+ declare function InnspelProgram({ invitationToken, onEndring, }: {
387
+ /** Engangs-tokenet fra leietakerens egen kanal, typisk fanget fra en dyplenke. */
388
+ invitationToken?: string;
389
+ /** Kalles når testeren blir med eller går ut — for vertens egen navigasjon. */
390
+ onEndring?: (deltakelse: ParticipationView | null) => void;
391
+ }): React.JSX.Element | null;
392
+
367
393
  /**
368
394
  * Formen på lagringspakkene i React Native-økosystemet: AsyncStorage, MMKV med
369
395
  * async-fasade, expo-secure-store. Alle tre har disse tre metodene.
@@ -525,6 +551,37 @@ declare const tekst: {
525
551
  readonly svarFra: (leietaker: string) => string;
526
552
  readonly svarFraGenerisk: "Svar";
527
553
  readonly sendNytt: "Send nytt innspill";
554
+ readonly invitasjonRegion: "Invitasjon til betaprogram";
555
+ readonly invitasjonHva: (navn: string) => string;
556
+ readonly invitasjonKontakter: (maks: number) => string;
557
+ readonly lesMer: "Les mer og bli med";
558
+ readonly avvisInvitasjon: "Avvis invitasjonen";
559
+ readonly akseptHva: "Hva testes";
560
+ /** DD-32: tallene på én linje, FØR forklaringen — N oppgaver · ca. M min hver · dato–dato · maks K kontakter. */
561
+ readonly akseptTall: (oppgaver: number, minutter: number, periode: string, kontakter: number) => string;
562
+ readonly akseptOppgaver: "Hva vi ber om";
563
+ readonly akseptOppgaveTid: (minutter: number) => string;
564
+ readonly akseptKjenteFeil: (antall: number) => string;
565
+ readonly akseptKjenteFeilUnder: "Disse trenger du ikke melde — vi vet om dem.";
566
+ readonly akseptInnsamling: "Hva som samles inn";
567
+ readonly akseptInnsamlingTekst: "Skjerm, appversjon, plattform, OS og hva du svarer. Aldri navn, e-post, telefon eller posisjon.";
568
+ readonly akseptVilkar: "Les vilkårene (åpnes i nettleser)";
569
+ readonly akseptVilkarHint: "Bare en lenke. Ingen avkryssing, ingen signatur — «Bli med» binder deg ikke til noe.";
570
+ readonly akseptIngenVilkar: "Ingen vilkår, ingen avtale — plattformen kjenner ikke navnet ditt og kan ikke inngå én.";
571
+ readonly akseptUtgang: "Du kan gå ut når som helst — herfra.";
572
+ readonly bliMed: "Bli med";
573
+ readonly bliMedHint: "Samtykket gjelder bare dette programmet.";
574
+ /** DD-12: tokenet er engangs og kom i leietakerens egen kanal — plattformen sender aldri. */
575
+ readonly bliMedUtenToken: "Åpne lenken vi sendte deg for å bli med.";
576
+ readonly akseptFeil: (arsak: string) => string;
577
+ readonly deltakelseRegion: "Ditt betaprogram";
578
+ readonly deltakelsePeriode: (periode: string, brukt: number, maks: number) => string;
579
+ readonly deltakelseOppgaverKommer: "Oppgavene kan gjøres når det passer deg.";
580
+ readonly gaaUt: "Gå ut av programmet";
581
+ readonly gaaUtTittel: "Gå ut av programmet?";
582
+ readonly gaaUtTekst: (navn: string) => string;
583
+ readonly bliVaerende: "Bli værende";
584
+ readonly gaaUtBekreft: "Gå ut";
528
585
  readonly tilbake: "Tilbake";
529
586
  readonly lukk: "Lukk";
530
587
  readonly venterPaaNett: "Venter på nett — sendes automatisk";
@@ -548,4 +605,4 @@ declare const lofte: {
548
605
  readonly avvist: "Vi har svart på denne.";
549
606
  };
550
607
 
551
- export { type AsyncStorageLike, type CaptureOptions, type CaptureRefFn, type InnspelColors, InnspelPrivate, InnspelProvider, type InnspelTheme, type PartialTheme, type ScreenshotAdapter, type ScreenshotCapture, type StatusVisning, type Tema, byggTema, createMemoryStorage, createScreenshotAdapter, createStorage, t as darkTokens, light, lofte, screenRules, statusVisning, tekst, useInnspel, useTema, venterVisning };
608
+ export { type AsyncStorageLike, type CaptureOptions, type CaptureRefFn, type InnspelColors, InnspelPrivate, InnspelProgram, InnspelProvider, type InnspelTheme, type PartialTheme, type ScreenshotAdapter, type ScreenshotCapture, type StatusVisning, type Tema, byggTema, createMemoryStorage, createScreenshotAdapter, createStorage, t as darkTokens, light, lofte, screenRules, statusVisning, tekst, useInnspel, useTema, venterVisning };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { MaskRect, InnspelClient, OpenFeedbackOptions, InnspelStorage, SubmissionStatus, SubmissionType } from '@innspel/core';
2
- export { AttachmentInput, AttachmentOutcome, CaptureInput, ContextInput, InnspelClient, InnspelOptions, InnspelStorage, MySubmission, OpenFeedbackOptions, SubmissionStatusView } from '@innspel/core';
1
+ import { MaskRect, InnspelClient, OpenFeedbackOptions, ParticipationView, InnspelStorage, SubmissionStatus, SubmissionType } from '@innspel/core';
2
+ export { AttachmentInput, AttachmentOutcome, CaptureInput, ContextInput, InnspelClient, InnspelOptions, InnspelStorage, InvitationView, MyPrograms, MySubmission, OpenFeedbackOptions, ParticipationView, ProgramTask, ProgramView, SubmissionStatusView } from '@innspel/core';
3
3
  import React, { RefObject, ComponentRef } from 'react';
4
4
  import { View, StyleProp, ViewStyle } from 'react-native';
5
5
 
@@ -364,6 +364,32 @@ declare function InnspelPrivate({ children, style }: {
364
364
  style?: StyleProp<ViewStyle>;
365
365
  }): React.JSX.Element;
366
366
 
367
+ /**
368
+ * Betaprogrammet i vertsappen: 3a banneret, 3b aksepten og deltakelsen med
369
+ * «Gå ut» (DD-36). Sett den der vertsappen vil ha invitasjonen — typisk øverst
370
+ * på hjemskjermen. Uten invitasjon og uten deltakelse rendrer den INGENTING;
371
+ * det er wireframens «tom → fanen skjules helt», ikke en tom tilstand.
372
+ *
373
+ * **Tokenet kommer fra verten** (DD-12). Plattformen sender aldri invitasjonen:
374
+ * leietakeren får `program.invitation_created` med tokenet, løser pseudonymet til
375
+ * sin egen bruker og sender lenken i sin egen kanal. Verten fanger tokenet fra
376
+ * dyplenken og gir det hit. Uten det viser banneret fortsatt invitasjonen — men
377
+ * «Bli med» er inaktiv, med lenken forklart, i stedet for å feile ved trykk.
378
+ *
379
+ * Oppgavene, «Hvordan gikk det?» og oppfølgingene er M3.2.
380
+ *
381
+ * @example
382
+ * ```tsx
383
+ * <InnspelProgram invitationToken={tokenFraDyplenke} />
384
+ * ```
385
+ */
386
+ declare function InnspelProgram({ invitationToken, onEndring, }: {
387
+ /** Engangs-tokenet fra leietakerens egen kanal, typisk fanget fra en dyplenke. */
388
+ invitationToken?: string;
389
+ /** Kalles når testeren blir med eller går ut — for vertens egen navigasjon. */
390
+ onEndring?: (deltakelse: ParticipationView | null) => void;
391
+ }): React.JSX.Element | null;
392
+
367
393
  /**
368
394
  * Formen på lagringspakkene i React Native-økosystemet: AsyncStorage, MMKV med
369
395
  * async-fasade, expo-secure-store. Alle tre har disse tre metodene.
@@ -525,6 +551,37 @@ declare const tekst: {
525
551
  readonly svarFra: (leietaker: string) => string;
526
552
  readonly svarFraGenerisk: "Svar";
527
553
  readonly sendNytt: "Send nytt innspill";
554
+ readonly invitasjonRegion: "Invitasjon til betaprogram";
555
+ readonly invitasjonHva: (navn: string) => string;
556
+ readonly invitasjonKontakter: (maks: number) => string;
557
+ readonly lesMer: "Les mer og bli med";
558
+ readonly avvisInvitasjon: "Avvis invitasjonen";
559
+ readonly akseptHva: "Hva testes";
560
+ /** DD-32: tallene på én linje, FØR forklaringen — N oppgaver · ca. M min hver · dato–dato · maks K kontakter. */
561
+ readonly akseptTall: (oppgaver: number, minutter: number, periode: string, kontakter: number) => string;
562
+ readonly akseptOppgaver: "Hva vi ber om";
563
+ readonly akseptOppgaveTid: (minutter: number) => string;
564
+ readonly akseptKjenteFeil: (antall: number) => string;
565
+ readonly akseptKjenteFeilUnder: "Disse trenger du ikke melde — vi vet om dem.";
566
+ readonly akseptInnsamling: "Hva som samles inn";
567
+ readonly akseptInnsamlingTekst: "Skjerm, appversjon, plattform, OS og hva du svarer. Aldri navn, e-post, telefon eller posisjon.";
568
+ readonly akseptVilkar: "Les vilkårene (åpnes i nettleser)";
569
+ readonly akseptVilkarHint: "Bare en lenke. Ingen avkryssing, ingen signatur — «Bli med» binder deg ikke til noe.";
570
+ readonly akseptIngenVilkar: "Ingen vilkår, ingen avtale — plattformen kjenner ikke navnet ditt og kan ikke inngå én.";
571
+ readonly akseptUtgang: "Du kan gå ut når som helst — herfra.";
572
+ readonly bliMed: "Bli med";
573
+ readonly bliMedHint: "Samtykket gjelder bare dette programmet.";
574
+ /** DD-12: tokenet er engangs og kom i leietakerens egen kanal — plattformen sender aldri. */
575
+ readonly bliMedUtenToken: "Åpne lenken vi sendte deg for å bli med.";
576
+ readonly akseptFeil: (arsak: string) => string;
577
+ readonly deltakelseRegion: "Ditt betaprogram";
578
+ readonly deltakelsePeriode: (periode: string, brukt: number, maks: number) => string;
579
+ readonly deltakelseOppgaverKommer: "Oppgavene kan gjøres når det passer deg.";
580
+ readonly gaaUt: "Gå ut av programmet";
581
+ readonly gaaUtTittel: "Gå ut av programmet?";
582
+ readonly gaaUtTekst: (navn: string) => string;
583
+ readonly bliVaerende: "Bli værende";
584
+ readonly gaaUtBekreft: "Gå ut";
528
585
  readonly tilbake: "Tilbake";
529
586
  readonly lukk: "Lukk";
530
587
  readonly venterPaaNett: "Venter på nett — sendes automatisk";
@@ -548,4 +605,4 @@ declare const lofte: {
548
605
  readonly avvist: "Vi har svart på denne.";
549
606
  };
550
607
 
551
- export { type AsyncStorageLike, type CaptureOptions, type CaptureRefFn, type InnspelColors, InnspelPrivate, InnspelProvider, type InnspelTheme, type PartialTheme, type ScreenshotAdapter, type ScreenshotCapture, type StatusVisning, type Tema, byggTema, createMemoryStorage, createScreenshotAdapter, createStorage, t as darkTokens, light, lofte, screenRules, statusVisning, tekst, useInnspel, useTema, venterVisning };
608
+ export { type AsyncStorageLike, type CaptureOptions, type CaptureRefFn, type InnspelColors, InnspelPrivate, InnspelProgram, InnspelProvider, type InnspelTheme, type PartialTheme, type ScreenshotAdapter, type ScreenshotCapture, type StatusVisning, type Tema, byggTema, createMemoryStorage, createScreenshotAdapter, createStorage, t as darkTokens, light, lofte, screenRules, statusVisning, tekst, useInnspel, useTema, venterVisning };
package/dist/index.js CHANGED
@@ -546,6 +546,40 @@ var tekst = {
546
546
  svarFra: (leietaker) => `Svar fra ${leietaker}`,
547
547
  svarFraGenerisk: "Svar",
548
548
  sendNytt: "Send nytt innspill",
549
+ // 3a — invitasjonsbanneret (DD-12)
550
+ invitasjonRegion: "Invitasjon til betaprogram",
551
+ invitasjonHva: (navn) => `Vil du teste \xAB${navn}\xBB?`,
552
+ invitasjonKontakter: (maks) => `Vi tar kontakt maks ${maks} ganger. Du kan g\xE5 ut n\xE5r som helst.`,
553
+ lesMer: "Les mer og bli med",
554
+ avvisInvitasjon: "Avvis invitasjonen",
555
+ // 3b — aksepten (DD-13, DD-32, DD-37)
556
+ akseptHva: "Hva testes",
557
+ /** DD-32: tallene på én linje, FØR forklaringen — N oppgaver · ca. M min hver · dato–dato · maks K kontakter. */
558
+ akseptTall: (oppgaver, minutter, periode2, kontakter) => `${oppgaver} oppgaver \xB7 ca. ${minutter} min hver \xB7 ${periode2} \xB7 maks ${kontakter} kontakter`,
559
+ akseptOppgaver: "Hva vi ber om",
560
+ akseptOppgaveTid: (minutter) => `ca. ${minutter} min`,
561
+ akseptKjenteFeil: (antall) => `Kjente feil (${antall})`,
562
+ akseptKjenteFeilUnder: "Disse trenger du ikke melde \u2014 vi vet om dem.",
563
+ akseptInnsamling: "Hva som samles inn",
564
+ akseptInnsamlingTekst: "Skjerm, appversjon, plattform, OS og hva du svarer. Aldri navn, e-post, telefon eller posisjon.",
565
+ akseptVilkar: "Les vilk\xE5rene (\xE5pnes i nettleser)",
566
+ akseptVilkarHint: "Bare en lenke. Ingen avkryssing, ingen signatur \u2014 \xABBli med\xBB binder deg ikke til noe.",
567
+ akseptIngenVilkar: "Ingen vilk\xE5r, ingen avtale \u2014 plattformen kjenner ikke navnet ditt og kan ikke inng\xE5 \xE9n.",
568
+ akseptUtgang: "Du kan g\xE5 ut n\xE5r som helst \u2014 herfra.",
569
+ bliMed: "Bli med",
570
+ bliMedHint: "Samtykket gjelder bare dette programmet.",
571
+ /** DD-12: tokenet er engangs og kom i leietakerens egen kanal — plattformen sender aldri. */
572
+ bliMedUtenToken: "\xC5pne lenken vi sendte deg for \xE5 bli med.",
573
+ akseptFeil: (arsak) => `Kunne ikke bli med: ${arsak}`,
574
+ // 3c (M3.1-delen) — deltakelsen og «Gå ut» (DD-36)
575
+ deltakelseRegion: "Ditt betaprogram",
576
+ deltakelsePeriode: (periode2, brukt, maks) => `${periode2} \xB7 ${brukt} av ${maks} kontakter brukt`,
577
+ deltakelseOppgaverKommer: "Oppgavene kan gj\xF8res n\xE5r det passer deg.",
578
+ gaaUt: "G\xE5 ut av programmet",
579
+ gaaUtTittel: "G\xE5 ut av programmet?",
580
+ gaaUtTekst: (navn) => `Du tas ut av testgruppen med en gang. \xAB${navn}\xBB sl\xE5s av for deg, og vi tar ikke kontakt igjen. Det du allerede har svart, beholdes.`,
581
+ bliVaerende: "Bli v\xE6rende",
582
+ gaaUtBekreft: "G\xE5 ut",
549
583
  // Felles
550
584
  tilbake: "Tilbake",
551
585
  lukk: "Lukk",
@@ -1756,6 +1790,309 @@ function InnspelPrivate({ children, style }) {
1756
1790
  return /* @__PURE__ */ jsx10(View10, { ref, collapsable: false, style, testID: "innspel-privat", children });
1757
1791
  }
1758
1792
 
1793
+ // src/program/InnspelProgram.tsx
1794
+ import { InnspelApiError as InnspelApiError2 } from "@innspel/core";
1795
+ import { useCallback as useCallback4, useEffect as useEffect3, useState as useState5 } from "react";
1796
+ import { Modal as Modal3, Pressable as Pressable7, Text as Text10, View as View12 } from "react-native";
1797
+
1798
+ // src/program/Aksept.tsx
1799
+ import "react";
1800
+ import { Linking, Modal as Modal2, Pressable as Pressable6, ScrollView as ScrollView2, Text as Text9, View as View11 } from "react-native";
1801
+
1802
+ // src/program/periode.ts
1803
+ var MANEDER = [
1804
+ "januar",
1805
+ "februar",
1806
+ "mars",
1807
+ "april",
1808
+ "mai",
1809
+ "juni",
1810
+ "juli",
1811
+ "august",
1812
+ "september",
1813
+ "oktober",
1814
+ "november",
1815
+ "desember"
1816
+ ];
1817
+ function periode(opensAt, closesAt) {
1818
+ const fra = new Date(opensAt);
1819
+ const til = new Date(closesAt);
1820
+ if (Number.isNaN(fra.getTime()) || Number.isNaN(til.getTime())) return "";
1821
+ const sammeManed = fra.getMonth() === til.getMonth() && fra.getFullYear() === til.getFullYear();
1822
+ return sammeManed ? `${fra.getDate()}.\u2013${til.getDate()}. ${MANEDER[til.getMonth()]}` : `${fra.getDate()}. ${MANEDER[fra.getMonth()]} \u2013 ${til.getDate()}. ${MANEDER[til.getMonth()]}`;
1823
+ }
1824
+ function minutterPerOppgave(tasks) {
1825
+ if (tasks.length === 0) return 0;
1826
+ return Math.round(tasks.reduce((sum, t2) => sum + t2.estimatedMinutes, 0) / tasks.length);
1827
+ }
1828
+
1829
+ // src/program/Aksept.tsx
1830
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
1831
+ function Aksept({
1832
+ program,
1833
+ harToken,
1834
+ sender,
1835
+ feil,
1836
+ onBliMed,
1837
+ onLukk
1838
+ }) {
1839
+ const tema = useTema();
1840
+ const tall = tekst.akseptTall(
1841
+ program.tasks.length,
1842
+ minutterPerOppgave(program.tasks),
1843
+ periode(program.opensAt, program.closesAt),
1844
+ program.maxContacts
1845
+ );
1846
+ return /* @__PURE__ */ jsx11(Modal2, { visible: true, transparent: true, animationType: "slide", onRequestClose: onLukk, accessibilityViewIsModal: true, children: /* @__PURE__ */ jsx11(View11, { style: { flex: 1, backgroundColor: tema.color.scrim, justifyContent: "flex-end" }, children: /* @__PURE__ */ jsxs10(
1847
+ View11,
1848
+ {
1849
+ testID: "program-aksept",
1850
+ accessibilityViewIsModal: true,
1851
+ style: {
1852
+ backgroundColor: tema.color.bg,
1853
+ borderTopLeftRadius: tema.radius.lg * 2,
1854
+ borderTopRightRadius: tema.radius.lg * 2,
1855
+ padding: tema.screenPadding,
1856
+ gap: tema.space.md,
1857
+ maxHeight: "92%",
1858
+ minHeight: "60%"
1859
+ },
1860
+ children: [
1861
+ /* @__PURE__ */ jsxs10(View11, { style: { flexDirection: "row", justifyContent: "space-between", alignItems: "center" }, children: [
1862
+ /* @__PURE__ */ jsx11(Btn, { variant: "ghost", testID: "aksept-tilbake", ikon: "\u2190", label: tekst.tilbake, onPress: onLukk }),
1863
+ /* @__PURE__ */ jsx11(View11, {})
1864
+ ] }),
1865
+ feil ? /* @__PURE__ */ jsx11(Banner, { live: true, ikon: "\u26A0", tone: "danger", children: tekst.akseptFeil(feil) }) : null,
1866
+ /* @__PURE__ */ jsxs10(ScrollView2, { contentContainerStyle: { gap: tema.space.md }, children: [
1867
+ /* @__PURE__ */ jsx11(H, { sub: tekst.akseptHva, children: program.name }),
1868
+ /* @__PURE__ */ jsx11(
1869
+ Text9,
1870
+ {
1871
+ accessibilityRole: "text",
1872
+ style: {
1873
+ color: tema.color.textPrimary,
1874
+ fontSize: tema.font.body,
1875
+ lineHeight: tema.lineHeight.body,
1876
+ fontWeight: tema.weight.semibold
1877
+ },
1878
+ children: tall
1879
+ }
1880
+ ),
1881
+ /* @__PURE__ */ jsx11(Text9, { style: { color: tema.color.textPrimary, fontSize: tema.font.body, lineHeight: tema.lineHeight.body }, children: program.hypothesis }),
1882
+ /* @__PURE__ */ jsxs10(Card, { children: [
1883
+ /* @__PURE__ */ jsx11(H, { children: tekst.akseptOppgaver }),
1884
+ program.tasks.map((o) => /* @__PURE__ */ jsxs10(View11, { style: { gap: tema.space.xs }, children: [
1885
+ /* @__PURE__ */ jsx11(Text9, { style: { color: tema.color.textPrimary, fontSize: tema.font.body, lineHeight: tema.lineHeight.body }, children: o.title }),
1886
+ /* @__PURE__ */ jsxs10(Hint, { children: [
1887
+ tekst.akseptOppgaveTid(o.estimatedMinutes),
1888
+ o.screenHint ? ` \xB7 ${o.screenHint}` : ""
1889
+ ] })
1890
+ ] }, o.id))
1891
+ ] }),
1892
+ program.knownIssues.length > 0 ? /* @__PURE__ */ jsxs10(Card, { children: [
1893
+ /* @__PURE__ */ jsx11(H, { sub: tekst.akseptKjenteFeilUnder, children: tekst.akseptKjenteFeil(program.knownIssues.length) }),
1894
+ program.knownIssues.map((k) => /* @__PURE__ */ jsxs10(View11, { style: { gap: tema.space.xs }, children: [
1895
+ /* @__PURE__ */ jsx11(Text9, { style: { color: tema.color.textPrimary, fontSize: tema.font.small, lineHeight: tema.lineHeight.small }, children: k.title }),
1896
+ k.workaround ? /* @__PURE__ */ jsxs10(Text9, { style: { color: tema.color.textSecondary, fontSize: tema.font.small, lineHeight: tema.lineHeight.small }, children: [
1897
+ tekst.omvei,
1898
+ " ",
1899
+ k.workaround
1900
+ ] }) : null,
1901
+ k.fixedIn ? /* @__PURE__ */ jsx11(
1902
+ StatusPill,
1903
+ {
1904
+ ikon: k.status === "fikset" ? "\u2713" : "\u{1F527}",
1905
+ etikett: k.status === "fikset" ? tekst.rettetI(k.fixedIn) : tekst.rettesI(k.fixedIn),
1906
+ farge: k.status === "fikset" ? "success" : "info"
1907
+ }
1908
+ ) : null
1909
+ ] }, k.id))
1910
+ ] }) : null,
1911
+ /* @__PURE__ */ jsxs10(Card, { children: [
1912
+ /* @__PURE__ */ jsx11(H, { sub: tekst.akseptInnsamlingTekst, children: tekst.akseptInnsamling }),
1913
+ /* @__PURE__ */ jsx11(Hint, { children: tekst.kontekstAldri })
1914
+ ] }),
1915
+ program.termsUrl && !program.commitmentFree ? /* @__PURE__ */ jsxs10(View11, { style: { gap: tema.space.xs }, children: [
1916
+ /* @__PURE__ */ jsx11(
1917
+ Pressable6,
1918
+ {
1919
+ testID: "vilkar-lenke",
1920
+ accessibilityRole: "link",
1921
+ accessibilityLabel: tekst.akseptVilkar,
1922
+ onPress: () => void Linking.openURL(String(program.termsUrl)),
1923
+ style: { minHeight: tema.touch, justifyContent: "center" },
1924
+ children: /* @__PURE__ */ jsxs10(Text9, { style: { color: tema.color.textPrimary, fontSize: tema.font.body, textDecorationLine: "underline" }, children: [
1925
+ "\u{1F517} ",
1926
+ tekst.akseptVilkar
1927
+ ] })
1928
+ }
1929
+ ),
1930
+ /* @__PURE__ */ jsx11(Hint, { children: tekst.akseptVilkarHint })
1931
+ ] }) : /* @__PURE__ */ jsx11(Hint, { children: tekst.akseptIngenVilkar }),
1932
+ /* @__PURE__ */ jsxs10(Text9, { style: { color: tema.color.textPrimary, fontSize: tema.font.body, lineHeight: tema.lineHeight.body }, children: [
1933
+ "\u{1F6AA} ",
1934
+ tekst.akseptUtgang
1935
+ ] })
1936
+ ] }),
1937
+ /* @__PURE__ */ jsx11(
1938
+ Btn,
1939
+ {
1940
+ testID: "bli-med",
1941
+ label: tekst.bliMed,
1942
+ disabled: !harToken || sender,
1943
+ hint: harToken ? tekst.bliMedHint : tekst.bliMedUtenToken,
1944
+ onPress: onBliMed
1945
+ }
1946
+ )
1947
+ ]
1948
+ }
1949
+ ) }) });
1950
+ }
1951
+
1952
+ // src/program/InnspelProgram.tsx
1953
+ import { Fragment as Fragment2, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
1954
+ function InnspelProgram({
1955
+ invitationToken,
1956
+ onEndring
1957
+ }) {
1958
+ const { client } = useInnspel();
1959
+ const tema = useTema();
1960
+ const [mine, setMine] = useState5();
1961
+ const [deltakelse, setDeltakelse] = useState5();
1962
+ const [viserAksept, setViserAksept] = useState5(false);
1963
+ const [viserGaaUt, setViserGaaUt] = useState5(false);
1964
+ const [avvist, setAvvist] = useState5(false);
1965
+ const [sender, setSender] = useState5(false);
1966
+ const [feil, setFeil] = useState5();
1967
+ useEffect3(() => {
1968
+ if (client.isAnonymous) return;
1969
+ let levende = true;
1970
+ void client.programs.list().then((svar) => {
1971
+ if (!levende) return;
1972
+ setMine(svar);
1973
+ setDeltakelse(svar.participations.find((p) => p.status !== "trukket"));
1974
+ }).catch(() => void 0);
1975
+ return () => {
1976
+ levende = false;
1977
+ };
1978
+ }, [client]);
1979
+ const bliMed = useCallback4(async () => {
1980
+ if (!invitationToken) return;
1981
+ setSender(true);
1982
+ setFeil(void 0);
1983
+ try {
1984
+ const ny = await client.programs.accept(invitationToken);
1985
+ setDeltakelse(ny);
1986
+ setViserAksept(false);
1987
+ onEndring?.(ny);
1988
+ } catch (e) {
1989
+ setFeil(e instanceof InnspelApiError2 ? e.message : "tjeneren svarte ikke.");
1990
+ } finally {
1991
+ setSender(false);
1992
+ }
1993
+ }, [client, invitationToken, onEndring]);
1994
+ const gaaUt = useCallback4(async () => {
1995
+ if (!deltakelse) return;
1996
+ setSender(true);
1997
+ try {
1998
+ await client.programs.withdraw(deltakelse.id);
1999
+ setDeltakelse(void 0);
2000
+ setViserGaaUt(false);
2001
+ onEndring?.(null);
2002
+ } catch (e) {
2003
+ setFeil(e instanceof InnspelApiError2 ? e.message : "tjeneren svarte ikke.");
2004
+ } finally {
2005
+ setSender(false);
2006
+ }
2007
+ }, [client, deltakelse, onEndring]);
2008
+ const invitasjon = deltakelse ? void 0 : mine?.invitations[0];
2009
+ const program = deltakelse?.program ?? invitasjon?.program;
2010
+ if (!program) return null;
2011
+ if (deltakelse) {
2012
+ return /* @__PURE__ */ jsxs11(View12, { testID: "program-deltakelse", accessibilityLabel: tekst.deltakelseRegion, style: { gap: tema.space.sm }, children: [
2013
+ /* @__PURE__ */ jsxs11(Card, { children: [
2014
+ /* @__PURE__ */ jsx12(H, { sub: tekst.deltakelsePeriode(periode(program.opensAt, program.closesAt), deltakelse.contactsSent, program.maxContacts), children: program.name }),
2015
+ /* @__PURE__ */ jsx12(Hint, { children: tekst.deltakelseOppgaverKommer }),
2016
+ /* @__PURE__ */ jsx12(Btn, { variant: "ghost", testID: "gaa-ut", ikon: "\u{1F6AA}", label: tekst.gaaUt, onPress: () => setViserGaaUt(true) })
2017
+ ] }),
2018
+ viserGaaUt ? /* @__PURE__ */ jsx12(Modal3, { visible: true, transparent: true, animationType: "fade", onRequestClose: () => setViserGaaUt(false), accessibilityViewIsModal: true, children: /* @__PURE__ */ jsx12(View12, { style: { flex: 1, backgroundColor: tema.color.scrim, justifyContent: "flex-end", padding: tema.space.md }, children: /* @__PURE__ */ jsxs11(
2019
+ View12,
2020
+ {
2021
+ testID: "gaa-ut-dialog",
2022
+ accessibilityViewIsModal: true,
2023
+ style: {
2024
+ backgroundColor: tema.color.surface,
2025
+ borderRadius: tema.radius.lg,
2026
+ borderWidth: tema.hairline,
2027
+ borderColor: tema.color.borderStrong,
2028
+ padding: tema.space.md,
2029
+ gap: tema.space.md
2030
+ },
2031
+ children: [
2032
+ /* @__PURE__ */ jsx12(H, { children: tekst.gaaUtTittel }),
2033
+ /* @__PURE__ */ jsx12(Text10, { style: { color: tema.color.textPrimary, fontSize: tema.font.body, lineHeight: tema.lineHeight.body }, children: tekst.gaaUtTekst(program.name) }),
2034
+ /* @__PURE__ */ jsxs11(View12, { style: { flexDirection: "row", gap: tema.space.sm }, children: [
2035
+ /* @__PURE__ */ jsx12(Btn, { grow: true, variant: "secondary", testID: "bli-vaerende", label: tekst.bliVaerende, onPress: () => setViserGaaUt(false) }),
2036
+ /* @__PURE__ */ jsx12(Btn, { grow: true, variant: "danger", testID: "bekreft-gaa-ut", ikon: "\u{1F6AA}", label: tekst.gaaUtBekreft, disabled: sender, onPress: () => void gaaUt() })
2037
+ ] })
2038
+ ]
2039
+ }
2040
+ ) }) }) : null
2041
+ ] });
2042
+ }
2043
+ if (!invitasjon || avvist) return null;
2044
+ return /* @__PURE__ */ jsxs11(Fragment2, { children: [
2045
+ /* @__PURE__ */ jsxs11(
2046
+ View12,
2047
+ {
2048
+ testID: "program-banner",
2049
+ accessibilityLabel: tekst.invitasjonRegion,
2050
+ style: {
2051
+ backgroundColor: tema.color.brandBg,
2052
+ borderRadius: tema.radius.lg,
2053
+ borderWidth: tema.hairline,
2054
+ borderColor: tema.color.brand,
2055
+ padding: tema.space.md,
2056
+ gap: tema.space.sm
2057
+ },
2058
+ children: [
2059
+ /* @__PURE__ */ jsxs11(View12, { style: { flexDirection: "row", gap: tema.space.sm, alignItems: "flex-start" }, children: [
2060
+ /* @__PURE__ */ jsx12(Text10, { accessibilityElementsHidden: true, importantForAccessibility: "no", style: { fontSize: tema.font.h3 }, children: "\u{1F9EA}" }),
2061
+ /* @__PURE__ */ jsxs11(View12, { style: { flex: 1, gap: tema.space.xs }, children: [
2062
+ /* @__PURE__ */ jsx12(Text10, { style: { color: tema.color.textPrimary, fontSize: tema.font.body, lineHeight: tema.lineHeight.body }, children: tekst.invitasjonHva(program.name) }),
2063
+ /* @__PURE__ */ jsx12(Hint, { children: tekst.invitasjonKontakter(program.maxContacts) })
2064
+ ] }),
2065
+ /* @__PURE__ */ jsx12(
2066
+ Pressable7,
2067
+ {
2068
+ testID: "avvis-invitasjon",
2069
+ accessibilityRole: "button",
2070
+ accessibilityLabel: tekst.avvisInvitasjon,
2071
+ onPress: () => setAvvist(true),
2072
+ style: { minWidth: tema.touch, minHeight: tema.touch, alignItems: "center", justifyContent: "center" },
2073
+ children: /* @__PURE__ */ jsx12(Text10, { style: { color: tema.color.textSecondary, fontSize: tema.font.body }, children: "\u2715" })
2074
+ }
2075
+ )
2076
+ ] }),
2077
+ feil && !viserAksept ? /* @__PURE__ */ jsx12(Banner, { live: true, ikon: "\u26A0", tone: "danger", children: tekst.akseptFeil(feil) }) : null,
2078
+ /* @__PURE__ */ jsx12(Btn, { variant: "secondary", testID: "les-mer", label: tekst.lesMer, onPress: () => setViserAksept(true) })
2079
+ ]
2080
+ }
2081
+ ),
2082
+ viserAksept ? /* @__PURE__ */ jsx12(
2083
+ Aksept,
2084
+ {
2085
+ program,
2086
+ harToken: !!invitationToken,
2087
+ sender,
2088
+ ...feil ? { feil } : {},
2089
+ onBliMed: () => void bliMed(),
2090
+ onLukk: () => setViserAksept(false)
2091
+ }
2092
+ ) : null
2093
+ ] });
2094
+ }
2095
+
1759
2096
  // src/lagring.ts
1760
2097
  function createStorage(pakke) {
1761
2098
  return {
@@ -1778,6 +2115,7 @@ function createMemoryStorage() {
1778
2115
  }
1779
2116
  export {
1780
2117
  InnspelPrivate,
2118
+ InnspelProgram,
1781
2119
  InnspelProvider,
1782
2120
  byggTema,
1783
2121
  createMemoryStorage,