@innspel/react-native 0.5.1 → 0.6.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",
@@ -693,6 +707,8 @@ function Felt({
693
707
  onSend,
694
708
  sender,
695
709
  onSkjermbilde,
710
+ onVelgBilde,
711
+ bildeNotat,
696
712
  harVedlegg,
697
713
  onFjernVedlegg
698
714
  }) {
@@ -883,17 +899,31 @@ function Felt({
883
899
  )
884
900
  ]
885
901
  }
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,
902
+ ) : onSkjermbilde || onVelgBilde ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native3.View, { style: { gap: tema.space.sm }, children: [
903
+ onSkjermbilde ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
904
+ Btn,
905
+ {
906
+ variant: "secondary",
907
+ testID: "legg-ved-skjermbilde",
908
+ ikon: "\u{1F4F7}",
909
+ label: tekst.leggVedSkjermbilde,
910
+ hint: tekst.leggVedHint,
911
+ onPress: onSkjermbilde
912
+ }
913
+ ) : null,
914
+ onVelgBilde ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
915
+ Btn,
916
+ {
917
+ variant: "secondary",
918
+ testID: "velg-bilde",
919
+ ikon: "\u{1F5BC}",
920
+ label: tekst.velgBilde,
921
+ hint: tekst.velgBildeHint,
922
+ onPress: onVelgBilde
923
+ }
924
+ ) : null,
925
+ 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
926
+ ] }) : null,
897
927
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native3.View, { style: { borderTopWidth: tema.hairline, borderTopColor: tema.color.border, paddingTop: tema.space.sm }, children: [
898
928
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
899
929
  import_react_native3.Pressable,
@@ -1308,6 +1338,8 @@ function Skjermbilde({
1308
1338
  rotStorrelse,
1309
1339
  previewRef,
1310
1340
  komponerer,
1341
+ kilde = "skjerm",
1342
+ notat,
1311
1343
  onFjern,
1312
1344
  onLeggVed
1313
1345
  }) {
@@ -1400,7 +1432,8 @@ function Skjermbilde({
1400
1432
  };
1401
1433
  const a11y = tekst.skjermbildeA11y(automasker.length, egne.length, allTekst);
1402
1434
  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 }),
1435
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(H, { sub: kilde === "galleri" ? tekst.skjermbildeUnderGalleri : tekst.skjermbildeUnder, children: tekst.skjermbildeTittel }),
1436
+ 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
1437
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native8.View, { accessibilityRole: "toolbar", accessibilityLabel: tekst.maskering, style: { flexDirection: "row", gap: tema.space.sm }, children: [
1405
1438
  knapp("felt", "\u25A3", tekst.maskerFelt),
1406
1439
  knapp("pensel", "\u270E", tekst.tegnOver),
@@ -1505,7 +1538,8 @@ function Widget({
1505
1538
  synlig,
1506
1539
  apneOpsjoner,
1507
1540
  onLukk,
1508
- tenantName
1541
+ tenantName,
1542
+ imageStepNote
1509
1543
  }) {
1510
1544
  const tema = useTema();
1511
1545
  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 +1562,13 @@ function Widget({
1528
1562
  const [automasker, setAutomasker] = (0, import_react10.useState)([]);
1529
1563
  const [rotStorrelse, setRotStorrelse] = (0, import_react10.useState)({ width: 1, height: 1 });
1530
1564
  const [komponerer, setKomponerer] = (0, import_react10.useState)(false);
1565
+ const [kilde, setKilde] = (0, import_react10.useState)("skjerm");
1531
1566
  const [vedlegg, setVedlegg] = (0, import_react10.useState)();
1532
1567
  const [innstillinger, setInnstillinger] = (0, import_react10.useState)({ ...client.settings });
1533
1568
  const skjermbildeMulig = innstillinger.screenshotsEnabled && skjermbilde?.adapter != null;
1569
+ const fangstMulig = skjermbildeMulig && skjermbilde?.adapter?.captureScreen !== false;
1570
+ const galleriMulig = skjermbildeMulig && typeof skjermbilde?.adapter?.pick === "function";
1571
+ const bildeNotat = typeof imageStepNote === "string" ? imageStepNote : imageStepNote?.[spor];
1534
1572
  const nullstill = (0, import_react10.useCallback)(() => {
1535
1573
  setSteg("valg");
1536
1574
  setSpor("feil");
@@ -1607,11 +1645,28 @@ function Widget({
1607
1645
  setAutomasker([]);
1608
1646
  }
1609
1647
  setBilde(fangst);
1648
+ setKilde("skjerm");
1610
1649
  setSteg("skjermbilde");
1611
1650
  } catch {
1612
1651
  setFeil(tekst.skjermbildeFeilet);
1613
1652
  }
1614
1653
  }, [skjermbilde]);
1654
+ const velgBilde = (0, import_react10.useCallback)(async () => {
1655
+ const pick = skjermbilde?.adapter?.pick;
1656
+ if (!pick) return;
1657
+ setFeil(void 0);
1658
+ try {
1659
+ const valgt = await pick.call(skjermbilde.adapter);
1660
+ if (!valgt) return;
1661
+ setRotStorrelse({ width: valgt.width, height: valgt.height });
1662
+ setAutomasker([]);
1663
+ setBilde(valgt);
1664
+ setKilde("galleri");
1665
+ setSteg("skjermbilde");
1666
+ } catch {
1667
+ setFeil(tekst.bildeFeilet);
1668
+ }
1669
+ }, [skjermbilde]);
1615
1670
  const leggVedBilde = (0, import_react10.useCallback)(async () => {
1616
1671
  if (!skjermbilde?.adapter) return;
1617
1672
  setKomponerer(true);
@@ -1775,7 +1830,9 @@ function Widget({
1775
1830
  funn,
1776
1831
  onSend: send,
1777
1832
  sender,
1778
- ...skjermbildeMulig ? { onSkjermbilde: taSkjermbilde } : {},
1833
+ ...fangstMulig ? { onSkjermbilde: taSkjermbilde } : {},
1834
+ ...galleriMulig ? { onVelgBilde: velgBilde } : {},
1835
+ ...bildeNotat ? { bildeNotat } : {},
1779
1836
  harVedlegg: vedlegg !== void 0,
1780
1837
  onFjernVedlegg: fjernBilde
1781
1838
  }
@@ -1788,6 +1845,8 @@ function Widget({
1788
1845
  rotStorrelse,
1789
1846
  previewRef,
1790
1847
  komponerer,
1848
+ kilde,
1849
+ ...bildeNotat ? { notat: bildeNotat } : {},
1791
1850
  onFjern: fjernBilde,
1792
1851
  onLeggVed: leggVedBilde
1793
1852
  }
@@ -1835,7 +1894,8 @@ function InnspelProvider({
1835
1894
  theme,
1836
1895
  colorScheme = "dark",
1837
1896
  tenantName,
1838
- screenshot
1897
+ screenshot,
1898
+ imageStepNote
1839
1899
  }) {
1840
1900
  const [synlig, setSynlig] = (0, import_react11.useState)(false);
1841
1901
  const [apneOpsjoner, setApneOpsjoner] = (0, import_react11.useState)();
@@ -1869,6 +1929,7 @@ function InnspelProvider({
1869
1929
  synlig,
1870
1930
  ...apneOpsjoner ? { apneOpsjoner } : {},
1871
1931
  ...tenantName ? { tenantName } : {},
1932
+ ...imageStepNote ? { imageStepNote } : {},
1872
1933
  onLukk: close
1873
1934
  }
1874
1935
  )