@innspel/react-native 0.9.1 → 0.9.3

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
@@ -73,6 +73,18 @@ innsenderen får se før hun skriver.
73
73
  Utenfor React — en navigasjonshandler, en krasjlytter — bruker du klienten
74
74
  direkte: `innspel.openFeedback({ screen: 'Dørlista' })`.
75
75
 
76
+ Lukking — X, eller Android-tilbake på første steg — beholder det innsenderen har
77
+ skrevet til widgeten åpnes igjen; bare en fullført innsending nullstiller. Åpner du med
78
+ `track` mens det står et utkast, står hun der hun var. `view: 'mine'` legges over
79
+ utkastet, og «Tilbake» i lista lukker.
80
+
81
+ **Legg provideren rundt appen, ikke inni en `ScrollView`.** Arket er en `Modal`, men
82
+ React Natives trykkhåndtering går gjennom React-treet: en `ScrollView` over provideren
83
+ med `keyboardShouldPersistTaps` på standardverdien tar første trykk mens tastaturet er
84
+ oppe, og bruker det til å lukke tastaturet. Send teller det trykket likevel; de andre
85
+ knappene i arket gjør ikke. Må provideren stå inni en scrollflate, gi den
86
+ `keyboardShouldPersistTaps="handled"`.
87
+
76
88
  ## Token-endepunktet
77
89
 
78
90
  Det eneste du må bygge selv. Se
package/dist/index.cjs CHANGED
@@ -572,10 +572,12 @@ function Btn({
572
572
  accessibilityLabel,
573
573
  grow,
574
574
  testID,
575
- busy
575
+ busy,
576
+ trykkForbiForelder
576
577
  }) {
577
578
  const tema = useTema();
578
579
  const c = tema.color;
580
+ const forbi = useTrykkForbiForelder(onPress, !!disabled, tema.touch / 2);
579
581
  const bakgrunn = variant === "primary" ? c.actionPrimary : variant === "secondary" ? c.surfaceRaised : "transparent";
580
582
  const tekstfarge = variant === "primary" ? c.onActionPrimary : variant === "danger" ? c.danger : c.actionSecondaryText;
581
583
  const kant = variant === "ghost" ? "transparent" : variant === "primary" ? c.actionPrimary : c.actionSecondaryBorder;
@@ -589,6 +591,7 @@ function Btn({
589
591
  accessibilityState: { disabled: !!disabled, busy: !!busy },
590
592
  disabled,
591
593
  onPress,
594
+ ...trykkForbiForelder ? forbi : {},
592
595
  style: {
593
596
  minHeight: tema.touch,
594
597
  borderRadius: tema.radius.md,
@@ -621,6 +624,37 @@ function Btn({
621
624
  hint ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Hint, { children: hint }) : null
622
625
  ] });
623
626
  }
627
+ function useTrykkForbiForelder(onPress, laast, slingring) {
628
+ const start = (0, import_react4.useRef)(null);
629
+ const fikkResponder = (0, import_react4.useRef)(false);
630
+ const siste = (0, import_react4.useRef)({ onPress, laast });
631
+ siste.current = { onPress, laast };
632
+ return (0, import_react4.useMemo)(
633
+ () => ({
634
+ onPressIn: () => {
635
+ fikkResponder.current = true;
636
+ },
637
+ onTouchStart: (e) => {
638
+ start.current = { x: e.nativeEvent.pageX, y: e.nativeEvent.pageY };
639
+ },
640
+ onTouchEnd: (e) => {
641
+ const s = start.current;
642
+ start.current = null;
643
+ const innenfor = s !== null && Math.abs(e.nativeEvent.pageX - s.x) <= slingring && Math.abs(e.nativeEvent.pageY - s.y) <= slingring;
644
+ setTimeout(() => {
645
+ const tatt = fikkResponder.current;
646
+ fikkResponder.current = false;
647
+ if (!tatt && innenfor && !siste.current.laast) siste.current.onPress?.();
648
+ }, 0);
649
+ },
650
+ onTouchCancel: () => {
651
+ start.current = null;
652
+ fikkResponder.current = false;
653
+ }
654
+ }),
655
+ [slingring]
656
+ );
657
+ }
624
658
  function H({ children, sub }) {
625
659
  const tema = useTema();
626
660
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: { gap: tema.space.xs }, children: [
@@ -868,6 +902,8 @@ var tekst = {
868
902
  mineSak: (nr) => `Sak ${saksnr(nr)}`,
869
903
  /** «Feil · sak 2041» — sporet først, aldri rå id og aldri kontraktens type (DD-50). */
870
904
  sakHode: (spor, nr) => `${stor(spor)} \xB7 sak ${saksnr(nr)}`,
905
+ /** F14: svaret kom siden forrige gang denne enheten viste lista. */
906
+ nyttSvar: "Nytt svar",
871
907
  // Leietakeren fjernet vedlegget. Bare AT — aldri hvorfor, av hvem eller når.
872
908
  bildeFjernet: "Bildet er fjernet.",
873
909
  svarFra: (leietaker) => `Svar fra ${leietaker}`,
@@ -1255,7 +1291,8 @@ function Felt({
1255
1291
  disabled: forKort || sender,
1256
1292
  busy: sender,
1257
1293
  hint: forKort ? tekst.forKort(minTextLength) : harVedlegg ? tekst.sendHintMedBilde : void 0,
1258
- onPress: onSend
1294
+ onPress: onSend,
1295
+ trykkForbiForelder: true
1259
1296
  }
1260
1297
  ) })
1261
1298
  ] });
