@innspel/react-native 0.5.1 → 0.7.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/README.md CHANGED
@@ -98,7 +98,7 @@ Innspels. Innsenderen skal se hvem som svarte.
98
98
  | Kjente feil | I feilsporet **før** feltet, i ønskesporet etter. Tre svar per kort: «Ja, jeg også» (teller deg uten ny sak), «Ligner, men ikke helt» (ditt eget innspill med lenke), «Nei, det er noe annet». Tom liste hopper skjermen over — treffene er et tilbud, aldri en sperre. |
99
99
  | Feltet | Ett tekstfelt. Send er aktiv fra N tegn, og **ingenting annet er obligatorisk**. Konteksten som legges ved er synlig og kan fjernes linje for linje. |
100
100
  | Mønstervarsel | Ser teksten ut til å inneholde e-post, telefonnummer, kortnummer, IP eller en tilgangsnøkkel, spør widgeten om det skal fjernes. Den fjerner **aldri** noe selv. |
101
- | Skjermbilde | Valgfritt. Tas ved trykk, forhåndsvises, maskeres på enheten — private views automatisk, resten med felt, pensel eller «Masker all tekst». Se under. |
101
+ | Skjermbilde | Valgfritt, én eller to veier: «Legg ved skjermbilde» (skjermen bak arket, tatt ved trykk) og «Velg bilde» (fra galleriet, med din velger). Forhåndsvises og maskeres på enheten — private views automatisk på en fangst, resten med felt, pensel eller «Masker all tekst». Se under. |
102
102
  | Kvittering | Lover noe konkret, per spor, med saksnummer. Ber aldri om en vurdering av appen. Feilet bildet, sier den det — saken er sendt uansett. |
103
103
  | Mine innspill | Status, løftet som hører til, og svaret fra deg under saken. Det som venter på nett står øverst. |
104
104
 
@@ -132,6 +132,47 @@ import { createScreenshotAdapter, InnspelPrivate } from '@innspel/react-native';
132
132
  <InnspelProvider client={innspel} screenshot={createScreenshotAdapter(captureRef)}>
