@innspel/react-native 0.1.0 → 0.2.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/dist/index.js CHANGED
@@ -1,8 +1,51 @@
1
1
  // src/InnspelProvider.tsx
2
- import { createContext as createContext2, useCallback as useCallback2, useContext as useContext2, useMemo as useMemo2, useRef, useState as useState3 } from "react";
2
+ import { createContext as createContext3, useCallback as useCallback3, useContext as useContext3, useMemo as useMemo3, useRef as useRef3, useState as useState4 } from "react";
3
+ import { View as View9 } from "react-native";
3
4
 
4
- // src/tema.ts
5
+ // src/skjermbilde.ts
5
6
  import { createContext, useContext } from "react";
7
+ import { Image } from "react-native";
8
+ function createScreenshotAdapter(captureRef) {
9
+ return {
10
+ async capture(ref, options) {
11
+ const uri = await captureRef(ref, { format: options.format, quality: options.quality, result: "tmpfile" });
12
+ const { width, height } = await new Promise(
13
+ (resolve, reject) => Image.getSize(uri, (w, h) => resolve({ width: w, height: h }), reject)
14
+ );
15
+ return { uri, width, height };
16
+ },
17
+ measure(ref) {
18
+ return new Promise((resolve) => {
19
+ const view = ref.current;
20
+ if (!view) return resolve(null);
21
+ view.measureInWindow((x, y, width, height) => resolve({ x, y, width, height }));
22
+ });
23
+ },
24
+ async read(uri) {
25
+ const res = await fetch(uri);
26
+ return res.blob();
27
+ }
28
+ };
29
+ }
30
+ function createPrivateRegistry() {
31
+ const alle = /* @__PURE__ */ new Set();
32
+ return {
33
+ register(ref) {
34
+ alle.add(ref);
35
+ return () => {
36
+ alle.delete(ref);
37
+ };
38
+ },
39
+ refs: () => [...alle]
40
+ };
41
+ }
42
+ var SkjermbildeContext = createContext(null);
43
+ function useSkjermbilde() {
44
+ return useContext(SkjermbildeContext);
45
+ }
46
+
47
+ // src/tema.ts
48
+ import { createContext as createContext2, useContext as useContext2 } from "react";
6
49
 
7
50
  // src/generated/tokens.ts
8
51
  var t = {
@@ -177,7 +220,9 @@ var screenRules = {
177
220
  brushFill: "#E5007D",
178
221
  autoMaskFill: "#101114",
179
222
  autoMaskStroke: "#E5007D",
180
- previewBackdrop: "#000000"
223
+ previewBackdrop: "#000000",
224
+ brushWidth: 28,
225
+ textBlurRadius: 24
181
226
  };
182
227
 
183
228
  // src/tema.ts
@@ -190,10 +235,10 @@ function byggTema(vertens, palett = "dark") {
190
235
  color: { ...bunn.color, ...vertens.color }
191
236
  };
192
237
  }
193
- var TemaContext = createContext(t);
238
+ var TemaContext = createContext2(t);
194
239
  var TemaProvider = TemaContext.Provider;
195
240
  function useTema() {
196
- return useContext(TemaContext);
241
+ return useContext2(TemaContext);
197
242
  }
198
243
 
199
244
  // src/widget/Widget.tsx
@@ -201,8 +246,8 @@ import {
201
246
  InnspelApiError,
202
247
  InnspelQueueFullError
203
248
  } from "@innspel/core";
204
- import { useCallback, useEffect, useState as useState2 } from "react";
205
- import { Modal, Pressable as Pressable4, Text as Text7, View as View7 } from "react-native";
249
+ import { useCallback as useCallback2, useEffect, useRef as useRef2, useState as useState3 } from "react";
250
+ import { Modal, Pressable as Pressable5, Text as Text8, View as View8 } from "react-native";
206
251
 
207
252
  // src/widget/Felt.tsx
208
253
  import { useMemo, useState } from "react";
