@camstack/ui-library 1.2.192 → 1.2.193
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/composites/clips/clip-view-state.d.ts +11 -0
- package/dist/index.cjs +48 -25
- package/dist/index.js +48 -25
- package/package.json +1 -1
|
@@ -11,6 +11,17 @@ export interface ClipsViewInput {
|
|
|
11
11
|
* will stay `pending` for ever.
|
|
12
12
|
*/
|
|
13
13
|
readonly plan: ClipBandPlan;
|
|
14
|
+
/**
|
|
15
|
+
* The provider the clip list was asked of, or `null` when the camera lists
|
|
16
|
+
* NO source to ask.
|
|
17
|
+
*
|
|
18
|
+
* Naming one is required (D554 amended), so "nobody to ask" is a real state and it
|
|
19
|
+
* has to be told apart from "not answered yet": the query never fires, its
|
|
20
|
+
* status stays `pending` for ever, and a surface that only consults the
|
|
21
|
+
* status spins against a question with no addressee. Exactly the trap
|
|
22
|
+
* `too-wide` has, arriving from the other end.
|
|
23
|
+
*/
|
|
24
|
+
readonly provider: string | null;
|
|
14
25
|
readonly clipsStatus: ClipQueryStatus;
|
|
15
26
|
readonly clips: readonly Clip[];
|
|
16
27
|
readonly sourcesStatus: ClipQueryStatus;
|
package/dist/index.cjs
CHANGED
|
@@ -19593,11 +19593,18 @@ function resolveClipsView(input) {
|
|
|
19593
19593
|
kind: "failed",
|
|
19594
19594
|
message: input.sourcesError ?? "The source list could not be read"
|
|
19595
19595
|
};
|
|
19596
|
-
if (input.clipsStatus === "pending" || input.sourcesStatus === "pending") return { kind: "loading" };
|
|
19597
19596
|
const notices = input.sources.map(describeSource);
|
|
19598
19597
|
const answered = notices.filter((notice) => notice.answered);
|
|
19599
19598
|
const silent = notices.filter((notice) => !notice.answered);
|
|
19600
19599
|
const clips = input.clips;
|
|
19600
|
+
if (input.provider === null && input.sourcesStatus === "success") return {
|
|
19601
|
+
kind: "empty",
|
|
19602
|
+
band: input.plan.band,
|
|
19603
|
+
answered,
|
|
19604
|
+
silent,
|
|
19605
|
+
nobodyAnswered: true
|
|
19606
|
+
};
|
|
19607
|
+
if (input.clipsStatus === "pending" || input.sourcesStatus === "pending") return { kind: "loading" };
|
|
19601
19608
|
if (clips.length === 0) return {
|
|
19602
19609
|
kind: "empty",
|
|
19603
19610
|
band: input.plan.band,
|
|
@@ -19689,23 +19696,34 @@ function resolveClipsView(input) {
|
|
|
19689
19696
|
* ## Why the picker is a SELECT and not a multiselect
|
|
19690
19697
|
*
|
|
19691
19698
|
* The operator asked for *"un multiselect common"* while `listClips` still
|
|
19692
|
-
* took `sources?: string[]`. It now takes ONE `provider` —
|
|
19693
|
-
*
|
|
19694
|
-
*
|
|
19695
|
-
*
|
|
19696
|
-
*
|
|
19697
|
-
*
|
|
19698
|
-
*
|
|
19699
|
-
*
|
|
19699
|
+
* took `sources?: string[]`. It now takes ONE `provider` —
|
|
19700
|
+
* *"l'utilizzatore è uno solo"* — and a multi-pick control over a
|
|
19701
|
+
* one-at-a-time argument offers a sum the hub cannot answer. So the control is
|
|
19702
|
+
* the shared single-choice one, `Select`: the same component the Recordings
|
|
19703
|
+
* page's own camera picker uses, which is what *"segue la nostra UI"* means
|
|
19704
|
+
* here.
|
|
19705
|
+
*
|
|
19706
|
+
* ## The control's value is ALWAYS what the query carries
|
|
19707
|
+
*
|
|
19708
|
+
* There is no "default" option and no empty value. This section used to send
|
|
19709
|
+
* no `provider` at all until the operator chose, because the cap documented
|
|
19710
|
+
* absence as "the device's bound provider, which is CamStack on every camera".
|
|
19711
|
+
* No code implemented that: measured on device 592 (2026-09-20, bound to
|
|
19712
|
+
* `recorder` AND `provider-reolink`) a bare `listClips` with `limit: 3`
|
|
19713
|
+
* answered SIX rows — three per source, merged — while the default option read
|
|
19714
|
+
* "CamStack recordings". The label named one thing and the query asked two.
|
|
19715
|
+
*
|
|
19716
|
+
* Naming a provider is now required (D554 amended), and the default is this surface's
|
|
19717
|
+
* to make: the FIRST source the camera lists, shown selected in the control, so
|
|
19718
|
+
* what is on screen and what was asked are the same fact. A camera that lists
|
|
19719
|
+
* no source is asked NOTHING — the query never fires and the body says nobody
|
|
19720
|
+
* answered, rather than spinning against a question with no addressee.
|
|
19700
19721
|
*
|
|
19701
19722
|
* ## One provider is a fleet STATE
|
|
19702
19723
|
*
|
|
19703
|
-
* `listSources` derives from `deviceManager.getBindings
|
|
19704
|
-
*
|
|
19705
|
-
*
|
|
19706
|
-
* single-entry control would read as a defect; the binding store learning to
|
|
19707
|
-
* hold several providers for a collection cap is what changes this, and
|
|
19708
|
-
* nothing here needs to change with it.
|
|
19724
|
+
* `listSources` derives from `deviceManager.getBindings`. A camera bound to one
|
|
19725
|
+
* provider makes the selector inert, and it SAYS why: an empty or silently
|
|
19726
|
+
* single-entry control would read as a defect.
|
|
19709
19727
|
*/
|
|
19710
19728
|
/** Newest-first, and bounded: a busy day on 592 lists 546 clips. */
|
|
19711
19729
|
var CLIP_PAGE_LIMIT = 500;
|
|
@@ -19820,16 +19838,26 @@ function ClipsSection({ deviceId }) {
|
|
|
19820
19838
|
}), [view, selected]);
|
|
19821
19839
|
const plan = planClipBand(band);
|
|
19822
19840
|
const sources = useVideoclipsListSources({ deviceId });
|
|
19841
|
+
const sourceRows = sources.data ?? [];
|
|
19842
|
+
/**
|
|
19843
|
+
* The provider the list is ASKED of: the operator's pick, else the first
|
|
19844
|
+
* source the camera lists. Empty only while the camera has no source at all
|
|
19845
|
+
* — and then nothing is asked, because there is nobody to ask.
|
|
19846
|
+
*/
|
|
19847
|
+
const askedProvider = (0, react$1.useMemo)(() => {
|
|
19848
|
+
if (provider !== "") return provider;
|
|
19849
|
+
return sourceRows.find((row) => row.addonId !== void 0)?.addonId ?? "";
|
|
19850
|
+
}, [provider, sourceRows]);
|
|
19823
19851
|
const clips = useVideoclipsListClips({
|
|
19824
19852
|
deviceId,
|
|
19825
19853
|
since: band.fromMs,
|
|
19826
19854
|
until: band.toMs,
|
|
19827
19855
|
limit: CLIP_PAGE_LIMIT,
|
|
19828
|
-
|
|
19829
|
-
}, { enabled: plan.kind === "askable" });
|
|
19830
|
-
const sourceRows = sources.data ?? [];
|
|
19856
|
+
provider: askedProvider
|
|
19857
|
+
}, { enabled: plan.kind === "askable" && askedProvider !== "" });
|
|
19831
19858
|
const clipsView = resolveClipsView({
|
|
19832
19859
|
plan,
|
|
19860
|
+
provider: askedProvider === "" ? null : askedProvider,
|
|
19833
19861
|
clipsStatus: clips.status,
|
|
19834
19862
|
clips: clips.data ?? [],
|
|
19835
19863
|
sourcesStatus: sources.status,
|
|
@@ -19847,15 +19875,10 @@ function ClipsSection({ deviceId }) {
|
|
|
19847
19875
|
if (labels === void 0) byAddon.set(addonId, [row.label]);
|
|
19848
19876
|
else labels.push(row.label);
|
|
19849
19877
|
}
|
|
19850
|
-
|
|
19878
|
+
return [...byAddon].map(([value, labels]) => ({
|
|
19851
19879
|
value,
|
|
19852
19880
|
label: labels.join(" · ")
|
|
19853
19881
|
}));
|
|
19854
|
-
const first = rest[0];
|
|
19855
|
-
return [{
|
|
19856
|
-
value: "",
|
|
19857
|
-
label: first === void 0 ? "The camera’s bound provider" : first.label
|
|
19858
|
-
}, ...rest.slice(1)];
|
|
19859
19882
|
}, [sourceRows]);
|
|
19860
19883
|
const tableRows = (0, react$1.useMemo)(() => sourceRows.map((row) => ({
|
|
19861
19884
|
notice: describeSource(row),
|
|
@@ -19933,7 +19956,7 @@ function ClipsSection({ deviceId }) {
|
|
|
19933
19956
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Select, {
|
|
19934
19957
|
id: "clips-provider",
|
|
19935
19958
|
options,
|
|
19936
|
-
value:
|
|
19959
|
+
value: askedProvider,
|
|
19937
19960
|
disabled: options.length < 2,
|
|
19938
19961
|
onChange: (e) => pick(e.target.value)
|
|
19939
19962
|
})
|
package/dist/index.js
CHANGED
|
@@ -19569,11 +19569,18 @@ function resolveClipsView(input) {
|
|
|
19569
19569
|
kind: "failed",
|
|
19570
19570
|
message: input.sourcesError ?? "The source list could not be read"
|
|
19571
19571
|
};
|
|
19572
|
-
if (input.clipsStatus === "pending" || input.sourcesStatus === "pending") return { kind: "loading" };
|
|
19573
19572
|
const notices = input.sources.map(describeSource);
|
|
19574
19573
|
const answered = notices.filter((notice) => notice.answered);
|
|
19575
19574
|
const silent = notices.filter((notice) => !notice.answered);
|
|
19576
19575
|
const clips = input.clips;
|
|
19576
|
+
if (input.provider === null && input.sourcesStatus === "success") return {
|
|
19577
|
+
kind: "empty",
|
|
19578
|
+
band: input.plan.band,
|
|
19579
|
+
answered,
|
|
19580
|
+
silent,
|
|
19581
|
+
nobodyAnswered: true
|
|
19582
|
+
};
|
|
19583
|
+
if (input.clipsStatus === "pending" || input.sourcesStatus === "pending") return { kind: "loading" };
|
|
19577
19584
|
if (clips.length === 0) return {
|
|
19578
19585
|
kind: "empty",
|
|
19579
19586
|
band: input.plan.band,
|
|
@@ -19665,23 +19672,34 @@ function resolveClipsView(input) {
|
|
|
19665
19672
|
* ## Why the picker is a SELECT and not a multiselect
|
|
19666
19673
|
*
|
|
19667
19674
|
* The operator asked for *"un multiselect common"* while `listClips` still
|
|
19668
|
-
* took `sources?: string[]`. It now takes ONE `provider` —
|
|
19669
|
-
*
|
|
19670
|
-
*
|
|
19671
|
-
*
|
|
19672
|
-
*
|
|
19673
|
-
*
|
|
19674
|
-
*
|
|
19675
|
-
*
|
|
19675
|
+
* took `sources?: string[]`. It now takes ONE `provider` —
|
|
19676
|
+
* *"l'utilizzatore è uno solo"* — and a multi-pick control over a
|
|
19677
|
+
* one-at-a-time argument offers a sum the hub cannot answer. So the control is
|
|
19678
|
+
* the shared single-choice one, `Select`: the same component the Recordings
|
|
19679
|
+
* page's own camera picker uses, which is what *"segue la nostra UI"* means
|
|
19680
|
+
* here.
|
|
19681
|
+
*
|
|
19682
|
+
* ## The control's value is ALWAYS what the query carries
|
|
19683
|
+
*
|
|
19684
|
+
* There is no "default" option and no empty value. This section used to send
|
|
19685
|
+
* no `provider` at all until the operator chose, because the cap documented
|
|
19686
|
+
* absence as "the device's bound provider, which is CamStack on every camera".
|
|
19687
|
+
* No code implemented that: measured on device 592 (2026-09-20, bound to
|
|
19688
|
+
* `recorder` AND `provider-reolink`) a bare `listClips` with `limit: 3`
|
|
19689
|
+
* answered SIX rows — three per source, merged — while the default option read
|
|
19690
|
+
* "CamStack recordings". The label named one thing and the query asked two.
|
|
19691
|
+
*
|
|
19692
|
+
* Naming a provider is now required (D554 amended), and the default is this surface's
|
|
19693
|
+
* to make: the FIRST source the camera lists, shown selected in the control, so
|
|
19694
|
+
* what is on screen and what was asked are the same fact. A camera that lists
|
|
19695
|
+
* no source is asked NOTHING — the query never fires and the body says nobody
|
|
19696
|
+
* answered, rather than spinning against a question with no addressee.
|
|
19676
19697
|
*
|
|
19677
19698
|
* ## One provider is a fleet STATE
|
|
19678
19699
|
*
|
|
19679
|
-
* `listSources` derives from `deviceManager.getBindings
|
|
19680
|
-
*
|
|
19681
|
-
*
|
|
19682
|
-
* single-entry control would read as a defect; the binding store learning to
|
|
19683
|
-
* hold several providers for a collection cap is what changes this, and
|
|
19684
|
-
* nothing here needs to change with it.
|
|
19700
|
+
* `listSources` derives from `deviceManager.getBindings`. A camera bound to one
|
|
19701
|
+
* provider makes the selector inert, and it SAYS why: an empty or silently
|
|
19702
|
+
* single-entry control would read as a defect.
|
|
19685
19703
|
*/
|
|
19686
19704
|
/** Newest-first, and bounded: a busy day on 592 lists 546 clips. */
|
|
19687
19705
|
var CLIP_PAGE_LIMIT = 500;
|
|
@@ -19796,16 +19814,26 @@ function ClipsSection({ deviceId }) {
|
|
|
19796
19814
|
}), [view, selected]);
|
|
19797
19815
|
const plan = planClipBand(band);
|
|
19798
19816
|
const sources = useVideoclipsListSources({ deviceId });
|
|
19817
|
+
const sourceRows = sources.data ?? [];
|
|
19818
|
+
/**
|
|
19819
|
+
* The provider the list is ASKED of: the operator's pick, else the first
|
|
19820
|
+
* source the camera lists. Empty only while the camera has no source at all
|
|
19821
|
+
* — and then nothing is asked, because there is nobody to ask.
|
|
19822
|
+
*/
|
|
19823
|
+
const askedProvider = useMemo(() => {
|
|
19824
|
+
if (provider !== "") return provider;
|
|
19825
|
+
return sourceRows.find((row) => row.addonId !== void 0)?.addonId ?? "";
|
|
19826
|
+
}, [provider, sourceRows]);
|
|
19799
19827
|
const clips = useVideoclipsListClips({
|
|
19800
19828
|
deviceId,
|
|
19801
19829
|
since: band.fromMs,
|
|
19802
19830
|
until: band.toMs,
|
|
19803
19831
|
limit: CLIP_PAGE_LIMIT,
|
|
19804
|
-
|
|
19805
|
-
}, { enabled: plan.kind === "askable" });
|
|
19806
|
-
const sourceRows = sources.data ?? [];
|
|
19832
|
+
provider: askedProvider
|
|
19833
|
+
}, { enabled: plan.kind === "askable" && askedProvider !== "" });
|
|
19807
19834
|
const clipsView = resolveClipsView({
|
|
19808
19835
|
plan,
|
|
19836
|
+
provider: askedProvider === "" ? null : askedProvider,
|
|
19809
19837
|
clipsStatus: clips.status,
|
|
19810
19838
|
clips: clips.data ?? [],
|
|
19811
19839
|
sourcesStatus: sources.status,
|
|
@@ -19823,15 +19851,10 @@ function ClipsSection({ deviceId }) {
|
|
|
19823
19851
|
if (labels === void 0) byAddon.set(addonId, [row.label]);
|
|
19824
19852
|
else labels.push(row.label);
|
|
19825
19853
|
}
|
|
19826
|
-
|
|
19854
|
+
return [...byAddon].map(([value, labels]) => ({
|
|
19827
19855
|
value,
|
|
19828
19856
|
label: labels.join(" · ")
|
|
19829
19857
|
}));
|
|
19830
|
-
const first = rest[0];
|
|
19831
|
-
return [{
|
|
19832
|
-
value: "",
|
|
19833
|
-
label: first === void 0 ? "The camera’s bound provider" : first.label
|
|
19834
|
-
}, ...rest.slice(1)];
|
|
19835
19858
|
}, [sourceRows]);
|
|
19836
19859
|
const tableRows = useMemo(() => sourceRows.map((row) => ({
|
|
19837
19860
|
notice: describeSource(row),
|
|
@@ -19909,7 +19932,7 @@ function ClipsSection({ deviceId }) {
|
|
|
19909
19932
|
children: /* @__PURE__ */ jsx(Select, {
|
|
19910
19933
|
id: "clips-provider",
|
|
19911
19934
|
options,
|
|
19912
|
-
value:
|
|
19935
|
+
value: askedProvider,
|
|
19913
19936
|
disabled: options.length < 2,
|
|
19914
19937
|
onChange: (e) => pick(e.target.value)
|
|
19915
19938
|
})
|