@innspel/react-native 0.9.4 → 0.10.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
@@ -139,7 +139,7 @@ innsenderen alt ser i kvitteringen. «Ja, jeg også» på en kjent feil gir ikke
139
139
  | 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. |
140
140
  | 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. |
141
141
  | Kvittering | Lover noe konkret, per spor, med saksnummer. Ber aldri om en vurdering av appen. Feilet bildet, sier den det — saken er sendt uansett. |
142
- | Mine innspill | Status, løftet som hører til, og svaret fra deg under saken. Det som venter på nett står øverst. |
142
+ | Mine innspill | Status, løftet som hører til, og svaret fra deg under saken. Det som venter på nett står øverst. «Legg til mer» på en åpen sak: ett felt i kortet, maks tre tillegg, aldri et svar på svaret — tilleggene står under innsenderens egen tekst, og i panelet på saken. |
143
143
 
144
144
  ## Offline
145
145
 
package/dist/index.cjs CHANGED
@@ -392,7 +392,7 @@ function useTema() {
392
392
  }
393
393
 
394
394
  // src/widget/Widget.tsx
395
- var import_core2 = require("@innspel/core");
395
+ var import_core3 = require("@innspel/core");
396
396
  var import_react11 = require("react");
397
397
  var import_react_native9 = require("react-native");
398
398
  var import_react_native_safe_area_context = require("react-native-safe-area-context");