133
133
  ```
134
134
 
135
+ **Bilde fra galleriet.** Et innspill handler sjelden om skjermen widgeten åpnes fra — åpnes
136
+ den fra en profilside, viser fangsten profilen, ikke feilen. Gi adapteren din egen
137
+ bildevelger, så får innsenderen «Velg bilde» og kan legge ved et systemskjermbilde tatt
138
+ der feilen er. Pakken tar ingen avhengighet på velgeren:
139
+
140
+ ```tsx
141
+ import * as ImagePicker from 'expo-image-picker';
142
+ import { createScreenshotAdapter, type PickImageFn } from '@innspel/react-native';
143
+
144
+ const pickImage: PickImageFn = async () => {
145
+ const r = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ['images'], exif: false });
146
+ return r.canceled ? null : { uri: r.assets[0]!.uri, width: r.assets[0]!.width, height: r.assets[0]!.height };
147
+ };
148
+
149
+ // Begge veiene:
150
+ <InnspelProvider client={innspel} screenshot={createScreenshotAdapter(captureRef, { pickImage })}>
151
+ // Bare «Velg bilde» — skjermen bak arket fanges aldri:
152
+ <InnspelProvider client={innspel} screenshot={createScreenshotAdapter(captureRef, { pickImage, captureScreen: false })}>
153
+ ```
154
+
155
+ `captureRef` trengs fortsatt: forhåndsvisningen med maskene fanges på nytt, og det er
156
+ komposittet som lastes opp — aldri fila fra galleriet. Systemets bildevelger trenger ingen
157
+ tillatelse til biblioteket på Android 13+ og iOS. `<InnspelPrivate>` kan ikke gjelde et
158
+ bilde fra et annet sted, og forhåndsvisningen sier det til innsenderen.
159
+
160
+ **Din egen setning ved bildesteget.** Du kjenner brukerne dine og hva som er greit å legge
161
+ ved; plattformen eier ikke den setningen. Den står ved knappene og i forhåndsvisningen:
162
+
163
+ ```tsx
164
+ <InnspelProvider
165
+ client={innspel}
166
+ screenshot={adapter}
167
+ imageStepNote={{
168
+ feil: 'Legg ved et skjermbilde av feilen. Ikke send bilder av andre personer.',
169
+ onske: 'Vis gjerne hvor i appen du savner det.',
170
+ }}
171
+ >
172
+ ```
173
+
174
+ En streng gjelder begge spor. Et spor uten setning viser ingen.
175
+
135
176
  Uten `screenshot` finnes ikke steget — ikke som en grå knapp, det finnes ikke.
136
177
  Det samme gjelder når leietakeren har skjermbilde av (`screenshotsEnabled: false`,
137
178
  tvunget ved mindreårige).
package/dist/index.cjs CHANGED
@@ -46,15 +46,25 @@ var import_react_native10 = require("react-native");
46
46
  // src/skjermbilde.ts
47
47
  var import_react = require("react");
48
48
  var import_react_native = require("react-native");
49
- function createScreenshotAdapter(captureRef) {
49
+ function storrelse(uri) {
50
+ return new Promise((resolve, reject) => import_react_native.Image.getSize(uri, (w, h) => resolve({ width: w, height: h }), reject));
51
+ }
52
+ function createScreenshotAdapter(captureRef, options = {}) {
53
+ const { pickImage, captureScreen } = options;
50
54
  return {
51
- async capture(ref, options) {
52
- const uri = await captureRef(ref, { format: options.format, quality: options.quality, result: "tmpfile" });
53
- const { width, height } = await new Promise(
54
- (resolve, reject) => import_react_native.Image.getSize(uri, (w, h) => resolve({ width: w, height: h }), reject)
55
- );
56
- return { uri, width, height };
55
+ async capture(ref, opts) {
56
+ const uri = await captureRef(ref, { format: opts.format, quality: opts.quality, result: "tmpfile" });
57
+ return { uri, ...await storrelse(uri) };
57
58
  },
59
+ ...pickImage ? {
60
+ async pick() {
61
+ const valgt = await pickImage();
62
+ if (!valgt) return null;
63
+ const { width, height } = valgt.width && valgt.height ? { width: valgt.width, height: valgt.height } : await storrelse(valgt.uri);
64
+ return { uri: valgt.uri, width, height };
65
+ }
66
+ } : {},
67
+ ...captureScreen === false ? { captureScreen: false } : {},
58
68
  measure(ref) {
59
69
  return new Promise((resolve) => {
60
70
  const view = ref.current;
@@ -538,6 +548,10 @@ var tekst = {
538
548
  // 2f — skjermbilde (K4, DD-39, DD-40, DD-42)
539
549
  leggVedSkjermbilde: "Legg ved skjermbilde",
540
550
  leggVedHint: "Bildet tas f\xF8rst n\xE5r du trykker \u2014 du ser det f\xF8r det sendes.",
551
+ velgBilde: "Velg bilde",
552
+ velgBildeHint: "Fra bildene dine. Du ser det og kan maskere det f\xF8r det sendes.",
553
+ skjermbildeUnderGalleri: "Bildet er fra galleriet. Appen maskerer ingenting i det for deg \u2014 mal over det som ikke skal med.",
554
+ bildeFeilet: "kunne ikke hente bildet. Du kan sende uten.",
541
555
  skjermbildeTittel: "Se over bildet",
542
556
  skjermbildeUnder: "Bildet ble tatt da du trykket. Det du maskerer, ser vi aldri.",
543
557
  maskering: "Maskering",
@@ -594,6 +608,8 @@ var tekst = {
594
608
  mineTom: "Du har ikke sendt noe enn\xE5.",
595
609
  mineIkkeSendt: "Ikke sendt enn\xE5",
596
610
  mineSak: (nr) => `Sak ${nr}`,
611
+ // Leietakeren fjernet vedlegget. Bare AT — aldri hvorfor, av hvem eller når.
612
+ bildeFjernet: "Bildet er fjernet.",
597
613
  svarFra: (leietaker) => `Svar fra ${leietaker}`,
598
614
  svarFraGenerisk: "Svar",
599
615
  sendNytt: "Send nytt innspill",
@@ -693,6 +709,8 @@ function Felt({
693
709
  onSend,
694
710
  sender,
695
711
  onSkjermbilde,
712
+ onVelgBilde,
713
+ bildeNotat,
696
714
  harVedlegg,
697
715
  onFjernVedlegg
698
716
  }) {
@@ -883,17 +901,31 @@ function Felt({
883
901
  )
884
902
  ]
885
903
  }
886
- ) : onSkjermbilde ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
887
- Btn,
888
- {
889
- variant: "secondary",
890
- testID: "legg-ved-skjermbilde",
891
- ikon: "\u{1F4F7}",
892
- label: tekst.leggVedSkjermbilde,
893
- hint: tekst.leggVedHint,
894
- onPress: onSkjermbilde
895
- }
896
- ) : null,
904
+ ) : onSkjermbilde || onVelgBilde ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native3.View, { style: { gap: tema.space.sm }, children: [
905
+ onSkjermbilde ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
906
+ Btn,
907
+ {
908
+ variant: "secondary",
909
+ testID: "legg-ved-skjermbilde",
910
+ ikon: "\u{1F4F7}",
911
+ label: tekst.leggVedSkjermbilde,
912
+ hint: tekst.leggVedHint,
913
+ onPress: onSkjermbilde
914
+ }
915
+ ) : null,
916
+ onVelgBilde ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
917
+ Btn,
918
+ {
919
+ variant: "secondary",
920
+ testID: "velg-bilde",
921
+ ikon: "\u{1F5BC}",
922
+ label: tekst.velgBilde,
923
+ hint: tekst.velgBildeHint,
924
+ onPress: onVelgBilde
925
+ }
926
+ ) : null,
927
+ bildeNotat ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.View, { testID: "bilde-notat", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Hint, { children: bildeNotat }) }) : null
928
+ ] }) : null,
897
929
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native3.View, { style: { borderTopWidth: tema.hairline, borderTopColor: tema.color.border, paddingTop: tema.space.sm }, children: [
898
930
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
899
931
  import_react_native3.Pressable,
@@ -1266,6 +1298,10 @@ function MineInnspill({
1266
1298
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StatusPill, { ikon: v.ikon, etikett: v.etikett, farge: v.farge }),
1267
1299
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Hint, { children: v.lofte })
1268
1300
  ] }),
1301
+ !venter && rad.attachmentRemoved ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native7.View, { testID: `bilde-fjernet-${rad.id}`, style: { flexDirection: "row", gap: tema.space.xs, alignItems: "center" }, children: [
1302
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native7.Text, { accessibilityElementsHidden: true, importantForAccessibility: "no", style: { fontSize: tema.font.small }, children: "\u{1F5D1}" }),
1303
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Hint, { children: tekst.bildeFjernet })
1304
+ ] }) : null,
1269
1305
  svar ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1270
1306
  import_react_native7.View,
1271
1307
  {
@@ -1308,6 +1344,8 @@ function Skjermbilde({
1308
1344
  rotStorrelse,
1309
1345
  previewRef,
1310
1346
  komponerer,
1347
+ kilde = "skjerm",
1348
+ notat,
1311
1349
  onFjern,
1312
1350
  onLeggVed
1313
1351
  }) {
@@ -1400,7 +1438,8 @@ function Skjermbilde({
1400
1438
  };
1401
1439
  const a11y = tekst.skjermbildeA11y(automasker.length, egne.length, allTekst);
1402
1440
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native8.View, { style: { flex: 1, gap: tema.space.md }, children: [
1403
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(H, { sub: tekst.skjermbildeUnder, children: tekst.skjermbildeTittel }),
1441
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(H, { sub: kilde === "galleri" ? tekst.skjermbildeUnderGalleri : tekst.skjermbildeUnder, children: tekst.skjermbildeTittel }),
1442
+ notat ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native8.View, { testID: "bilde-notat", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Hint, { children: notat }) }) : null,
1404
1443
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native8.View, { accessibilityRole: "toolbar", accessibilityLabel: tekst.maskering, style: { flexDirection: "row", gap: tema.space.sm }, children: [
1405
1444
  knapp("felt", "\u25A3", tekst.maskerFelt),
1406
1445
  knapp("pensel", "\u270E", tekst.tegnOver),
@@ -1505,7 +1544,8 @@ function Widget({
1505
1544
  synlig,
1506
1545
  apneOpsjoner,
1507
1546
  onLukk,
1508
- tenantName
1547
+ tenantName,
1548
+ imageStepNote
1509
1549
  }) {
1510
1550
  const tema = useTema();
1511
1551
  const insets = (0, import_react10.useContext)(import_react_native_safe_area_context.SafeAreaInsetsContext) ?? import_react_native_safe_area_context.initialWindowMetrics?.insets ?? { top: 0, bottom: 0, left: 0, right: 0 };
@@ -1528,9 +1568,13 @@ function Widget({
1528
1568
  const [automasker, setAutomasker] = (0, import_react10.useState)([]);
1529
1569
  const [rotStorrelse, setRotStorrelse] = (0, import_react10.useState)({ width: 1, height: 1 });
1530
1570
  const [komponerer, setKomponerer] = (0, import_react10.useState)(false);
1571
+ const [kilde, setKilde] = (0, import_react10.useState)("skjerm");
1531
1572
  const [vedlegg, setVedlegg] = (0, import_react10.useState)();
1532
1573
  const [innstillinger, setInnstillinger] = (0, import_react10.useState)({ ...client.settings });
1533
1574
  const skjermbildeMulig = innstillinger.screenshotsEnabled && skjermbilde?.adapter != null;
1575
+ const fangstMulig = skjermbildeMulig && skjermbilde?.adapter?.captureScreen !== false;
1576
+ const galleriMulig = skjermbildeMulig && typeof skjermbilde?.adapter?.pick === "function";
1577
+ const bildeNotat = typeof imageStepNote === "string" ? imageStepNote : imageStepNote?.[spor];
1534
1578
  const nullstill = (0, import_react10.useCallback)(() => {
1535
1579
  setSteg("valg");
1536
1580
  setSpor("feil");
@@ -1607,11 +1651,28 @@ function Widget({
1607
1651
  setAutomasker([]);
1608
1652
  }
1609
1653
  setBilde(fangst);
1654
+ setKilde("skjerm");
1610
1655
  setSteg("skjermbilde");
1611
1656
  } catch {
1612
1657
  setFeil(tekst.skjermbildeFeilet);
1613
1658
  }
1614
1659
  }, [skjermbilde]);
1660
+ const velgBilde = (0, import_react10.useCallback)(async () => {
1661
+ const pick = skjermbilde?.adapter?.pick;
1662
+ if (!pick) return;
1663
+ setFeil(void 0);
1664
+ try {
1665
+ const valgt = await pick.call(skjermbilde.adapter);
1666
+ if (!valgt) return;
1667
+ setRotStorrelse({ width: valgt.width, height: valgt.height });
1668
+ setAutomasker([]);
1669
+ setBilde(valgt);
1670
+ setKilde("galleri");
1671
+ setSteg("skjermbilde");
1672
+ } catch {
1673
+ setFeil(tekst.bildeFeilet);
1674
+ }
1675
+ }, [skjermbilde]);
1615
1676
  const leggVedBilde = (0, import_react10.useCallback)(async () => {
1616
1677
  if (!skjermbilde?.adapter) return;
1617
1678
  setKomponerer(true);
@@ -1775,7 +1836,9 @@ function Widget({
1775
1836
  funn,
1776
1837
  onSend: send,
1777
1838
  sender,
1778
- ...skjermbildeMulig ? { onSkjermbilde: taSkjermbilde } : {},
1839
+ ...fangstMulig ? { onSkjermbilde: taSkjermbilde } : {},
1840
+ ...galleriMulig ? { onVelgBilde: velgBilde } : {},
1841
+ ...bildeNotat ? { bildeNotat } : {},
1779
1842
  harVedlegg: vedlegg !== void 0,
1780
1843
  onFjernVedlegg: fjernBilde
1781
1844
  }
@@ -1788,6 +1851,8 @@ function Widget({
1788
1851
  rotStorrelse,
1789
1852
  previewRef,
1790
1853
  komponerer,
1854
+ kilde,
1855
+ ...bildeNotat ? { notat: bildeNotat } : {},
1791
1856
  onFjern: fjernBilde,
1792
1857
  onLeggVed: leggVedBilde
1793
1858
  }
@@ -1835,7 +1900,8 @@ function InnspelProvider({
1835
1900
  theme,
1836
1901
  colorScheme = "dark",
1837
1902
  tenantName,
1838
- screenshot
1903
+ screenshot,
1904
+ imageStepNote
1839
1905
  }) {
1840
1906
  const [synlig, setSynlig] = (0, import_react11.useState)(false);
1841
1907
  const [apneOpsjoner, setApneOpsjoner] = (0, import_react11.useState)();
@@ -1869,6 +1935,7 @@ function InnspelProvider({
1869
1935
  synlig,
1870
1936
  ...apneOpsjoner ? { apneOpsjoner } : {},
1871
1937
  ...tenantName ? { tenantName } : {},
1938
+ ...imageStepNote ? { imageStepNote } : {},
1872
1939
  onLukk: close
1873
1940
  }
1874
1941
  )