@@ -1519,23 +1556,78 @@ function Kvittering({
1519
1556
  // src/widget/MineInnspill.tsx
1520
1557
  var import_react9 = require("react");
1521
1558
  var import_react_native7 = require("react-native");
1559
+
1560
+ // src/widget/nyttSvar.ts
1561
+ var N\u00D8KKEL = "innspel:svar-sett";
1562
+ async function merkNyeSvar(rader, storage) {
1563
+ let grense = 0;
1564
+ try {
1565
+ grense = Number.parseInt(await storage.get(N\u00D8KKEL) ?? "", 10) || 0;
1566
+ } catch {
1567
+ grense = 0;
1568
+ }
1569
+ const tid = (r) => r.pending === true ? NaN : Date.parse(r.reply?.at ?? "");
1570
+ const merket = rader.map((rad) => ({ rad, nytt: tid(rad) > grense }));
1571
+ return [...merket.filter((m) => m.nytt), ...merket.filter((m) => !m.nytt)];
1572
+ }
1573
+ async function markerSvarSett(storage, n\u00E5 = Date.now()) {
1574
+ try {
1575
+ await storage.set(N\u00D8KKEL, String(n\u00E5));
1576
+ } catch {
1577
+ }
1578
+ }
1579
+
1580
+ // src/widget/MineInnspill.tsx
1522
1581
  var import_jsx_runtime7 = require("react/jsx-runtime");
1523
1582
  function MineInnspill({
1524
1583
  rader,
1525
1584
  laster,
1526
1585
  tenantName,
1527
- onNytt
1586
+ onNytt,
1587
+ storage
1528
1588
  }) {
1529
1589
  const tema = useTema();
1590
+ const [visning, setVisning] = (0, import_react9.useState)(() => rader.map((rad) => ({ rad, nytt: false })));
1591
+ (0, import_react9.useEffect)(() => {
1592
+ let gjelder = true;
1593
+ void merkNyeSvar(rader, storage).then((m) => {
1594
+ if (gjelder) setVisning(m);
1595
+ }).catch(() => {
1596
+ if (gjelder) setVisning(rader.map((rad) => ({ rad, nytt: false })));
1597
+ });
1598
+ return () => {
1599
+ gjelder = false;
1600
+ };
1601
+ }, [storage, rader]);
1602
+ (0, import_react9.useEffect)(() => {
1603
+ if (!laster && rader.length) void markerSvarSett(storage).catch(() => void 0);
1604
+ }, [storage, laster, rader]);
1530
1605
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native7.View, { style: { flex: 1, gap: tema.space.md }, children: [
1531
1606
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(H, { sub: tekst.mineUnder, children: tekst.mineTittel }),
1532
- laster ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native7.ActivityIndicator, { accessibilityLabel: "Henter innspillene dine", color: tema.color.textSecondary }) : rader.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Hint, { children: tekst.mineTom }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native7.ScrollView, { style: { flex: 1 }, contentContainerStyle: { gap: tema.space.sm }, children: rader.map((rad) => {
1607
+ laster ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native7.ActivityIndicator, { accessibilityLabel: "Henter innspillene dine", color: tema.color.textSecondary }) : rader.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Hint, { children: tekst.mineTom }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native7.ScrollView, { style: { flex: 1 }, contentContainerStyle: { gap: tema.space.sm }, children: visning.map(({ rad, nytt }) => {
1533
1608
  const venter = rad.pending === true;
1534
1609
  const v = venter ? venterVisning : statusVisning(rad.status, rad.type, rad.deadlineAt);
1535
1610
  const nokkel = venter ? rad.clientRef : rad.id;
1536
1611
  const svar = venter ? void 0 : rad.reply;
1537
1612
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Card, { dashed: venter, children: [
1538
- /* @__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) }),
1613
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native7.View, { style: { flexDirection: "row", alignItems: "center", gap: tema.space.xs, flexWrap: "wrap" }, children: [
1614
+ /* @__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) }),
1615
+ nytt ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1616
+ import_react_native7.Text,
1617
+ {
1618
+ testID: "nytt-svar",
1619
+ style: {
1620
+ color: tema.color.info,
1621
+ backgroundColor: tema.color.infoBg,
1622
+ fontSize: tema.font.tiny,
1623
+ fontWeight: tema.weight.semibold,
1624
+ borderRadius: tema.radius.pill,
1625
+ paddingHorizontal: tema.space.sm
1626
+ },
1627
+ children: tekst.nyttSvar
1628
+ }
1629
+ ) : null
1630
+ ] }),
1539
1631
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1540
1632
  import_react_native7.Text,
1541
1633
  {
@@ -1871,9 +1963,23 @@ function Widget({
1871
1963
  setVedlegg(void 0);
1872
1964
  setKomponerer(false);
1873
1965
  }, []);
1966
+ const [mineDirekte, setMineDirekte] = (0, import_react11.useState)(false);
1967
+ const [stegForMine, setStegForMine] = (0, import_react11.useState)("valg");
1968
+ const stegRef = (0, import_react11.useRef)(steg);
1969
+ stegRef.current = steg;
1970
+ const harUtkast = verdi.trim().length > 0 || vedlegg !== void 0;
1874
1971
  const lukk = (0, import_react11.useCallback)(() => {
1972
+ onLukk();
1973
+ if (stegRef.current === "mine" && mineDirekte) {
1974
+ setSteg(stegForMine);
1975
+ setMineDirekte(false);
1976
+ }
1977
+ }, [onLukk, mineDirekte, stegForMine]);
1978
+ const lukkOgNullstill = (0, import_react11.useCallback)(() => {
1875
1979
  onLukk();
1876
1980
  nullstill();
1981
+ setMineDirekte(false);
1982
+ setStegForMine("valg");
1877
1983
  }, [onLukk, nullstill]);
1878
1984
  (0, import_react11.useEffect)(() => {
1879
1985
  if (!synlig) return;
@@ -1883,8 +1989,8 @@ function Widget({
1883
1989
  if (!client.isAnonymous) {
1884
1990
  void client.subjects.submissions().then(setMine).catch(() => void 0);
1885
1991
  }
1886
- if (apneOpsjoner?.view === "mine" && !client.isAnonymous) void visMine();
1887
- else if (apneOpsjoner?.track) velgSpor(apneOpsjoner.track);
1992
+ if (apneOpsjoner?.view === "mine" && !client.isAnonymous) void visMine(true);
1993
+ else if (apneOpsjoner?.track && !harUtkast) void velgSpor(apneOpsjoner.track);
1888
1994
  }, [synlig]);
1889
1995
  const hentKort = (0, import_react11.useCallback)(async () => {
1890
1996
  setLasterKort(true);
@@ -1982,6 +2088,11 @@ function Widget({
1982
2088
  }, []);
1983
2089
  const gaaTilbake = (0, import_react11.useCallback)(() => {
1984
2090
  setFeil(void 0);
2091
+ if (steg === "mine") {
2092
+ if (mineDirekte) lukk();
2093
+ else setSteg("valg");
2094
+ return;
2095
+ }
1985
2096
  if (steg === "skjermbilde") {
1986
2097
  setBilde(void 0);
1987
2098
  setAutomasker([]);
@@ -1993,7 +2104,12 @@ function Widget({
1993
2104
  return;
1994
2105
  }
1995
2106
  setSteg("valg");
1996
- }, [steg, spor, kort.length]);
2107
+ }, [steg, spor, kort.length, mineDirekte, lukk]);
2108
+ const androidTilbake = (0, import_react11.useCallback)(() => {
2109
+ if (steg === "valg") lukk();
2110
+ else if (steg === "kvittering") lukkOgNullstill();
2111
+ else gaaTilbake();
2112
+ }, [steg, lukk, lukkOgNullstill, gaaTilbake]);
1997
2113
  const send = (0, import_react11.useCallback)(async () => {
1998
2114
  setSender(true);
1999
2115
  setFeil(void 0);
@@ -2003,7 +2119,7 @@ function Widget({
2003
2119
  text: verdi,
2004
2120
  ...vedlegg ? { attachment: vedlegg } : {},
2005
2121
  ...spor === "feil" && lenketTil ? { relatedKnownIssueId: lenketTil.id } : {},
2006
- ...fjernede.length ? { context: fjernetKontekst(client, fjernede) } : {}
2122
+ context: fjernetKontekst(client, fjernede, apneOpsjoner?.screen)
2007
2123
  });
2008
2124
  if (res.status === "queued") {
2009
2125
  setKvittering({ art: "k\xF8", ...res.attachment ? { vedlegg: "forkastet" } : {} });
@@ -2024,7 +2140,7 @@ function Widget({
2024
2140
  } finally {
2025
2141
  setSender(false);
2026
2142
  }
2027
- }, [client, spor, verdi, vedlegg, lenketTil, fjernede]);
2143
+ }, [client, spor, verdi, vedlegg, lenketTil, fjernede, apneOpsjoner?.screen]);
2028
2144
  const jaOgsa = (0, import_react11.useCallback)(
2029
2145
  async (k) => {
2030
2146
  setSender(true);
@@ -2040,7 +2156,13 @@ function Widget({
2040
2156
  },
2041
2157
  [client]
2042
2158
  );
2043
- const visMine = (0, import_react11.useCallback)(async () => {
2159
+ const visMine = (0, import_react11.useCallback)(async (direkte = false) => {
2160
+ if (direkte) {
2161
+ if (stegRef.current !== "mine") setStegForMine(stegRef.current);
2162
+ } else {
2163
+ setStegForMine("valg");
2164
+ }
2165
+ setMineDirekte(direkte);
2044
2166
  setSteg("mine");
2045
2167
  setLasterMine(true);
2046
2168
  try {
@@ -2051,13 +2173,18 @@ function Widget({
2051
2173
  setLasterMine(false);
2052
2174
  }
2053
2175
  }, [client]);
2176
+ const nyttFraMine = (0, import_react11.useCallback)(() => {
2177
+ setMineDirekte(false);
2178
+ if (harUtkast) setSteg(stegForMine);
2179
+ else nullstill();
2180
+ }, [harUtkast, stegForMine, nullstill]);
2054
2181
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2055
2182
  import_react_native9.Modal,
2056
2183
  {
2057
2184
  visible: synlig,
2058
2185
  transparent: true,
2059
2186
  animationType: "slide",
2060
- onRequestClose: lukk,
2187
+ onRequestClose: androidTilbake,
2061
2188
  accessibilityViewIsModal: true,
2062
2189
  children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2063
2190
  import_react_native9.KeyboardAvoidingView,
@@ -2095,7 +2222,7 @@ function Widget({
2095
2222
  testID: "lukk",
2096
2223
  accessibilityRole: "button",
2097
2224
  accessibilityLabel: tekst.lukk,
2098
- onPress: lukk,
2225
+ onPress: steg === "kvittering" ? lukkOgNullstill : lukk,
2099
2226
  style: { minWidth: tema.touch, minHeight: tema.touch, alignItems: "center", justifyContent: "center" },
2100
2227
  children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Ikon, { navn: "close-outline", farge: tema.color.textSecondary })
2101
2228
  }
@@ -2113,7 +2240,7 @@ function Widget({
2113
2240
  ikon: "list-outline",
2114
2241
  label: mine.length > 0 ? tekst.mineMedAntall(mine.length) : tekst.mineTittel,
2115
2242
  accessibilityLabel: mine.length > 0 ? tekst.mineEtikett(mine.length) : tekst.mineTittel,
2116
- onPress: visMine
2243
+ onPress: () => void visMine()
2117
2244
  }
2118
2245
  ) : null
2119
2246
  ] }) : null,
@@ -2140,7 +2267,7 @@ function Widget({
2140
2267
  onEndre: endreTekst,
2141
2268
  minTextLength: innstillinger.minTextLength,
2142
2269
  textMaxLength: innstillinger.textMaxLength,
2143
- kontekst: client.context,
2270
+ kontekst: { ...client.context, ...apneOpsjoner?.screen ? { screen: apneOpsjoner.screen } : {} },
2144
2271
  fjernedeFelter: fjernede,
2145
2272
  onFjernKontekst: (f) => setFjernede((x) => [...x, f]),
2146
2273
  ...lenketTil ? { lenketTil, onFjernLenke: () => setLenketTil(void 0) } : {},
@@ -2177,16 +2304,17 @@ function Widget({
2177
2304
  ...kvittering.nr ? { saksnummer: kvittering.nr } : {},
2178
2305
  ...kvittering.vedlegg ? { vedlegg: kvittering.vedlegg } : {},
2179
2306
  ...kvittering.frist ? { frist: kvittering.frist } : {},
2180
- onLukk: lukk
2307
+ onLukk: lukkOgNullstill
2181
2308
  }
2182
2309
  ) : null,
2183
2310
  steg === "mine" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2184
2311
  MineInnspill,
2185
2312
  {
2313
+ storage: client.storage,
2186
2314
  rader: mine,
2187
2315
  laster: lasterMine,
2188
2316
  ...tenantName ? { tenantName } : {},
2189
- onNytt: nullstill
2317
+ onNytt: nyttFraMine
2190
2318
  }
2191
2319
  ) : null
2192
2320
  ]
@@ -2207,9 +2335,10 @@ function useNettstatus(client) {
2207
2335
  }, [c]);
2208
2336
  return online;
2209
2337
  }
2210
- function fjernetKontekst(client, fjernede) {
2338
+ function fjernetKontekst(client, fjernede, skjerm) {
2211
2339
  const ut = {};
2212
- for (const k of Object.keys(client.context)) {
2340
+ if (skjerm) ut["screen"] = skjerm;
2341
+ for (const k of [...Object.keys(client.context), "screen"]) {
2213
2342
  if (k !== "sdkVersion" && fjernede.includes(k)) ut[k] = void 0;
2214
2343
  }
2215
2344
  return ut;