@@ -401,6 +401,9 @@ var import_react_native_safe_area_context = require("react-native-safe-area-cont
401
401
  var import_react3 = require("react");
402
402
  var import_react_native_svg = require("react-native-svg");
403
403
 
404
+ // ../../node_modules/ionicons/dist/svg/add-outline.svg
405
+ var add_outline_default = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="ionicon"><path d="M256 112v288M400 256H112" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32px"/></svg>';
406
+
404
407
  // ../../node_modules/ionicons/dist/svg/arrow-back-outline.svg
405
408
  var arrow_back_outline_default = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="ionicon"><path d="M244 400 100 256l144-144M120 256h292" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="48px"/></svg>';
406
409
 
@@ -496,6 +499,7 @@ var import_jsx_runtime = require("react/jsx-runtime");
496
499
  var utenTittel = (svg) => svg.replace(/<title>[\s\S]*?<\/title>/g, "");
497
500
  var IKONER = Object.fromEntries(
498
501
  Object.entries({
502
+ "add-outline": add_outline_default,
499
503
  "arrow-back-outline": arrow_back_outline_default,
500
504
  "brush-outline": brush_outline_default,
501
505
  // Fylte varianter: bare på sporvalgets to kort (DD-63), der ikonet bærer stemningen.
@@ -906,6 +910,17 @@ var tekst = {
906
910
  bildeFjernet: "Bildet er fjernet.",
907
911
  svarFra: (leietaker) => `Svar fra ${leietaker}`,
908
912
  svarFraGenerisk: "Svar",
913
+ // DD-69 — «Legg til mer»: innsenderens egne ord, ikke et svar på svaret.
914
+ leggTilMer: "Legg til mer",
915
+ leggTilMerLabel: "Legg til mer p\xE5 denne saken",
916
+ leggTilMerPlaceholder: "Hva vil du legge til?",
917
+ leggTilMerHint: "Blir st\xE5ende p\xE5 saken din, under det du alt har skrevet. Ikke et svar p\xE5 svaret \u2014 det blir ingen samtale her.",
918
+ avbryt: "Avbryt",
919
+ duLaTil: (dato) => `Du la til \xB7 ${dato}`,
920
+ lagtTil: "Lagt til",
921
+ tilleggTak: "Du har lagt til tre ganger. Har du mer, send et nytt innspill.",
922
+ tilleggLukket: "Saken er avsluttet \u2014 send et nytt innspill.",
923
+ tilleggFeil: (arsak) => `Kunne ikke legge til: ${arsak} Teksten din er beholdt.`,
909
924
  sendNytt: "Send nytt innspill",
910
925
  /** Tom tilstand: det finnes ikke noe «nytt» å sende når ingenting er sendt. */
911
926
  sendForste: "Send innspill",
@@ -1556,6 +1571,7 @@ function Kvittering({
1556
1571
  }
1557
1572
 
1558
1573
  // src/widget/MineInnspill.tsx
1574
+ var import_core = require("@innspel/core");
1559
1575
  var import_react9 = require("react");
1560
1576
  var import_react_native7 = require("react-native");
1561
1577
 
@@ -1586,9 +1602,38 @@ function MineInnspill({
1586
1602
  laster,
1587
1603
  tenantName,
1588
1604
  onNytt,
1589
- storage
1605
+ storage,
1606
+ leggTil,
1607
+ onOppdatert,
1608
+ minTextLength,
1609
+ textMaxLength
1590
1610
  }) {
1591
1611
  const tema = useTema();
1612
+ const min = minTextLength ?? 10;
1613
+ const maks = textMaxLength ?? 280;
1614
+ const [apen, setApen] = (0, import_react9.useState)(null);
1615
+ const [utkast, setUtkast] = (0, import_react9.useState)("");
1616
+ const [sender, setSender] = (0, import_react9.useState)(false);
1617
+ const [feil, setFeil] = (0, import_react9.useState)();
1618
+ const [lagtTil, setLagtTil] = (0, import_react9.useState)();
1619
+ const send = async (id) => {
1620
+ if (!leggTil) return;
1621
+ setSender(true);
1622
+ setFeil(void 0);
1623
+ try {
1624
+ const sak = await leggTil(id, utkast.trim());
1625
+ onOppdatert?.(sak);
1626
+ setApen(null);
1627
+ setUtkast("");
1628
+ setLagtTil(id);
1629
+ } catch (e) {
1630
+ const kode = e instanceof import_core.InnspelApiError ? e.code : void 0;
1631
+ const tekstFeil = kode === "addendum_limit_reached" ? tekst.tilleggTak : kode === "submission_closed" ? tekst.tilleggLukket : tekst.tilleggFeil(e instanceof Error && e.message ? e.message : "ukjent feil.");
1632
+ setFeil({ id, tekst: tekstFeil });
1633
+ } finally {
1634
+ setSender(false);
1635
+ }
1636
+ };
1592
1637
  const [visning, setVisning] = (0, import_react9.useState)(() => rader.map((rad) => ({ rad, nytt: false })));
1593
1638
  (0, import_react9.useEffect)(() => {
1594
1639
  let gjelder = true;
@@ -1610,7 +1655,16 @@ function MineInnspill({
1610
1655
  const venter = rad.pending === true;
1611
1656
  const v = venter ? venterVisning : statusVisning(rad.status, rad.type, rad.deadlineAt);
1612
1657
  const nokkel = venter ? rad.clientRef : rad.id;
1658
+ const sakId = venter ? void 0 : rad.id;
1613
1659
  const svar = venter ? void 0 : rad.reply;
1660
+ const tillegg = venter ? [] : rad.addenda ?? [];
1661
+ const kanLeggeTil = !venter && !!leggTil && rad.canAddAddendum === true;
1662
+ const taketN\u00E5dd = !venter && rad.canAddAddendum === false && tillegg.length >= 3;
1663
+ const feltApent = sakId !== void 0 && apen === sakId;
1664
+ const brukt = utkast.length;
1665
+ const forKort = brukt < min;
1666
+ const full = brukt >= maks;
1667
+ const visTeller = brukt >= maks * 0.8;
1614
1668
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Card, { dashed: venter, children: [
1615
1669
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native7.View, { style: { flexDirection: "row", alignItems: "center", gap: tema.space.xs, flexWrap: "wrap" }, children: [
1616
1670
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Hint, { children: venter ? `${tekst.mineIkkeSendt} \xB7 ${rad.type === "onske" ? tekst.onske : tekst.feil}` : tekst.sakHode(rad.type === "onske" ? tekst.onske : tekst.feil, rad.id) }),
@@ -1641,10 +1695,123 @@ function MineInnspill({
1641
1695
  children: rad.text
1642
1696
  }
1643
1697
  ),
1698
+ tillegg.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native7.View, { testID: `tillegg-${sakId}`, style: { gap: tema.space.xs }, children: tillegg.map((a) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1699
+ import_react_native7.View,
1700
+ {
1701
+ style: {
1702
+ borderLeftWidth: tema.hairline * 2,
1703
+ borderLeftColor: tema.color.border,
1704
+ paddingLeft: tema.space.sm,
1705
+ gap: tema.space.xs
1706
+ },
1707
+ children: [
1708
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native7.View, { style: { flexDirection: "row", alignItems: "center", gap: tema.space.xs }, children: [
1709
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Ikon, { navn: "add-outline", storrelse: "minste", farge: tema.color.textSecondary }),
1710
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native7.Text, { style: { color: tema.color.textSecondary, fontSize: tema.font.small }, children: tekst.duLaTil(formaterFrist(a.createdAt)) })
1711
+ ] }),
1712
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native7.Text, { style: { color: tema.color.textPrimary, fontSize: tema.font.small, lineHeight: tema.lineHeight.small }, children: a.text })
1713
+ ]
1714
+ },
1715
+ a.id
1716
+ )) }) : null,
1644
1717
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native7.View, { style: { gap: tema.space.xs }, children: [
1645
1718
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(StatusPill, { ikon: v.ikon, etikett: v.etikett, fg: v.fg, bg: v.bg }),
1646
1719
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Hint, { children: v.lofte })
1647
1720
  ] }),
1721
+ sakId !== void 0 && lagtTil === sakId ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native7.Text, { accessibilityLiveRegion: "polite", testID: `lagt-til-${sakId}`, style: { color: tema.color.success, fontSize: tema.font.small }, children: tekst.lagtTil }) : null,
1722
+ feltApent ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native7.View, { testID: `tillegg-felt-${sakId}`, style: { gap: tema.space.sm }, children: [
1723
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native7.Text, { style: { color: tema.color.textSecondary, fontSize: tema.font.small }, children: tekst.leggTilMerLabel }),
1724
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1725
+ import_react_native7.TextInput,
1726
+ {
1727
+ testID: "tillegg-tekst",
1728
+ accessibilityLabel: tekst.leggTilMerLabel,
1729
+ value: utkast,
1730
+ onChangeText: setUtkast,
1731
+ maxLength: maks,
1732
+ multiline: true,
1733
+ numberOfLines: 3,
1734
+ autoFocus: true,
1735
+ placeholder: tekst.leggTilMerPlaceholder,
1736
+ placeholderTextColor: tema.color.textMuted,
1737
+ style: {
1738
+ backgroundColor: tema.color.surfaceRaised,
1739
+ borderWidth: tema.hairline,
1740
+ borderColor: full ? tema.color.warning : tema.color.borderStrong,
1741
+ borderRadius: tema.radius.md,
1742
+ padding: tema.space.md,
1743
+ color: tema.color.textPrimary,
1744
+ fontSize: tema.font.body,
1745
+ lineHeight: tema.lineHeight.body,
1746
+ minHeight: tema.touch * 2,
1747
+ textAlignVertical: "top"
1748
+ }
1749
+ }
1750
+ ),
1751
+ visTeller ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1752
+ import_react_native7.Text,
1753
+ {
1754
+ testID: "tillegg-teller",
1755
+ accessibilityLiveRegion: full ? "assertive" : "polite",
1756
+ style: { alignSelf: "flex-end", color: full ? tema.color.warning : tema.color.textMuted, fontSize: tema.font.small },
1757
+ children: [
1758
+ brukt,
1759
+ " / ",
1760
+ maks,
1761
+ full ? tekst.tellerFull : ""
1762
+ ]
1763
+ }
1764
+ ) : null,
1765
+ feil?.id === sakId ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native7.Text, { accessibilityLiveRegion: "assertive", testID: `tillegg-feil-${sakId}`, style: { color: tema.color.danger, fontSize: tema.font.small, lineHeight: tema.lineHeight.small }, children: feil.tekst }) : null,
1766
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native7.View, { style: { flexDirection: "row", gap: tema.space.sm }, children: [
1767
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1768
+ Btn,
1769
+ {
1770
+ testID: "tillegg-send",
1771
+ label: sender ? tekst.sender : tekst.send,
1772
+ disabled: forKort || sender,
1773
+ busy: sender,
1774
+ ...forKort ? { hint: tekst.forKort(min) } : {},
1775
+ onPress: () => sakId !== void 0 && void send(sakId),
1776
+ grow: true
1777
+ }
1778
+ ),
1779
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1780
+ Btn,
1781
+ {
1782
+ testID: "tillegg-avbryt",
1783
+ label: tekst.avbryt,
1784
+ variant: "secondary",
1785
+ onPress: () => {
1786
+ setApen(null);
1787
+ setUtkast("");
1788
+ setFeil(void 0);
1789
+ },
1790
+ grow: true
1791
+ }
1792
+ )
1793
+ ] }),
1794
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Hint, { children: tekst.leggTilMerHint })
1795
+ ] }) : kanLeggeTil ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1796
+ import_react_native7.Pressable,
1797
+ {
1798
+ testID: `legg-til-mer-${sakId}`,
1799
+ accessibilityRole: "button",
1800
+ accessibilityLabel: tekst.leggTilMerLabel,
1801
+ onPress: () => {
1802
+ if (sakId === void 0) return;
1803
+ setApen(sakId);
1804
+ setUtkast("");
1805
+ setFeil(void 0);
1806
+ setLagtTil(void 0);
1807
+ },
1808
+ style: { alignSelf: "flex-start", minHeight: tema.touch, flexDirection: "row", alignItems: "center", gap: tema.space.xs, paddingHorizontal: tema.space.sm },
1809
+ children: [
1810
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Ikon, { navn: "add-outline", storrelse: "minste", farge: tema.color.actionSecondaryText }),
1811
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native7.Text, { style: { color: tema.color.actionSecondaryText, fontSize: tema.font.body, fontWeight: tema.weight.semibold }, children: tekst.leggTilMer })
1812
+ ]
1813
+ }
1814
+ ) : taketN\u00E5dd ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Hint, { children: tekst.tilleggTak }) : null,
1648
1815
  !venter && rad.attachmentRemoved ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native7.View, { testID: `bilde-fjernet-${rad.id}`, style: { flexDirection: "row", gap: tema.space.xs, alignItems: "center" }, children: [
1649
1816
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Ikon, { navn: "trash-outline", storrelse: "minste", farge: tema.color.textMuted }),
1650
1817
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Hint, { children: tekst.bildeFjernet })
@@ -1681,7 +1848,7 @@ function MineInnspill({
1681
1848
  }
1682
1849
 
1683
1850
  // src/widget/Skjermbilde.tsx
1684
- var import_core = require("@innspel/core");
1851
+ var import_core2 = require("@innspel/core");
1685
1852
  var import_react10 = require("react");
1686
1853
  var import_react_native8 = require("react-native");
1687
1854
  var import_jsx_runtime8 = require("react/jsx-runtime");
@@ -1708,7 +1875,7 @@ function Skjermbilde({
1708
1875
  return { width: Math.floor(bilde.width * s), height: Math.floor(bilde.height * s) };
1709
1876
  }, [ramme, bilde.width, bilde.height]);
1710
1877
  const automasker = (0, import_react10.useMemo)(
1711
- () => visning ? automaskerRot.map((r) => (0, import_core.skaler)(r, rotStorrelse, visning)) : [],
1878
+ () => visning ? automaskerRot.map((r) => (0, import_core2.skaler)(r, rotStorrelse, visning)) : [],
1712
1879
  [automaskerRot, rotStorrelse, visning]
1713
1880
  );
1714
1881
  const onLayout = (0, import_react10.useCallback)((e) => {
@@ -1719,13 +1886,13 @@ function Skjermbilde({
1719
1886
  const onGrant = (e) => {
1720
1887
  if (!visning) return;
1721
1888
  const p = punkt(e);
1722
- const treff = egne.findIndex((r) => (0, import_core.treffer)(p, r));
1889
+ const treff = egne.findIndex((r) => (0, import_core2.treffer)(p, r));
1723
1890
  if (treff >= 0) {
1724
1891
  setEgne((x) => x.filter((_, i) => i !== treff));
1725
1892
  return;
1726
1893
  }
1727
1894
  if (verktoy === "felt") {
1728
- const r = (0, import_core.trykkTilRekt)(p, { width: tema.touch * 4, height: tema.touch }, visning);
1895
+ const r = (0, import_core2.trykkTilRekt)(p, { width: tema.touch * 4, height: tema.touch }, visning);
1729
1896
  if (r) setEgne((x) => [...x, r]);
1730
1897
  return;
1731
1898
  }
@@ -1737,7 +1904,7 @@ function Skjermbilde({
1737
1904
  };
1738
1905
  const onRelease = () => {
1739
1906
  if (verktoy !== "pensel" || !visning || strok.current.length === 0) return;
1740
- const rekter = (0, import_core.strokTilRekter)(strok.current, screenRules.brushWidth, visning);
1907
+ const rekter = (0, import_core2.strokTilRekter)(strok.current, screenRules.brushWidth, visning);
1741
1908
  strok.current = [];
1742
1909
  if (rekter.length) setEgne((x) => [...x, ...rekter]);
1743
1910
  };
@@ -2138,8 +2305,8 @@ function Widget({
2138
2305
  setSteg("kvittering");
2139
2306
  onSubmitted?.(res.status === "queued" ? { type: spor, queued: true } : { type: spor, queued: false, id: res.submission.id });
2140
2307
  } catch (e) {
2141
- if (e instanceof import_core2.InnspelQueueFullError) setFeil(e.userMessage);
2142
- else if (e instanceof import_core2.InnspelApiError) setFeil(e.message);
2308
+ if (e instanceof import_core3.InnspelQueueFullError) setFeil(e.userMessage);
2309
+ else if (e instanceof import_core3.InnspelApiError) setFeil(e.message);
2143
2310
  else setFeil("tjeneren svarte ikke.");
2144
2311
  } finally {
2145
2312
  setSender(false);
@@ -2153,7 +2320,7 @@ function Widget({
2153
2320
  setKvittering({ art: "jaOgsa", nr: res.id });
2154
2321
  setSteg("kvittering");
2155
2322
  } catch (e) {
2156
- setFeil(e instanceof import_core2.InnspelApiError ? e.message : "tjeneren svarte ikke.");
2323
+ setFeil(e instanceof import_core3.InnspelApiError ? e.message : "tjeneren svarte ikke.");
2157
2324
  } finally {
2158
2325
  setSender(false);
2159
2326
  }
@@ -2318,7 +2485,11 @@ function Widget({
2318
2485
  rader: mine,
2319
2486
  laster: lasterMine,
2320
2487
  ...tenantName ? { tenantName } : {},
2321
- onNytt: nyttFraMine
2488
+ onNytt: nyttFraMine,
2489
+ leggTil: (id, text) => client.subjects.addAddendum(id, text),
2490
+ onOppdatert: (sak) => setMine((liste) => liste.map((r) => r.pending !== true && r.id === sak.id ? sak : r)),
2491
+ minTextLength: innstillinger.minTextLength,
2492
+ textMaxLength: innstillinger.textMaxLength
2322
2493
  }
2323
2494
  ) : null
2324
2495
  ]
@@ -2426,7 +2597,7 @@ function InnspelPrivate({ children, style }) {
2426
2597
  }
2427
2598
 
2428
2599
  // src/program/InnspelProgram.tsx
2429
- var import_core3 = require("@innspel/core");
2600
+ var import_core4 = require("@innspel/core");
2430
2601
  var import_react18 = require("react");
2431
2602
  var import_react_native16 = require("react-native");
2432
2603
 
@@ -2874,7 +3045,7 @@ function InnspelProgram({
2874
3045
  }
2875
3046
  setDialog(void 0);
2876
3047
  } catch (e) {
2877
- setSvarFeil(e instanceof import_core3.InnspelApiError ? e.message : "tjeneren svarte ikke.");
3048
+ setSvarFeil(e instanceof import_core4.InnspelApiError ? e.message : "tjeneren svarte ikke.");
2878
3049
  } finally {
2879
3050
  setSender(false);
2880
3051
  }
@@ -2903,7 +3074,7 @@ function InnspelProgram({
2903
3074
  setViserAksept(false);
2904
3075
  onEndring?.(ny);
2905
3076
  } catch (e) {
2906
- setFeil(e instanceof import_core3.InnspelApiError ? e.message : "tjeneren svarte ikke.");
3077
+ setFeil(e instanceof import_core4.InnspelApiError ? e.message : "tjeneren svarte ikke.");
2907
3078
  } finally {
2908
3079
  setSender(false);
2909
3080
  }
@@ -2917,7 +3088,7 @@ function InnspelProgram({
2917
3088
  setViserGaaUt(false);
2918
3089
  onEndring?.(null);
2919
3090
  } catch (e) {
2920
- setFeil(e instanceof import_core3.InnspelApiError ? e.message : "tjeneren svarte ikke.");
3091
+ setFeil(e instanceof import_core4.InnspelApiError ? e.message : "tjeneren svarte ikke.");
2921
3092
  } finally {
2922
3093
  setSender(false);
2923
3094
  }