@isograph/react 0.0.0-main-359621bd → 0.0.0-main-3a0c06c0
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/index.d.ts +1 -0
- package/dist/index.js +3 -1
- package/dist/loadable-hooks/useConnectionSpecPagination.d.ts +33 -0
- package/dist/loadable-hooks/useConnectionSpecPagination.js +148 -0
- package/package.json +4 -4
- package/src/index.ts +1 -0
- package/src/loadable-hooks/useConnectionSpecPagination.ts +319 -0
package/dist/index.d.ts
CHANGED
@@ -18,4 +18,5 @@ export { useRerenderOnChange } from './react/useRerenderOnChange';
|
|
18
18
|
export { useClientSideDefer } from './loadable-hooks/useClientSideDefer';
|
19
19
|
export { useImperativeExposedMutationField } from './loadable-hooks/useImperativeExposedMutationField';
|
20
20
|
export { useSkipLimitPagination } from './loadable-hooks/useSkipLimitPagination';
|
21
|
+
export { useConnectionSpecPagination } from './loadable-hooks/useConnectionSpecPagination';
|
21
22
|
export { useImperativeLoadableField } from './loadable-hooks/useImperativeLoadableField';
|
package/dist/index.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
exports.useImperativeLoadableField = exports.useSkipLimitPagination = exports.useImperativeExposedMutationField = exports.useClientSideDefer = exports.useRerenderOnChange = exports.useLazyReference = exports.useSubscribeToMultiple = exports.useReadAndSubscribe = exports.useResult = exports.FragmentReader = exports.useImperativeReference = exports.useIsographEnvironment = exports.IsographEnvironmentProvider = exports.stableIdForFragmentReference = exports.readButDoNotEvaluate = exports.assertIsEntrypoint = exports.defaultMissingFieldHandler = exports.createIsographStore = exports.createIsographEnvironment = exports.ROOT_ID = exports.makeNetworkRequest = exports.normalizeData = exports.subscribe = exports.wrapPromise = exports.wrapResolvedValue = exports.getPromiseState = exports.readPromise = exports.garbageCollectEnvironment = exports.unretainQuery = exports.retainQuery = void 0;
|
3
|
+
exports.useImperativeLoadableField = exports.useConnectionSpecPagination = exports.useSkipLimitPagination = exports.useImperativeExposedMutationField = exports.useClientSideDefer = exports.useRerenderOnChange = exports.useLazyReference = exports.useSubscribeToMultiple = exports.useReadAndSubscribe = exports.useResult = exports.FragmentReader = exports.useImperativeReference = exports.useIsographEnvironment = exports.IsographEnvironmentProvider = exports.stableIdForFragmentReference = exports.readButDoNotEvaluate = exports.assertIsEntrypoint = exports.defaultMissingFieldHandler = exports.createIsographStore = exports.createIsographEnvironment = exports.ROOT_ID = exports.makeNetworkRequest = exports.normalizeData = exports.subscribe = exports.wrapPromise = exports.wrapResolvedValue = exports.getPromiseState = exports.readPromise = exports.garbageCollectEnvironment = exports.unretainQuery = exports.retainQuery = void 0;
|
4
4
|
var garbageCollection_1 = require("./core/garbageCollection");
|
5
5
|
Object.defineProperty(exports, "retainQuery", { enumerable: true, get: function () { return garbageCollection_1.retainQuery; } });
|
6
6
|
Object.defineProperty(exports, "unretainQuery", { enumerable: true, get: function () { return garbageCollection_1.unretainQuery; } });
|
@@ -48,5 +48,7 @@ var useImperativeExposedMutationField_1 = require("./loadable-hooks/useImperativ
|
|
48
48
|
Object.defineProperty(exports, "useImperativeExposedMutationField", { enumerable: true, get: function () { return useImperativeExposedMutationField_1.useImperativeExposedMutationField; } });
|
49
49
|
var useSkipLimitPagination_1 = require("./loadable-hooks/useSkipLimitPagination");
|
50
50
|
Object.defineProperty(exports, "useSkipLimitPagination", { enumerable: true, get: function () { return useSkipLimitPagination_1.useSkipLimitPagination; } });
|
51
|
+
var useConnectionSpecPagination_1 = require("./loadable-hooks/useConnectionSpecPagination");
|
52
|
+
Object.defineProperty(exports, "useConnectionSpecPagination", { enumerable: true, get: function () { return useConnectionSpecPagination_1.useConnectionSpecPagination; } });
|
51
53
|
var useImperativeLoadableField_1 = require("./loadable-hooks/useImperativeLoadableField");
|
52
54
|
Object.defineProperty(exports, "useImperativeLoadableField", { enumerable: true, get: function () { return useImperativeLoadableField_1.useImperativeLoadableField; } });
|
@@ -0,0 +1,33 @@
|
|
1
|
+
import { FragmentReference } from '../core/FragmentReference';
|
2
|
+
import { LoadableField } from '../core/reader';
|
3
|
+
type FirstOrAfter = 'first' | 'after';
|
4
|
+
type OmitFirstAfter<TArgs> = keyof Omit<TArgs, FirstOrAfter> extends never ? void | Record<string, never> : Omit<TArgs, FirstOrAfter>;
|
5
|
+
type UsePaginationReturnValue<TReadFromStore extends {
|
6
|
+
parameters: object;
|
7
|
+
data: object;
|
8
|
+
}, TItem> = {
|
9
|
+
kind: 'Pending';
|
10
|
+
pendingFragment: FragmentReference<TReadFromStore, Connection<TItem>>;
|
11
|
+
results: ReadonlyArray<TItem>;
|
12
|
+
} | {
|
13
|
+
kind: 'Complete';
|
14
|
+
fetchMore: (args: OmitFirstAfter<TReadFromStore['parameters']>, first: number) => void;
|
15
|
+
results: ReadonlyArray<TItem>;
|
16
|
+
hasNextPage: boolean;
|
17
|
+
};
|
18
|
+
type PageInfo = {
|
19
|
+
readonly hasNextPage: boolean;
|
20
|
+
readonly endCursor: string | null;
|
21
|
+
};
|
22
|
+
type Connection<T> = {
|
23
|
+
readonly edges: ReadonlyArray<T> | null;
|
24
|
+
readonly pageInfo: PageInfo;
|
25
|
+
};
|
26
|
+
export declare function useConnectionSpecPagination<TReadFromStore extends {
|
27
|
+
parameters: {
|
28
|
+
readonly first?: number | void | null;
|
29
|
+
readonly after?: string | void | null;
|
30
|
+
};
|
31
|
+
data: object;
|
32
|
+
}, TItem>(loadableField: LoadableField<TReadFromStore, Connection<TItem>>): UsePaginationReturnValue<TReadFromStore, TItem>;
|
33
|
+
export {};
|
@@ -0,0 +1,148 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.useConnectionSpecPagination = void 0;
|
4
|
+
const react_disposable_state_1 = require("@isograph/react-disposable-state");
|
5
|
+
const reference_counted_pointer_1 = require("@isograph/reference-counted-pointer");
|
6
|
+
const react_1 = require("react");
|
7
|
+
const cache_1 = require("../core/cache");
|
8
|
+
const PromiseWrapper_1 = require("../core/PromiseWrapper");
|
9
|
+
const read_1 = require("../core/read");
|
10
|
+
const IsographEnvironmentProvider_1 = require("../react/IsographEnvironmentProvider");
|
11
|
+
const useReadAndSubscribe_1 = require("../react/useReadAndSubscribe");
|
12
|
+
const useResult_1 = require("../react/useResult");
|
13
|
+
function flatten(arr) {
|
14
|
+
let outArray = [];
|
15
|
+
for (const subarr of arr) {
|
16
|
+
for (const item of subarr) {
|
17
|
+
outArray.push(item);
|
18
|
+
}
|
19
|
+
}
|
20
|
+
return outArray;
|
21
|
+
}
|
22
|
+
function useConnectionSpecPagination(loadableField) {
|
23
|
+
const networkRequestOptions = {
|
24
|
+
suspendIfInFlight: true,
|
25
|
+
throwOnNetworkError: true,
|
26
|
+
};
|
27
|
+
const { state, setState } = (0, react_disposable_state_1.useUpdatableDisposableState)();
|
28
|
+
const environment = (0, IsographEnvironmentProvider_1.useIsographEnvironment)();
|
29
|
+
// TODO move this out of useSkipLimitPagination, and pass environment and networkRequestOptions
|
30
|
+
// as parameters (or recreate networkRequestOptions)
|
31
|
+
function readCompletedFragmentReferences(completedReferences) {
|
32
|
+
var _a, _b;
|
33
|
+
const results = completedReferences.map((fragmentReference, i) => {
|
34
|
+
const readerWithRefetchQueries = (0, PromiseWrapper_1.readPromise)(fragmentReference.readerWithRefetchQueries);
|
35
|
+
const firstParameter = {
|
36
|
+
data: readOutDataAndRecords[i].item,
|
37
|
+
parameters: fragmentReference.variables,
|
38
|
+
};
|
39
|
+
if (readerWithRefetchQueries.readerArtifact.kind !== 'EagerReaderArtifact') {
|
40
|
+
throw new Error(`@loadable field of kind "${readerWithRefetchQueries.readerArtifact.kind}" is not supported by useSkipLimitPagination`);
|
41
|
+
}
|
42
|
+
return readerWithRefetchQueries.readerArtifact.resolver(firstParameter);
|
43
|
+
});
|
44
|
+
const items = flatten(results.map((result) => { var _a; return (_a = result.edges) !== null && _a !== void 0 ? _a : []; }));
|
45
|
+
return {
|
46
|
+
edges: items,
|
47
|
+
pageInfo: (_b = (_a = results[results.length - 1]) === null || _a === void 0 ? void 0 : _a.pageInfo) !== null && _b !== void 0 ? _b : {
|
48
|
+
endCursor: null,
|
49
|
+
hasNextPage: true,
|
50
|
+
},
|
51
|
+
};
|
52
|
+
}
|
53
|
+
function subscribeCompletedFragmentReferences(completedReferences) {
|
54
|
+
return completedReferences.map((fragmentReference, i) => {
|
55
|
+
(0, useResult_1.maybeUnwrapNetworkRequest)(fragmentReference.networkRequest, networkRequestOptions);
|
56
|
+
const readerWithRefetchQueries = (0, PromiseWrapper_1.readPromise)(fragmentReference.readerWithRefetchQueries);
|
57
|
+
return {
|
58
|
+
fragmentReference,
|
59
|
+
readerAst: readerWithRefetchQueries.readerArtifact.readerAst,
|
60
|
+
records: readOutDataAndRecords[i],
|
61
|
+
callback(_data) {
|
62
|
+
rerender({});
|
63
|
+
},
|
64
|
+
};
|
65
|
+
});
|
66
|
+
}
|
67
|
+
const getFetchMore = (after) => (args, first) => {
|
68
|
+
// @ts-expect-error
|
69
|
+
const loadedField = loadableField(Object.assign(Object.assign({}, args), { after: after, first: first }))[1]();
|
70
|
+
const newPointer = (0, reference_counted_pointer_1.createReferenceCountedPointer)(loadedField);
|
71
|
+
const clonedPointers = loadedReferences.map(([refCountedPointer]) => {
|
72
|
+
const clonedRefCountedPointer = refCountedPointer.cloneIfNotDisposed();
|
73
|
+
if (clonedRefCountedPointer == null) {
|
74
|
+
throw new Error('This reference counted pointer has already been disposed. \
|
75
|
+
This is indicative of a bug in useSkipLimitPagination.');
|
76
|
+
}
|
77
|
+
return clonedRefCountedPointer;
|
78
|
+
});
|
79
|
+
clonedPointers.push(newPointer);
|
80
|
+
const totalItemCleanupPair = [
|
81
|
+
clonedPointers,
|
82
|
+
() => {
|
83
|
+
clonedPointers.forEach(([, dispose]) => {
|
84
|
+
dispose();
|
85
|
+
});
|
86
|
+
},
|
87
|
+
];
|
88
|
+
setState(totalItemCleanupPair);
|
89
|
+
};
|
90
|
+
const [, rerender] = (0, react_1.useState)({});
|
91
|
+
const loadedReferences = state === react_disposable_state_1.UNASSIGNED_STATE ? [] : state;
|
92
|
+
const mostRecentItem = loadedReferences[loadedReferences.length - 1];
|
93
|
+
const mostRecentFragmentReference = mostRecentItem === null || mostRecentItem === void 0 ? void 0 : mostRecentItem[0].getItemIfNotDisposed();
|
94
|
+
if (mostRecentItem && mostRecentFragmentReference === null) {
|
95
|
+
throw new Error('FragmentReference is unexpectedly disposed. \
|
96
|
+
This is indicative of a bug in Isograph.');
|
97
|
+
}
|
98
|
+
const networkRequestStatus = mostRecentFragmentReference &&
|
99
|
+
(0, PromiseWrapper_1.getPromiseState)(mostRecentFragmentReference.networkRequest);
|
100
|
+
const slicedFragmentReferences = (networkRequestStatus === null || networkRequestStatus === void 0 ? void 0 : networkRequestStatus.kind) === 'Ok'
|
101
|
+
? loadedReferences
|
102
|
+
: loadedReferences.slice(0, loadedReferences.length - 1);
|
103
|
+
const completedFragmentReferences = slicedFragmentReferences.map(([pointer]) => {
|
104
|
+
const fragmentReference = pointer.getItemIfNotDisposed();
|
105
|
+
if (fragmentReference == null) {
|
106
|
+
throw new Error('FragmentReference is unexpectedly disposed. \
|
107
|
+
This is indicative of a bug in Isograph.');
|
108
|
+
}
|
109
|
+
return fragmentReference;
|
110
|
+
});
|
111
|
+
const readOutDataAndRecords = completedFragmentReferences.map((fragmentReference) => (0, read_1.readButDoNotEvaluate)(environment, fragmentReference, networkRequestOptions));
|
112
|
+
(0, useReadAndSubscribe_1.useSubscribeToMultiple)(subscribeCompletedFragmentReferences(completedFragmentReferences));
|
113
|
+
if (!networkRequestStatus) {
|
114
|
+
return {
|
115
|
+
kind: 'Complete',
|
116
|
+
fetchMore: getFetchMore(null),
|
117
|
+
results: [],
|
118
|
+
hasNextPage: true,
|
119
|
+
};
|
120
|
+
}
|
121
|
+
switch (networkRequestStatus.kind) {
|
122
|
+
case 'Pending': {
|
123
|
+
const unsubscribe = (0, cache_1.subscribeToAnyChange)(environment, () => {
|
124
|
+
unsubscribe();
|
125
|
+
rerender({});
|
126
|
+
});
|
127
|
+
const results = readCompletedFragmentReferences(completedFragmentReferences);
|
128
|
+
return {
|
129
|
+
results: results.edges,
|
130
|
+
kind: 'Pending',
|
131
|
+
pendingFragment: mostRecentFragmentReference,
|
132
|
+
};
|
133
|
+
}
|
134
|
+
case 'Err': {
|
135
|
+
throw networkRequestStatus.error;
|
136
|
+
}
|
137
|
+
case 'Ok': {
|
138
|
+
const results = readCompletedFragmentReferences(completedFragmentReferences);
|
139
|
+
return {
|
140
|
+
results: results.edges,
|
141
|
+
hasNextPage: results.pageInfo.hasNextPage,
|
142
|
+
kind: 'Complete',
|
143
|
+
fetchMore: getFetchMore(results.pageInfo.endCursor),
|
144
|
+
};
|
145
|
+
}
|
146
|
+
}
|
147
|
+
}
|
148
|
+
exports.useConnectionSpecPagination = useConnectionSpecPagination;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@isograph/react",
|
3
|
-
"version": "0.0.0-main-
|
3
|
+
"version": "0.0.0-main-3a0c06c0",
|
4
4
|
"description": "Use Isograph with React",
|
5
5
|
"homepage": "https://isograph.dev",
|
6
6
|
"main": "dist/index.js",
|
@@ -17,9 +17,9 @@
|
|
17
17
|
"tsc": "tsc"
|
18
18
|
},
|
19
19
|
"dependencies": {
|
20
|
-
"@isograph/disposable-types": "0.0.0-main-
|
21
|
-
"@isograph/react-disposable-state": "0.0.0-main-
|
22
|
-
"@isograph/reference-counted-pointer": "0.0.0-main-
|
20
|
+
"@isograph/disposable-types": "0.0.0-main-3a0c06c0",
|
21
|
+
"@isograph/react-disposable-state": "0.0.0-main-3a0c06c0",
|
22
|
+
"@isograph/reference-counted-pointer": "0.0.0-main-3a0c06c0"
|
23
23
|
},
|
24
24
|
"peerDependencies": {
|
25
25
|
"react": "18.2.0"
|
package/src/index.ts
CHANGED
@@ -86,4 +86,5 @@ export { useRerenderOnChange } from './react/useRerenderOnChange';
|
|
86
86
|
export { useClientSideDefer } from './loadable-hooks/useClientSideDefer';
|
87
87
|
export { useImperativeExposedMutationField } from './loadable-hooks/useImperativeExposedMutationField';
|
88
88
|
export { useSkipLimitPagination } from './loadable-hooks/useSkipLimitPagination';
|
89
|
+
export { useConnectionSpecPagination } from './loadable-hooks/useConnectionSpecPagination';
|
89
90
|
export { useImperativeLoadableField } from './loadable-hooks/useImperativeLoadableField';
|
@@ -0,0 +1,319 @@
|
|
1
|
+
import { ItemCleanupPair } from '@isograph/disposable-types';
|
2
|
+
import {
|
3
|
+
UNASSIGNED_STATE,
|
4
|
+
useUpdatableDisposableState,
|
5
|
+
} from '@isograph/react-disposable-state';
|
6
|
+
import {
|
7
|
+
createReferenceCountedPointer,
|
8
|
+
ReferenceCountedPointer,
|
9
|
+
} from '@isograph/reference-counted-pointer';
|
10
|
+
import { useState } from 'react';
|
11
|
+
import { subscribeToAnyChange } from '../core/cache';
|
12
|
+
import { FragmentReference } from '../core/FragmentReference';
|
13
|
+
import { getPromiseState, readPromise } from '../core/PromiseWrapper';
|
14
|
+
import {
|
15
|
+
readButDoNotEvaluate,
|
16
|
+
type WithEncounteredRecords,
|
17
|
+
} from '../core/read';
|
18
|
+
import { LoadableField, type ReaderAst } from '../core/reader';
|
19
|
+
import { useIsographEnvironment } from '../react/IsographEnvironmentProvider';
|
20
|
+
import { useSubscribeToMultiple } from '../react/useReadAndSubscribe';
|
21
|
+
import { maybeUnwrapNetworkRequest } from '../react/useResult';
|
22
|
+
|
23
|
+
type FirstOrAfter = 'first' | 'after';
|
24
|
+
type OmitFirstAfter<TArgs> = keyof Omit<TArgs, FirstOrAfter> extends never
|
25
|
+
? void | Record<string, never>
|
26
|
+
: Omit<TArgs, FirstOrAfter>;
|
27
|
+
|
28
|
+
type UsePaginationReturnValue<
|
29
|
+
TReadFromStore extends { parameters: object; data: object },
|
30
|
+
TItem,
|
31
|
+
> =
|
32
|
+
| {
|
33
|
+
kind: 'Pending';
|
34
|
+
pendingFragment: FragmentReference<TReadFromStore, Connection<TItem>>;
|
35
|
+
results: ReadonlyArray<TItem>;
|
36
|
+
}
|
37
|
+
| {
|
38
|
+
kind: 'Complete';
|
39
|
+
fetchMore: (
|
40
|
+
args: OmitFirstAfter<TReadFromStore['parameters']>,
|
41
|
+
first: number,
|
42
|
+
) => void;
|
43
|
+
results: ReadonlyArray<TItem>;
|
44
|
+
hasNextPage: boolean;
|
45
|
+
};
|
46
|
+
|
47
|
+
type LoadedFragmentReferences<
|
48
|
+
TReadFromStore extends { parameters: object; data: object },
|
49
|
+
TItem,
|
50
|
+
> = ReadonlyArray<LoadedFragmentReference<TReadFromStore, TItem>>;
|
51
|
+
|
52
|
+
type LoadedFragmentReference<
|
53
|
+
TReadFromStore extends { parameters: object; data: object },
|
54
|
+
TItem,
|
55
|
+
> = ItemCleanupPair<
|
56
|
+
ReferenceCountedPointer<FragmentReference<TReadFromStore, TItem>>
|
57
|
+
>;
|
58
|
+
|
59
|
+
function flatten<T>(arr: ReadonlyArray<ReadonlyArray<T>>): ReadonlyArray<T> {
|
60
|
+
let outArray: Array<T> = [];
|
61
|
+
for (const subarr of arr) {
|
62
|
+
for (const item of subarr) {
|
63
|
+
outArray.push(item);
|
64
|
+
}
|
65
|
+
}
|
66
|
+
return outArray;
|
67
|
+
}
|
68
|
+
|
69
|
+
type PageInfo = {
|
70
|
+
readonly hasNextPage: boolean;
|
71
|
+
readonly endCursor: string | null;
|
72
|
+
};
|
73
|
+
|
74
|
+
type Connection<T> = {
|
75
|
+
readonly edges: ReadonlyArray<T> | null;
|
76
|
+
readonly pageInfo: PageInfo;
|
77
|
+
};
|
78
|
+
|
79
|
+
type NonNullConnection<T> = {
|
80
|
+
readonly edges: ReadonlyArray<T>;
|
81
|
+
readonly pageInfo: PageInfo;
|
82
|
+
};
|
83
|
+
|
84
|
+
export function useConnectionSpecPagination<
|
85
|
+
TReadFromStore extends {
|
86
|
+
parameters: {
|
87
|
+
readonly first?: number | void | null;
|
88
|
+
readonly after?: string | void | null;
|
89
|
+
};
|
90
|
+
data: object;
|
91
|
+
},
|
92
|
+
TItem,
|
93
|
+
>(
|
94
|
+
loadableField: LoadableField<TReadFromStore, Connection<TItem>>,
|
95
|
+
): UsePaginationReturnValue<TReadFromStore, TItem> {
|
96
|
+
const networkRequestOptions = {
|
97
|
+
suspendIfInFlight: true,
|
98
|
+
throwOnNetworkError: true,
|
99
|
+
};
|
100
|
+
const { state, setState } =
|
101
|
+
useUpdatableDisposableState<
|
102
|
+
LoadedFragmentReferences<TReadFromStore, Connection<TItem>>
|
103
|
+
>();
|
104
|
+
|
105
|
+
const environment = useIsographEnvironment();
|
106
|
+
|
107
|
+
// TODO move this out of useSkipLimitPagination, and pass environment and networkRequestOptions
|
108
|
+
// as parameters (or recreate networkRequestOptions)
|
109
|
+
function readCompletedFragmentReferences(
|
110
|
+
completedReferences: FragmentReference<TReadFromStore, Connection<TItem>>[],
|
111
|
+
): NonNullConnection<TItem> {
|
112
|
+
const results = completedReferences.map((fragmentReference, i) => {
|
113
|
+
const readerWithRefetchQueries = readPromise(
|
114
|
+
fragmentReference.readerWithRefetchQueries,
|
115
|
+
);
|
116
|
+
|
117
|
+
const firstParameter = {
|
118
|
+
data: readOutDataAndRecords[i].item,
|
119
|
+
parameters: fragmentReference.variables,
|
120
|
+
};
|
121
|
+
|
122
|
+
if (
|
123
|
+
readerWithRefetchQueries.readerArtifact.kind !== 'EagerReaderArtifact'
|
124
|
+
) {
|
125
|
+
throw new Error(
|
126
|
+
`@loadable field of kind "${readerWithRefetchQueries.readerArtifact.kind}" is not supported by useSkipLimitPagination`,
|
127
|
+
);
|
128
|
+
}
|
129
|
+
|
130
|
+
return readerWithRefetchQueries.readerArtifact.resolver(firstParameter);
|
131
|
+
});
|
132
|
+
|
133
|
+
const items = flatten(results.map((result) => result.edges ?? []));
|
134
|
+
|
135
|
+
return {
|
136
|
+
edges: items,
|
137
|
+
pageInfo: results[results.length - 1]?.pageInfo ?? {
|
138
|
+
endCursor: null,
|
139
|
+
hasNextPage: true,
|
140
|
+
},
|
141
|
+
};
|
142
|
+
}
|
143
|
+
|
144
|
+
function subscribeCompletedFragmentReferences(
|
145
|
+
completedReferences: FragmentReference<TReadFromStore, Connection<TItem>>[],
|
146
|
+
) {
|
147
|
+
return completedReferences.map(
|
148
|
+
(
|
149
|
+
fragmentReference,
|
150
|
+
i,
|
151
|
+
): {
|
152
|
+
records: WithEncounteredRecords<TReadFromStore>;
|
153
|
+
callback: (
|
154
|
+
updatedRecords: WithEncounteredRecords<TReadFromStore>,
|
155
|
+
) => void;
|
156
|
+
fragmentReference: FragmentReference<TReadFromStore, Connection<TItem>>;
|
157
|
+
readerAst: ReaderAst<Connection<TItem>>;
|
158
|
+
} => {
|
159
|
+
maybeUnwrapNetworkRequest(
|
160
|
+
fragmentReference.networkRequest,
|
161
|
+
networkRequestOptions,
|
162
|
+
);
|
163
|
+
|
164
|
+
const readerWithRefetchQueries = readPromise(
|
165
|
+
fragmentReference.readerWithRefetchQueries,
|
166
|
+
);
|
167
|
+
|
168
|
+
return {
|
169
|
+
fragmentReference,
|
170
|
+
readerAst: readerWithRefetchQueries.readerArtifact.readerAst,
|
171
|
+
records: readOutDataAndRecords[i],
|
172
|
+
callback(_data) {
|
173
|
+
rerender({});
|
174
|
+
},
|
175
|
+
};
|
176
|
+
},
|
177
|
+
);
|
178
|
+
}
|
179
|
+
|
180
|
+
const getFetchMore =
|
181
|
+
(after: string | null | undefined) =>
|
182
|
+
(
|
183
|
+
args: OmitFirstAfter<TReadFromStore['parameters']>,
|
184
|
+
first: number,
|
185
|
+
): void => {
|
186
|
+
// @ts-expect-error
|
187
|
+
const loadedField = loadableField({
|
188
|
+
...args,
|
189
|
+
after: after,
|
190
|
+
first: first,
|
191
|
+
})[1]();
|
192
|
+
const newPointer = createReferenceCountedPointer(loadedField);
|
193
|
+
const clonedPointers = loadedReferences.map(([refCountedPointer]) => {
|
194
|
+
const clonedRefCountedPointer = refCountedPointer.cloneIfNotDisposed();
|
195
|
+
if (clonedRefCountedPointer == null) {
|
196
|
+
throw new Error(
|
197
|
+
'This reference counted pointer has already been disposed. \
|
198
|
+
This is indicative of a bug in useSkipLimitPagination.',
|
199
|
+
);
|
200
|
+
}
|
201
|
+
return clonedRefCountedPointer;
|
202
|
+
});
|
203
|
+
clonedPointers.push(newPointer);
|
204
|
+
|
205
|
+
const totalItemCleanupPair: ItemCleanupPair<
|
206
|
+
ReadonlyArray<
|
207
|
+
ItemCleanupPair<
|
208
|
+
ReferenceCountedPointer<
|
209
|
+
FragmentReference<TReadFromStore, Connection<TItem>>
|
210
|
+
>
|
211
|
+
>
|
212
|
+
>
|
213
|
+
> = [
|
214
|
+
clonedPointers,
|
215
|
+
() => {
|
216
|
+
clonedPointers.forEach(([, dispose]) => {
|
217
|
+
dispose();
|
218
|
+
});
|
219
|
+
},
|
220
|
+
];
|
221
|
+
|
222
|
+
setState(totalItemCleanupPair);
|
223
|
+
};
|
224
|
+
|
225
|
+
const [, rerender] = useState({});
|
226
|
+
|
227
|
+
const loadedReferences = state === UNASSIGNED_STATE ? [] : state;
|
228
|
+
|
229
|
+
const mostRecentItem: LoadedFragmentReference<
|
230
|
+
TReadFromStore,
|
231
|
+
Connection<TItem>
|
232
|
+
> | null = loadedReferences[loadedReferences.length - 1];
|
233
|
+
const mostRecentFragmentReference =
|
234
|
+
mostRecentItem?.[0].getItemIfNotDisposed();
|
235
|
+
|
236
|
+
if (mostRecentItem && mostRecentFragmentReference === null) {
|
237
|
+
throw new Error(
|
238
|
+
'FragmentReference is unexpectedly disposed. \
|
239
|
+
This is indicative of a bug in Isograph.',
|
240
|
+
);
|
241
|
+
}
|
242
|
+
|
243
|
+
const networkRequestStatus =
|
244
|
+
mostRecentFragmentReference &&
|
245
|
+
getPromiseState(mostRecentFragmentReference.networkRequest);
|
246
|
+
|
247
|
+
const slicedFragmentReferences =
|
248
|
+
networkRequestStatus?.kind === 'Ok'
|
249
|
+
? loadedReferences
|
250
|
+
: loadedReferences.slice(0, loadedReferences.length - 1);
|
251
|
+
|
252
|
+
const completedFragmentReferences = slicedFragmentReferences.map(
|
253
|
+
([pointer]) => {
|
254
|
+
const fragmentReference = pointer.getItemIfNotDisposed();
|
255
|
+
if (fragmentReference == null) {
|
256
|
+
throw new Error(
|
257
|
+
'FragmentReference is unexpectedly disposed. \
|
258
|
+
This is indicative of a bug in Isograph.',
|
259
|
+
);
|
260
|
+
}
|
261
|
+
return fragmentReference;
|
262
|
+
},
|
263
|
+
);
|
264
|
+
|
265
|
+
const readOutDataAndRecords = completedFragmentReferences.map(
|
266
|
+
(fragmentReference) =>
|
267
|
+
readButDoNotEvaluate(
|
268
|
+
environment,
|
269
|
+
fragmentReference,
|
270
|
+
networkRequestOptions,
|
271
|
+
),
|
272
|
+
);
|
273
|
+
|
274
|
+
useSubscribeToMultiple<TReadFromStore>(
|
275
|
+
subscribeCompletedFragmentReferences(completedFragmentReferences),
|
276
|
+
);
|
277
|
+
|
278
|
+
if (!networkRequestStatus) {
|
279
|
+
return {
|
280
|
+
kind: 'Complete',
|
281
|
+
fetchMore: getFetchMore(null),
|
282
|
+
results: [],
|
283
|
+
hasNextPage: true,
|
284
|
+
};
|
285
|
+
}
|
286
|
+
|
287
|
+
switch (networkRequestStatus.kind) {
|
288
|
+
case 'Pending': {
|
289
|
+
const unsubscribe = subscribeToAnyChange(environment, () => {
|
290
|
+
unsubscribe();
|
291
|
+
rerender({});
|
292
|
+
});
|
293
|
+
|
294
|
+
const results = readCompletedFragmentReferences(
|
295
|
+
completedFragmentReferences,
|
296
|
+
);
|
297
|
+
return {
|
298
|
+
results: results.edges,
|
299
|
+
kind: 'Pending',
|
300
|
+
pendingFragment: mostRecentFragmentReference,
|
301
|
+
};
|
302
|
+
}
|
303
|
+
case 'Err': {
|
304
|
+
throw networkRequestStatus.error;
|
305
|
+
}
|
306
|
+
case 'Ok': {
|
307
|
+
const results = readCompletedFragmentReferences(
|
308
|
+
completedFragmentReferences,
|
309
|
+
);
|
310
|
+
|
311
|
+
return {
|
312
|
+
results: results.edges,
|
313
|
+
hasNextPage: results.pageInfo.hasNextPage,
|
314
|
+
kind: 'Complete',
|
315
|
+
fetchMore: getFetchMore(results.pageInfo.endCursor),
|
316
|
+
};
|
317
|
+
}
|
318
|
+
}
|
319
|
+
}
|