@@ -439,6 +484,27 @@ var tekst = {
439
484
  kontekstAldri: "Aldri: navn, e-post, telefon, posisjon eller det som st\xE5r p\xE5 skjermen.",
440
485
  lenketTil: (tittel) => `Lenket til: \xAB${tittel}\xBB`,
441
486
  fjernLenke: "Fjern lenken til den kjente feilen",
487
+ // 2f — skjermbilde (K4, DD-39, DD-40, DD-42)
488
+ leggVedSkjermbilde: "Legg ved skjermbilde",
489
+ leggVedHint: "Bildet tas f\xF8rst n\xE5r du trykker \u2014 du ser det f\xF8r det sendes.",
490
+ skjermbildeTittel: "Se over bildet",
491
+ skjermbildeUnder: "Bildet ble tatt da du trykket. Det du maskerer, ser vi aldri.",
492
+ maskering: "Maskering",
493
+ maskerFelt: "Masker felt",
494
+ tegnOver: "Tegn over",
495
+ maskerAllTekst: "Masker all tekst",
496
+ maskertAvAppen: "Maskert av appen",
497
+ maskertAvAppenHint: "Registrert privat view. Penselen kan ikke oppheve den.",
498
+ dittStrok: "\u25B2 ditt str\xF8k \u2014 maskert (kan angres med ett trykk)",
499
+ allTekstHint: "Hele bildet er gjort uskarpt s\xE5 tekst ikke kan leses. Layouten er synlig.",
500
+ fjernBildet: "Fjern bildet",
501
+ leggVedOgFortsett: "Legg ved og fortsett",
502
+ leggVedHintDD42: "Teksten sendes f\xF8rst; bildet lastes opp for seg.",
503
+ skjermbildeLagtVed: "Skjermbilde lagt ved \u2014 maskert",
504
+ fjernSkjermbilde: "Fjern skjermbildet",
505
+ sendHintMedBilde: "Teksten sendes f\xF8rst; bildet lastes opp for seg.",
506
+ skjermbildeFeilet: "kunne ikke ta skjermbilde. Du kan sende uten.",
507
+ skjermbildeA11y: (auto, egne, uskarpt) => `Forh\xE5ndsvisning av skjermbildet. ${auto ? `${auto} felt maskert av appen og kan ikke avmaskeres. ` : ""}${egne ? `${egne} felt maskert av deg. ` : ""}${uskarpt ? "All tekst er gjort uskarp. " : ""}Trykk for \xE5 maskere et felt, dra for \xE5 tegne over.`,
442
508
  // DD-46 — mønstervarsel. Aldri stille redaksjon.
443
509
  monsterSpm: {
444
510
  email: "Dette ser ut som en e-postadresse. Vil du fjerne den?",
@@ -458,10 +524,17 @@ var tekst = {
458
524
  kvitteringJaOgsa: "Du f\xE5r beskjed n\xE5r den er rettet.",
459
525
  saksnummer: (nr) => `Saksnummer ${nr} \u2014 vis dette hvis du sp\xF8r support.`,
460
526
  tilbakeTilAppen: "Tilbake til appen",
527
+ /** DD-42: saken er sendt, bildet kom ikke med — aldri en blokkert innsending. */
528
+ kvitteringVedleggFeilet: "Sendt \u2014 skjermbildet kunne ikke legges ved",
529
+ tekstenErLagret: "Teksten din er lagret.",
530
+ vedleggFeiletBanner: "Bildet ble ikke lastet opp. Saken er sendt uten det \u2014 vil du, kan du sende et nytt innspill med bilde.",
531
+ saksnummerUtenVedlegg: (nr) => `Saksnummer ${nr} \xB7 uten vedlegg`,
461
532
  // 2i — tilstander
462
533
  offlineTittel: "Ingen nett",
463
534
  offlineBanner: "Ingen nett. Lagres og sendes automatisk \u2014 du ser det under \xABMine innspill\xBB til det er sendt.",
464
535
  offlineLagre: "Lagre \u2014 sendes automatisk",
536
+ /** DD-41: et skjermbilde køes aldri. */
537
+ offlineUtenBilde: "Skjermbilde kan ikke legges ved uten nett. Teksten sendes; bildet ble ikke lagret.",
465
538
  sendefeilBanner: (arsak) => `Kunne ikke sende: ${arsak} Teksten din er beholdt.`,
466
539
  provIgjen: "Pr\xF8v igjen",
467
540
  // 2j — mine innspill
@@ -507,7 +580,10 @@ function Felt({
507
580
  onFjernLenke,
508
581
  funn,
509
582
  onSend,
510
- sender
583
+ sender,
584
+ onSkjermbilde,
585
+ harVedlegg,
586
+ onFjernVedlegg
511
587
  }) {
512
588
  const tema = useTema();
513
589
  const [apen, setApen] = useState(false);
@@ -657,6 +733,48 @@ function Felt({
657
733
  ]
658
734
  }
659
735
  ) : null,
736
+ harVedlegg ? /* @__PURE__ */ jsxs2(
737
+ View2,
738
+ {
739
+ testID: "vedlegg-chip",
740
+ style: {
741
+ flexDirection: "row",
742
+ alignItems: "center",
743
+ gap: tema.space.sm,
744
+ minHeight: tema.touch,
745
+ borderWidth: tema.hairline,
746
+ borderColor: tema.color.borderInteractive,
747
+ borderRadius: tema.radius.pill,
748
+ paddingLeft: tema.space.md,
749
+ paddingRight: tema.space.xs
750
+ },
751
+ children: [
752
+ /* @__PURE__ */ jsx2(Text2, { accessibilityElementsHidden: true, importantForAccessibility: "no", style: { fontSize: tema.font.small }, children: "\u{1F4F7}" }),
753
+ /* @__PURE__ */ jsx2(Text2, { style: { flex: 1, color: tema.color.textPrimary, fontSize: tema.font.small }, children: tekst.skjermbildeLagtVed }),
754
+ /* @__PURE__ */ jsx2(
755
+ Pressable2,
756
+ {
757
+ testID: "fjern-vedlegg",
758
+ accessibilityRole: "button",
759
+ accessibilityLabel: tekst.fjernSkjermbilde,
760
+ onPress: onFjernVedlegg,
761
+ style: { minWidth: tema.touch, minHeight: tema.touch, alignItems: "center", justifyContent: "center" },
762
+ children: /* @__PURE__ */ jsx2(Text2, { style: { color: tema.color.textSecondary, fontSize: tema.font.small }, children: "\u2715" })
763
+ }
764
+ )
765
+ ]
766
+ }
767
+ ) : onSkjermbilde ? /* @__PURE__ */ jsx2(
768
+ Btn,
769
+ {
770
+ variant: "secondary",
771
+ testID: "legg-ved-skjermbilde",
772
+ ikon: "\u{1F4F7}",
773
+ label: tekst.leggVedSkjermbilde,
774
+ hint: tekst.leggVedHint,
775
+ onPress: onSkjermbilde
776
+ }
777
+ ) : null,
660
778
  /* @__PURE__ */ jsxs2(View2, { style: { borderTopWidth: tema.hairline, borderTopColor: tema.color.border, paddingTop: tema.space.sm }, children: [
661
779
  /* @__PURE__ */ jsx2(
662
780
  Pressable2,
@@ -717,7 +835,7 @@ function Felt({
717
835
  testID: "send",
718
836
  label: spor === "onske" && !forKort ? tekst.nesteOnske : tekst.send,
719
837
  disabled: forKort || sender,
720
- hint: forKort ? tekst.forKort(minTextLength) : void 0,
838
+ hint: forKort ? tekst.forKort(minTextLength) : harVedlegg ? tekst.sendHintMedBilde : void 0,
721
839
  onPress: onSend
722
840
  }
723
841
  ) })
@@ -872,6 +990,7 @@ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
872
990
  function Kvittering({
873
991
  art,
874
992
  saksnummer,
993
+ vedlegg,
875
994
  onLukk
876
995
  }) {
877
996
  const tema = useTema();
@@ -883,6 +1002,8 @@ function Kvittering({
883
1002
  // (DD-41 presiserer DD-07). Den lover bare at det sendes.
884
1003
  k\u00F8: { h: tekst.offlineTittel, p: tekst.offlineBanner }
885
1004
  }[art];
1005
+ const overskrift = vedlegg === "feilet" ? tekst.kvitteringVedleggFeilet : innhold.h;
1006
+ const brodtekst = vedlegg === "feilet" ? `${tekst.tekstenErLagret} ${innhold.p}` : innhold.p;
886
1007
  return /* @__PURE__ */ jsxs5(View5, { style: { flex: 1, justifyContent: "center", gap: tema.space.lg }, children: [
887
1008
  /* @__PURE__ */ jsxs5(View5, { style: { alignItems: "center", gap: tema.space.md }, children: [
888
1009
  /* @__PURE__ */ jsx5(
@@ -894,7 +1015,9 @@ function Kvittering({
894
1015
  children: art === "k\xF8" ? "\u21C5" : "\u2713"
895
1016
  }
896
1017
  ),
897
- /* @__PURE__ */ jsx5(H, { children: innhold.h }),
1018
+ /* @__PURE__ */ jsx5(H, { children: overskrift }),
1019
+ vedlegg === "feilet" ? /* @__PURE__ */ jsx5(Banner, { ikon: "\u26A0", tone: "warning", children: tekst.vedleggFeiletBanner }) : null,
1020
+ vedlegg === "forkastet" ? /* @__PURE__ */ jsx5(Banner, { ikon: "\u{1F4F7}", tone: "info", children: tekst.offlineUtenBilde }) : null,
898
1021
  /* @__PURE__ */ jsx5(
899
1022
  Text5,
900
1023
  {
@@ -907,10 +1030,10 @@ function Kvittering({
907
1030
  lineHeight: tema.lineHeight.body,
908
1031
  textAlign: "center"
909
1032
  },
910
- children: innhold.p
1033
+ children: brodtekst
911
1034
  }
912
1035
  ),
913
- saksnummer ? /* @__PURE__ */ jsx5(Hint, { children: tekst.saksnummer(saksnummer) }) : null
1036
+ saksnummer ? /* @__PURE__ */ jsx5(Hint, { children: vedlegg === "feilet" ? tekst.saksnummerUtenVedlegg(saksnummer) : tekst.saksnummer(saksnummer) }) : null
914
1037
  ] }),
915
1038
  /* @__PURE__ */ jsx5(Btn, { testID: "tilbake-til-appen", label: tekst.tilbakeTilAppen, onPress: onLukk })
916
1039
  ] });
@@ -1043,8 +1166,209 @@ function MineInnspill({
1043
1166
  ] });
1044
1167
  }
1045
1168
 
1169
+ // src/widget/Skjermbilde.tsx
1170
+ import { skaler, strokTilRekter, treffer, trykkTilRekt } from "@innspel/core";
1171
+ import { useCallback, useMemo as useMemo2, useRef, useState as useState2 } from "react";
1172
+ import { Image as Image2, Pressable as Pressable4, Text as Text7, View as View7 } from "react-native";
1173
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1174
+ function Skjermbilde({
1175
+ bilde,
1176
+ automaskerRot,
1177
+ rotStorrelse,
1178
+ previewRef,
1179
+ komponerer,
1180
+ onFjern,
1181
+ onLeggVed
1182
+ }) {
1183
+ const tema = useTema();
1184
+ const [verktoy, setVerktoy] = useState2("pensel");
1185
+ const [allTekst, setAllTekst] = useState2(false);
1186
+ const [egne, setEgne] = useState2([]);
1187
+ const [ramme, setRamme] = useState2(null);
1188
+ const strok = useRef([]);
1189
+ const visning = useMemo2(() => {
1190
+ if (!ramme) return null;
1191
+ const s = Math.min(ramme.width / bilde.width, ramme.height / bilde.height);
1192
+ return { width: Math.floor(bilde.width * s), height: Math.floor(bilde.height * s) };
1193
+ }, [ramme, bilde.width, bilde.height]);
1194
+ const automasker = useMemo2(
1195
+ () => visning ? automaskerRot.map((r) => skaler(r, rotStorrelse, visning)) : [],
1196
+ [automaskerRot, rotStorrelse, visning]
1197
+ );
1198
+ const onLayout = useCallback((e) => {
1199
+ const { width, height } = e.nativeEvent.layout;
1200
+ setRamme({ width, height });
1201
+ }, []);
1202
+ const punkt = (e) => ({ x: e.nativeEvent.locationX, y: e.nativeEvent.locationY });
1203
+ const onGrant = (e) => {
1204
+ if (!visning) return;
1205
+ const p = punkt(e);
1206
+ const treff = egne.findIndex((r) => treffer(p, r));
1207
+ if (treff >= 0) {
1208
+ setEgne((x) => x.filter((_, i) => i !== treff));
1209
+ return;
1210
+ }
1211
+ if (verktoy === "felt") {
1212
+ const r = trykkTilRekt(p, { width: tema.touch * 4, height: tema.touch }, visning);
1213
+ if (r) setEgne((x) => [...x, r]);
1214
+ return;
1215
+ }
1216
+ strok.current = [p];
1217
+ };
1218
+ const onMove = (e) => {
1219
+ if (verktoy !== "pensel" || strok.current.length === 0) return;
1220
+ strok.current.push(punkt(e));
1221
+ };
1222
+ const onRelease = () => {
1223
+ if (verktoy !== "pensel" || !visning || strok.current.length === 0) return;
1224
+ const rekter = strokTilRekter(strok.current, screenRules.brushWidth, visning);
1225
+ strok.current = [];
1226
+ if (rekter.length) setEgne((x) => [...x, ...rekter]);
1227
+ };
1228
+ const knapp = (id, ikon, label) => {
1229
+ const valgt = id === "allTekst" ? allTekst : verktoy === id && !allTekst;
1230
+ return /* @__PURE__ */ jsxs7(
1231
+ Pressable4,
1232
+ {
1233
+ testID: `verktoy-${id}`,
1234
+ accessibilityRole: "button",
1235
+ accessibilityLabel: label,
1236
+ accessibilityState: { selected: valgt },
1237
+ onPress: () => id === "allTekst" ? setAllTekst((a) => !a) : setVerktoy(id),
1238
+ style: {
1239
+ flex: 1,
1240
+ minHeight: tema.touch,
1241
+ alignItems: "center",
1242
+ justifyContent: "center",
1243
+ gap: tema.space.xs,
1244
+ padding: tema.space.xs,
1245
+ borderRadius: tema.radius.md,
1246
+ borderWidth: tema.hairline,
1247
+ borderColor: tema.color.actionSecondaryBorder,
1248
+ borderBottomWidth: valgt ? tema.hairline * 2 : tema.hairline,
1249
+ borderBottomColor: valgt ? tema.color.textPrimary : tema.color.actionSecondaryBorder,
1250
+ backgroundColor: valgt ? tema.color.surfaceRaised : "transparent"
1251
+ },
1252
+ children: [
1253
+ /* @__PURE__ */ jsx7(Text7, { accessibilityElementsHidden: true, importantForAccessibility: "no", style: { fontSize: tema.font.body }, children: ikon }),
1254
+ /* @__PURE__ */ jsx7(
1255
+ Text7,
1256
+ {
1257
+ style: {
1258
+ color: tema.color.textPrimary,
1259
+ fontSize: tema.font.small,
1260
+ fontWeight: valgt ? tema.weight.semibold : tema.weight.regular
1261
+ },
1262
+ children: label
1263
+ }
1264
+ )
1265
+ ]
1266
+ },
1267
+ id
1268
+ );
1269
+ };
1270
+ const a11y = tekst.skjermbildeA11y(automasker.length, egne.length, allTekst);
1271
+ return /* @__PURE__ */ jsxs7(View7, { style: { flex: 1, gap: tema.space.md }, children: [
1272
+ /* @__PURE__ */ jsx7(H, { sub: tekst.skjermbildeUnder, children: tekst.skjermbildeTittel }),
1273
+ /* @__PURE__ */ jsxs7(View7, { accessibilityRole: "toolbar", accessibilityLabel: tekst.maskering, style: { flexDirection: "row", gap: tema.space.sm }, children: [
1274
+ knapp("felt", "\u25A3", tekst.maskerFelt),
1275
+ knapp("pensel", "\u270E", tekst.tegnOver),
1276
+ knapp("allTekst", "\u25A6", tekst.maskerAllTekst)
1277
+ ] }),
1278
+ /* @__PURE__ */ jsx7(
1279
+ View7,
1280
+ {
1281
+ testID: "skjermbilde-ramme",
1282
+ onLayout,
1283
+ style: {
1284
+ flex: 1,
1285
+ minHeight: tema.touch * 5,
1286
+ alignItems: "center",
1287
+ justifyContent: "center",
1288
+ backgroundColor: screenRules.previewBackdrop,
1289
+ borderRadius: tema.radius.md,
1290
+ overflow: "hidden"
1291
+ },
1292
+ children: visning ? /* @__PURE__ */ jsxs7(
1293
+ View7,
1294
+ {
1295
+ ref: previewRef,
1296
+ collapsable: false,
1297
+ testID: "forhandsvisning",
1298
+ accessibilityRole: "image",
1299
+ accessibilityLabel: a11y,
1300
+ onStartShouldSetResponder: () => !komponerer,
1301
+ onMoveShouldSetResponder: () => !komponerer,
1302
+ onResponderGrant: onGrant,
1303
+ onResponderMove: onMove,
1304
+ onResponderRelease: onRelease,
1305
+ onResponderTerminate: onRelease,
1306
+ style: { width: visning.width, height: visning.height, backgroundColor: screenRules.previewBackdrop },
1307
+ children: [
1308
+ /* @__PURE__ */ jsx7(
1309
+ Image2,
1310
+ {
1311
+ testID: "skjermbilde-bilde",
1312
+ source: { uri: bilde.uri },
1313
+ blurRadius: allTekst ? screenRules.textBlurRadius : 0,
1314
+ style: { width: visning.width, height: visning.height },
1315
+ accessibilityIgnoresInvertColors: true
1316
+ }
1317
+ ),
1318
+ automasker.map((r, i) => /* @__PURE__ */ jsx7(
1319
+ View7,
1320
+ {
1321
+ testID: `automask-${i}`,
1322
+ pointerEvents: "none",
1323
+ style: {
1324
+ position: "absolute",
1325
+ left: r.x,
1326
+ top: r.y,
1327
+ width: r.width,
1328
+ height: r.height,
1329
+ backgroundColor: screenRules.autoMaskFill,
1330
+ borderWidth: tema.hairline * 2,
1331
+ borderColor: screenRules.autoMaskStroke,
1332
+ alignItems: "center",
1333
+ justifyContent: "center"
1334
+ },
1335
+ children: !komponerer ? /* @__PURE__ */ jsxs7(Text7, { style: { color: tema.color.textSecondary, fontSize: tema.font.small }, numberOfLines: 1, children: [
1336
+ "\u{1F512} ",
1337
+ tekst.maskertAvAppen
1338
+ ] }) : null
1339
+ },
1340
+ `auto-${i}`
1341
+ )),
1342
+ egne.map((r, i) => /* @__PURE__ */ jsx7(
1343
+ View7,
1344
+ {
1345
+ testID: `maske-${i}`,
1346
+ pointerEvents: "none",
1347
+ style: { position: "absolute", left: r.x, top: r.y, width: r.width, height: r.height, backgroundColor: screenRules.brushFill }
1348
+ },
1349
+ `egen-${i}`
1350
+ ))
1351
+ ]
1352
+ }
1353
+ ) : null
1354
+ }
1355
+ ),
1356
+ automasker.length ? /* @__PURE__ */ jsxs7(Hint, { children: [
1357
+ "\u{1F512} ",
1358
+ tekst.maskertAvAppenHint
1359
+ ] }) : null,
1360
+ egne.length ? /* @__PURE__ */ jsx7(Hint, { children: tekst.dittStrok }) : null,
1361
+ allTekst ? /* @__PURE__ */ jsx7(Hint, { children: tekst.allTekstHint }) : null,
1362
+ /* @__PURE__ */ jsxs7(View7, { style: { gap: tema.space.sm }, children: [
1363
+ /* @__PURE__ */ jsx7(Btn, { variant: "danger", testID: "fjern-bildet", ikon: "\u{1F5D1}", label: tekst.fjernBildet, onPress: onFjern, disabled: komponerer }),
1364
+ /* @__PURE__ */ jsx7(Btn, { testID: "legg-ved", label: tekst.leggVedOgFortsett, onPress: onLeggVed, disabled: komponerer || !visning, hint: tekst.leggVedHintDD42 })
1365
+ ] })
1366
+ ] });
1367
+ }
1368
+
1046
1369
  // src/widget/Widget.tsx
1047
- import { Fragment, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1370
+ import { Fragment, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1371
+ var FANGST = { format: "jpg", quality: 0.85 };
1048
1372
  function Widget({
1049
1373
  client,
1050
1374
  synlig,
@@ -1053,20 +1377,28 @@ function Widget({
1053
1377
  tenantName
1054
1378
  }) {
1055
1379
  const tema = useTema();
1056
- const [steg, setSteg] = useState2("valg");
1057
- const [spor, setSpor] = useState2("feil");
1058
- const [verdi, setVerdi] = useState2("");
1059
- const [kort, setKort] = useState2([]);
1060
- const [lasterKort, setLasterKort] = useState2(false);
1061
- const [lenketTil, setLenketTil] = useState2();
1062
- const [funn, setFunn] = useState2([]);
1063
- const [fjernede, setFjernede] = useState2([]);
1064
- const [sender, setSender] = useState2(false);
1065
- const [feil, setFeil] = useState2();
1066
- const [kvittering, setKvittering] = useState2();
1067
- const [mine, setMine] = useState2([]);
1068
- const [lasterMine, setLasterMine] = useState2(false);
1069
- const nullstill = useCallback(() => {
1380
+ const skjermbilde = useSkjermbilde();
1381
+ const previewRef = useRef2(null);
1382
+ const [steg, setSteg] = useState3("valg");
1383
+ const [spor, setSpor] = useState3("feil");
1384
+ const [verdi, setVerdi] = useState3("");
1385
+ const [kort, setKort] = useState3([]);
1386
+ const [lasterKort, setLasterKort] = useState3(false);
1387
+ const [lenketTil, setLenketTil] = useState3();
1388
+ const [funn, setFunn] = useState3([]);
1389
+ const [fjernede, setFjernede] = useState3([]);
1390
+ const [sender, setSender] = useState3(false);
1391
+ const [feil, setFeil] = useState3();
1392
+ const [kvittering, setKvittering] = useState3();
1393
+ const [mine, setMine] = useState3([]);
1394
+ const [lasterMine, setLasterMine] = useState3(false);
1395
+ const [bilde, setBilde] = useState3();
1396
+ const [automasker, setAutomasker] = useState3([]);
1397
+ const [rotStorrelse, setRotStorrelse] = useState3({ width: 1, height: 1 });
1398
+ const [komponerer, setKomponerer] = useState3(false);
1399
+ const [vedlegg, setVedlegg] = useState3();
1400
+ const skjermbildeMulig = client.settings.screenshotsEnabled && skjermbilde?.adapter != null;
1401
+ const nullstill = useCallback2(() => {
1070
1402
  setSteg("valg");
1071
1403
  setSpor("feil");
1072
1404
  setVerdi("");
@@ -1076,8 +1408,12 @@ function Widget({
1076
1408
  setFjernede([]);
1077
1409
  setFeil(void 0);
1078
1410
  setKvittering(void 0);
1411
+ setBilde(void 0);
1412
+ setAutomasker([]);
1413
+ setVedlegg(void 0);
1414
+ setKomponerer(false);
1079
1415
  }, []);
1080
- const lukk = useCallback(() => {
1416
+ const lukk = useCallback2(() => {
1081
1417
  onLukk();
1082
1418
  nullstill();
1083
1419
  }, [onLukk, nullstill]);
@@ -1087,7 +1423,7 @@ function Widget({
1087
1423
  void client.queue.flush().catch(() => void 0);
1088
1424
  if (apneOpsjoner?.track) velgSpor(apneOpsjoner.track);
1089
1425
  }, [synlig]);
1090
- const hentKort = useCallback(async () => {
1426
+ const hentKort = useCallback2(async () => {
1091
1427
  setLasterKort(true);
1092
1428
  try {
1093
1429
  const q = apneOpsjoner?.screen ? { screen: apneOpsjoner.screen } : {};
@@ -1096,7 +1432,7 @@ function Widget({
1096
1432
  setLasterKort(false);
1097
1433
  }
1098
1434
  }, [client, apneOpsjoner?.screen]);
1099
- const velgSpor = useCallback(
1435
+ const velgSpor = useCallback2(
1100
1436
  async (valgt) => {
1101
1437
  setSpor(valgt);
1102
1438
  client.track({ name: "track_selected", type: valgt });
@@ -1112,14 +1448,59 @@ function Widget({
1112
1448
  useEffect(() => {
1113
1449
  if (steg === "kjenteFeil" && !lasterKort && kort.length === 0) setSteg("felt");
1114
1450
  }, [steg, lasterKort, kort.length]);
1115
- const endreTekst = useCallback(
1451
+ const endreTekst = useCallback2(
1116
1452
  (v) => {
1117
1453
  setVerdi(v);
1118
1454
  setFunn(client.scanText(v));
1119
1455
  },
1120
1456
  [client]
1121
1457
  );
1122
- const send = useCallback(async () => {
1458
+ const taSkjermbilde = useCallback2(async () => {
1459
+ if (!skjermbilde?.adapter) return;
1460
+ const { adapter, rootRef, privat } = skjermbilde;
1461
+ setFeil(void 0);
1462
+ try {
1463
+ const fangst = await adapter.capture(rootRef, FANGST);
1464
+ const rot = await adapter.measure(rootRef);
1465
+ const private_ = await Promise.all(privat.refs().map((r) => adapter.measure(r)));
1466
+ if (rot) {
1467
+ setRotStorrelse({ width: rot.width, height: rot.height });
1468
+ setAutomasker(
1469
+ private_.filter((r) => r !== null).map((r) => ({ x: r.x - rot.x, y: r.y - rot.y, width: r.width, height: r.height }))
1470
+ );
1471
+ } else {
1472
+ setRotStorrelse({ width: fangst.width, height: fangst.height });
1473
+ setAutomasker([]);
1474
+ }
1475
+ setBilde(fangst);
1476
+ setSteg("skjermbilde");
1477
+ } catch {
1478
+ setFeil(tekst.skjermbildeFeilet);
1479
+ }
1480
+ }, [skjermbilde]);
1481
+ const leggVedBilde = useCallback2(async () => {
1482
+ if (!skjermbilde?.adapter) return;
1483
+ setKomponerer(true);
1484
+ try {
1485
+ const komposit = await skjermbilde.adapter.capture(previewRef, FANGST);
1486
+ const body = await skjermbilde.adapter.read(komposit.uri);
1487
+ setVedlegg({ mime: "image/jpeg", body, width: komposit.width, height: komposit.height });
1488
+ setBilde(void 0);
1489
+ setSteg("felt");
1490
+ } catch {
1491
+ setFeil(tekst.skjermbildeFeilet);
1492
+ setSteg("felt");
1493
+ } finally {
1494
+ setKomponerer(false);
1495
+ }
1496
+ }, [skjermbilde]);
1497
+ const fjernBilde = useCallback2(() => {
1498
+ setBilde(void 0);
1499
+ setAutomasker([]);
1500
+ setVedlegg(void 0);
1501
+ setSteg("felt");
1502
+ }, []);
1503
+ const send = useCallback2(async () => {
1123
1504
  if (spor === "onske" && steg === "felt" && kort.length === 0 && !lasterKort) {
1124
1505
  setSteg("kjenteFeil");
1125
1506
  await hentKort();
@@ -1131,13 +1512,18 @@ function Widget({
1131
1512
  const res = await client.capture({
1132
1513
  type: spor,
1133
1514
  text: verdi,
1515
+ ...vedlegg ? { attachment: vedlegg } : {},
1134
1516
  ...spor === "feil" && lenketTil ? { relatedKnownIssueId: lenketTil.id } : {},
1135
1517
  ...fjernede.length ? { context: fjernetKontekst(client, fjernede) } : {}
1136
1518
  });
1137
1519
  if (res.status === "queued") {
1138
- setKvittering({ art: "k\xF8" });
1520
+ setKvittering({ art: "k\xF8", ...res.attachment ? { vedlegg: "forkastet" } : {} });
1139
1521
  } else {
1140
- setKvittering({ art: spor, nr: res.submission.id });
1522
+ setKvittering({
1523
+ art: spor,
1524
+ nr: res.submission.id,
1525
+ ...res.attachment?.status === "failed" ? { vedlegg: "feilet" } : {}
1526
+ });
1141
1527
  }
1142
1528
  setSteg("kvittering");
1143
1529
  } catch (e) {
@@ -1147,8 +1533,8 @@ function Widget({
1147
1533
  } finally {
1148
1534
  setSender(false);
1149
1535
  }
1150
- }, [client, spor, steg, verdi, lenketTil, fjernede, kort.length, lasterKort, hentKort]);
1151
- const jaOgsa = useCallback(
1536
+ }, [client, spor, steg, verdi, vedlegg, lenketTil, fjernede, kort.length, lasterKort, hentKort]);
1537
+ const jaOgsa = useCallback2(
1152
1538
  async (k) => {
1153
1539
  setSender(true);
1154
1540
  try {
@@ -1163,7 +1549,7 @@ function Widget({
1163
1549
  },
1164
1550
  [client]
1165
1551
  );
1166
- const visMine = useCallback(async () => {
1552
+ const visMine = useCallback2(async () => {
1167
1553
  setSteg("mine");
1168
1554
  setLasterMine(true);
1169
1555
  try {
@@ -1174,7 +1560,7 @@ function Widget({
1174
1560
  setLasterMine(false);
1175
1561
  }
1176
1562
  }, [client]);
1177
- return /* @__PURE__ */ jsx7(
1563
+ return /* @__PURE__ */ jsx8(
1178
1564
  Modal,
1179
1565
  {
1180
1566
  visible: synlig,
@@ -1182,8 +1568,8 @@ function Widget({
1182
1568
  animationType: "slide",
1183
1569
  onRequestClose: lukk,
1184
1570
  accessibilityViewIsModal: true,
1185
- children: /* @__PURE__ */ jsx7(View7, { style: { flex: 1, backgroundColor: tema.color.scrim, justifyContent: "flex-end" }, children: /* @__PURE__ */ jsxs7(
1186
- View7,
1571
+ children: /* @__PURE__ */ jsx8(View8, { style: { flex: 1, backgroundColor: tema.color.scrim, justifyContent: "flex-end" }, children: /* @__PURE__ */ jsxs8(
1572
+ View8,
1187
1573
  {
1188
1574
  testID: "innspel-widget",
1189
1575
  accessibilityViewIsModal: true,
@@ -1198,26 +1584,26 @@ function Widget({
1198
1584
  minHeight: "60%"
1199
1585
  },
1200
1586
  children: [
1201
- /* @__PURE__ */ jsxs7(View7, { style: { flexDirection: "row", justifyContent: "space-between", alignItems: "center" }, children: [
1202
- steg !== "valg" && steg !== "kvittering" ? /* @__PURE__ */ jsx7(Btn, { variant: "ghost", testID: "tilbake", ikon: "\u2190", label: tekst.tilbake, onPress: nullstill }) : /* @__PURE__ */ jsx7(View7, {}),
1203
- /* @__PURE__ */ jsx7(
1204
- Pressable4,
1587
+ /* @__PURE__ */ jsxs8(View8, { style: { flexDirection: "row", justifyContent: "space-between", alignItems: "center" }, children: [
1588
+ steg === "skjermbilde" ? /* @__PURE__ */ jsx8(Btn, { variant: "ghost", testID: "tilbake", ikon: "\u2190", label: tekst.tilbake, onPress: fjernBilde }) : steg !== "valg" && steg !== "kvittering" ? /* @__PURE__ */ jsx8(Btn, { variant: "ghost", testID: "tilbake", ikon: "\u2190", label: tekst.tilbake, onPress: nullstill }) : /* @__PURE__ */ jsx8(View8, {}),
1589
+ /* @__PURE__ */ jsx8(
1590
+ Pressable5,
1205
1591
  {
1206
1592
  testID: "lukk",
1207
1593
  accessibilityRole: "button",
1208
1594
  accessibilityLabel: tekst.lukk,
1209
1595
  onPress: lukk,
1210
1596
  style: { minWidth: tema.touch, minHeight: tema.touch, alignItems: "center", justifyContent: "center" },
1211
- children: /* @__PURE__ */ jsx7(Text7, { style: { color: tema.color.textSecondary, fontSize: tema.font.body }, children: "\u2715" })
1597
+ children: /* @__PURE__ */ jsx8(Text8, { style: { color: tema.color.textSecondary, fontSize: tema.font.body }, children: "\u2715" })
1212
1598
  }
1213
1599
  )
1214
1600
  ] }),
1215
- feil ? /* @__PURE__ */ jsx7(Banner, { live: true, ikon: "\u26A0", tone: "danger", children: tekst.sendefeilBanner(feil) }) : null,
1216
- steg === "valg" ? /* @__PURE__ */ jsxs7(Fragment, { children: [
1217
- /* @__PURE__ */ jsx7(Valg, { onVelg: velgSpor }),
1218
- /* @__PURE__ */ jsx7(Btn, { variant: "ghost", testID: "mine-innspill", ikon: "\u{1F4CB}", label: tekst.mineTittel, onPress: visMine })
1601
+ feil ? /* @__PURE__ */ jsx8(Banner, { live: true, ikon: "\u26A0", tone: "danger", children: tekst.sendefeilBanner(feil) }) : null,
1602
+ steg === "valg" ? /* @__PURE__ */ jsxs8(Fragment, { children: [
1603
+ /* @__PURE__ */ jsx8(Valg, { onVelg: velgSpor }),
1604
+ /* @__PURE__ */ jsx8(Btn, { variant: "ghost", testID: "mine-innspill", ikon: "\u{1F4CB}", label: tekst.mineTittel, onPress: visMine })
1219
1605
  ] }) : null,
1220
- steg === "kjenteFeil" ? /* @__PURE__ */ jsx7(
1606
+ steg === "kjenteFeil" ? /* @__PURE__ */ jsx8(
1221
1607
  KjenteFeil,
1222
1608
  {
1223
1609
  kort,
@@ -1231,7 +1617,7 @@ function Widget({
1231
1617
  onNei: () => spor === "onske" ? void send() : setSteg("felt")
1232
1618
  }
1233
1619
  ) : null,
1234
- steg === "felt" ? /* @__PURE__ */ jsx7(
1620
+ steg === "felt" ? /* @__PURE__ */ jsx8(
1235
1621
  Felt,
1236
1622
  {
1237
1623
  spor,
@@ -1245,11 +1631,34 @@ function Widget({
1245
1631
  ...lenketTil ? { lenketTil, onFjernLenke: () => setLenketTil(void 0) } : {},
1246
1632
  funn,
1247
1633
  onSend: send,
1248
- sender
1634
+ sender,
1635
+ ...skjermbildeMulig ? { onSkjermbilde: taSkjermbilde } : {},
1636
+ harVedlegg: vedlegg !== void 0,
1637
+ onFjernVedlegg: fjernBilde
1249
1638
  }
1250
1639
  ) : null,
1251
- steg === "kvittering" && kvittering ? /* @__PURE__ */ jsx7(Kvittering, { art: kvittering.art, ...kvittering.nr ? { saksnummer: kvittering.nr } : {}, onLukk: lukk }) : null,
1252
- steg === "mine" ? /* @__PURE__ */ jsx7(
1640
+ steg === "skjermbilde" && bilde ? /* @__PURE__ */ jsx8(
1641
+ Skjermbilde,
1642
+ {
1643
+ bilde,
1644
+ automaskerRot: automasker,
1645
+ rotStorrelse,
1646
+ previewRef,
1647
+ komponerer,
1648
+ onFjern: fjernBilde,
1649
+ onLeggVed: leggVedBilde
1650
+ }
1651
+ ) : null,
1652
+ steg === "kvittering" && kvittering ? /* @__PURE__ */ jsx8(
1653
+ Kvittering,
1654
+ {
1655
+ art: kvittering.art,
1656
+ ...kvittering.nr ? { saksnummer: kvittering.nr } : {},
1657
+ ...kvittering.vedlegg ? { vedlegg: kvittering.vedlegg } : {},
1658
+ onLukk: lukk
1659
+ }
1660
+ ) : null,
1661
+ steg === "mine" ? /* @__PURE__ */ jsx8(
1253
1662
  MineInnspill,
1254
1663
  {
1255
1664
  rader: mine,
@@ -1273,35 +1682,42 @@ function fjernetKontekst(client, fjernede) {
1273
1682
  }
1274
1683
 
1275
1684
  // src/InnspelProvider.tsx
1276
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1277
- var Kontekst = createContext2(null);
1685
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1686
+ var Kontekst = createContext3(null);
1278
1687
  function InnspelProvider({
1279
1688
  client,
1280
1689
  children,
1281
1690
  theme,
1282
1691
  colorScheme = "dark",
1283
- tenantName
1692
+ tenantName,
1693
+ screenshot
1284
1694
  }) {
1285
- const [synlig, setSynlig] = useState3(false);
1286
- const [apneOpsjoner, setApneOpsjoner] = useState3();
1287
- const registrert = useRef(false);
1288
- const open = useCallback2(
1695
+ const [synlig, setSynlig] = useState4(false);
1696
+ const [apneOpsjoner, setApneOpsjoner] = useState4();
1697
+ const registrert = useRef3(false);
1698
+ const rootRef = useRef3(null);
1699
+ const privat = useMemo3(createPrivateRegistry, []);
1700
+ const open = useCallback3(
1289
1701
  async (options) => {
1290
1702
  setApneOpsjoner(options);
1291
1703
  setSynlig(true);
1292
1704
  },
1293
1705
  []
1294
1706
  );
1295
- const close = useCallback2(() => setSynlig(false), []);
1707
+ const close = useCallback3(() => setSynlig(false), []);
1296
1708
  if (!registrert.current) {
1297
1709
  client.registerPresenter({ open });
1298
1710
  registrert.current = true;
1299
1711
  }
1300
- const tema = useMemo2(() => byggTema(theme, colorScheme), [theme, colorScheme]);
1301
- const verdi = useMemo2(() => ({ client, open, close }), [client, open, close]);
1302
- return /* @__PURE__ */ jsx8(Kontekst.Provider, { value: verdi, children: /* @__PURE__ */ jsxs8(TemaProvider, { value: tema, children: [
1303
- children,
1304
- /* @__PURE__ */ jsx8(
1712
+ const tema = useMemo3(() => byggTema(theme, colorScheme), [theme, colorScheme]);
1713
+ const verdi = useMemo3(() => ({ client, open, close }), [client, open, close]);
1714
+ const skjermbilde = useMemo3(
1715
+ () => ({ adapter: screenshot ?? null, rootRef, privat }),
1716
+ [screenshot, privat]
1717
+ );
1718
+ return /* @__PURE__ */ jsx9(Kontekst.Provider, { value: verdi, children: /* @__PURE__ */ jsx9(SkjermbildeContext.Provider, { value: skjermbilde, children: /* @__PURE__ */ jsxs9(TemaProvider, { value: tema, children: [
1719
+ /* @__PURE__ */ jsx9(View9, { ref: rootRef, collapsable: false, style: { flex: 1 }, testID: "innspel-rot", children }),
1720
+ /* @__PURE__ */ jsx9(
1305
1721
  Widget,
1306
1722
  {
1307
1723
  client,
@@ -1311,10 +1727,10 @@ function InnspelProvider({
1311
1727
  onLukk: close
1312
1728
  }
1313
1729
  )
1314
- ] }) });
1730
+ ] }) }) });
1315
1731
  }
1316
1732
  function useInnspel() {
1317
- const k = useContext2(Kontekst);
1733
+ const k = useContext3(Kontekst);
1318
1734
  if (!k) {
1319
1735
  throw new Error(
1320
1736
  "[innspel] useInnspel() ble kalt utenfor <InnspelProvider>. Pakk appen din i den \u2014 se README.md."
@@ -1323,6 +1739,21 @@ function useInnspel() {
1323
1739
  return k;
1324
1740
  }
1325
1741
 
1742
+ // src/InnspelPrivate.tsx
1743
+ import { useEffect as useEffect2, useRef as useRef4 } from "react";
1744
+ import { View as View10 } from "react-native";
1745
+ import { jsx as jsx10 } from "react/jsx-runtime";
1746
+ function InnspelPrivate({ children, style }) {
1747
+ const ref = useRef4(null);
1748
+ const kontekst = useSkjermbilde();
1749
+ const registrer = kontekst?.privat.register;
1750
+ useEffect2(() => {
1751
+ if (!registrer) return;
1752
+ return registrer(ref);
1753
+ }, [registrer]);
1754
+ return /* @__PURE__ */ jsx10(View10, { ref, collapsable: false, style, testID: "innspel-privat", children });
1755
+ }
1756
+
1326
1757
  // src/lagring.ts
1327
1758
  function createStorage(pakke) {
1328
1759
  return {
@@ -1344,9 +1775,11 @@ function createMemoryStorage() {
1344
1775
  };
1345
1776
  }
1346
1777
  export {
1778
+ InnspelPrivate,
1347
1779
  InnspelProvider,
1348
1780
  byggTema,
1349
1781
  createMemoryStorage,
1782
+ createScreenshotAdapter,
1350
1783
  createStorage,
1351
1784
  t as darkTokens,
1352
1785
  light,