@webority/mobile-core 0.16.6 → 0.16.7
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 +30 -1
- package/lib/commonjs/query/index.js +1 -1
- package/lib/commonjs/query/persistedQueries.js +1 -0
- package/lib/module/query/index.js +1 -1
- package/lib/module/query/persistedQueries.js +1 -0
- package/lib/typescript/commonjs/query/index.d.ts +2 -0
- package/lib/typescript/commonjs/query/persistedQueries.d.ts +26 -0
- package/lib/typescript/module/query/index.d.ts +2 -0
- package/lib/typescript/module/query/persistedQueries.d.ts +26 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ import { createApiBaseQuery, createAppApi, setupApiListeners } from '@webority/m
|
|
|
40
40
|
Metro resolves requires statically, so anything reachable from the main barrel is bundled for
|
|
41
41
|
every consumer. Keeping these off it is what makes their peers genuinely optional.
|
|
42
42
|
|
|
43
|
-
`/query` is the RTK Query layer; it needs `@reduxjs/toolkit` (2.
|
|
43
|
+
`/query` is the RTK Query layer; it needs `@reduxjs/toolkit` (2.12 or later) and `react-redux`,
|
|
44
44
|
both optional peers. One `createApi` per app, built with `createAppApi` and the base query over
|
|
45
45
|
the library's clients, so refresh, retry, the offline check and the `extractApiError` failure
|
|
46
46
|
shape stay in one place. Never a `fetchBaseQuery`.
|
|
@@ -98,6 +98,35 @@ keeps push on calls `enablePush` at each start. `setBadgeCount(count)` sets the
|
|
|
98
98
|
an unread count. It returns a function that removes the handler. A notification arriving in the
|
|
99
99
|
background does not reach it; its tap still goes to `onOpen`.
|
|
100
100
|
|
|
101
|
+
### Offline copy
|
|
102
|
+
|
|
103
|
+
`createPersistedQueries` keeps the answers of chosen endpoints on the phone, so a screen opened
|
|
104
|
+
with no connection shows the last data with the time it was loaded. Listed endpoints are saved to
|
|
105
|
+
`Storage` when they load; `restore` puts them back once at start.
|
|
106
|
+
|
|
107
|
+
1. Pass `extractRehydrationInfo` to `createAppApi`.
|
|
108
|
+
2. Add `middleware` after the api's middleware.
|
|
109
|
+
3. Call `restore(store.dispatch)` once after the store is created.
|
|
110
|
+
4. Call `clear()` on sign-out, so the next person on the phone never sees the last person's data.
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
// services/api/offlineQueries.ts
|
|
114
|
+
export const offlineQueries = createPersistedQueries({
|
|
115
|
+
storageKey: 'offline.queries',
|
|
116
|
+
endpoints: ['getProjects']
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// services/api/apiSlice.ts: add extractRehydrationInfo: offlineQueries.extractRehydrationInfo
|
|
120
|
+
// store.ts: reducer: { [apiSlice.reducerPath]: apiSlice.reducer }
|
|
121
|
+
// middleware: (getDefault) => getDefault().concat(apiSlice.middleware, offlineQueries.middleware)
|
|
122
|
+
offlineQueries.restore(store.dispatch); // once, after the store exists
|
|
123
|
+
// sign-out
|
|
124
|
+
await offlineQueries.clear();
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
A restored answer shows at once without a fetch, so set `refetchOnMountOrArgChange: true` on each
|
|
128
|
+
kept endpoint: a screen that mounts then asks the server again and refreshes the restored data.
|
|
129
|
+
|
|
101
130
|
### Notification buttons
|
|
102
131
|
|
|
103
132
|
A push names a category; the category's buttons are registered on the phone. Register them at
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),Object.defineProperty(exports,"APP_API_DEFAULTS",{enumerable:!0,get:function(){return _createAppApi.APP_API_DEFAULTS}}),Object.defineProperty(exports,"LIST_TAG_ID",{enumerable:!0,get:function(){return _tags.LIST_TAG_ID}}),Object.defineProperty(exports,"buildListQuery",{enumerable:!0,get:function(){return _paging.buildListQuery}}),Object.defineProperty(exports,"createApiBaseQuery",{enumerable:!0,get:function(){return _apiBaseQuery.createApiBaseQuery}}),Object.defineProperty(exports,"createAppApi",{enumerable:!0,get:function(){return _createAppApi.createAppApi}}),Object.defineProperty(exports,"getNextPageParam",{enumerable:!0,get:function(){return _paging.getNextPageParam}}),Object.defineProperty(exports,"getPreviousPageParam",{enumerable:!0,get:function(){return _paging.getPreviousPageParam}}),Object.defineProperty(exports,"hasNextPage",{enumerable:!0,get:function(){return _paging.hasNextPage}}),Object.defineProperty(exports,"hasPreviousPage",{enumerable:!0,get:function(){return _paging.hasPreviousPage}}),Object.defineProperty(exports,"itemTag",{enumerable:!0,get:function(){return _tags.itemTag}}),Object.defineProperty(exports,"listTag",{enumerable:!0,get:function(){return _tags.listTag}}),Object.defineProperty(exports,"providesList",{enumerable:!0,get:function(){return _tags.providesList}}),Object.defineProperty(exports,"providesPagedList",{enumerable:!0,get:function(){return _tags.providesPagedList}}),Object.defineProperty(exports,"setupApiListeners",{enumerable:!0,get:function(){return _listeners.setupApiListeners}});var _apiBaseQuery=require("./apiBaseQuery.js"),_createAppApi=require("./createAppApi.js"),_listeners=require("./listeners.js"),_paging=require("./paging.js"),_tags=require("./tags.js");
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),Object.defineProperty(exports,"APP_API_DEFAULTS",{enumerable:!0,get:function(){return _createAppApi.APP_API_DEFAULTS}}),Object.defineProperty(exports,"LIST_TAG_ID",{enumerable:!0,get:function(){return _tags.LIST_TAG_ID}}),Object.defineProperty(exports,"PERSISTED_QUERIES_RESTORED",{enumerable:!0,get:function(){return _persistedQueries.PERSISTED_QUERIES_RESTORED}}),Object.defineProperty(exports,"buildListQuery",{enumerable:!0,get:function(){return _paging.buildListQuery}}),Object.defineProperty(exports,"createApiBaseQuery",{enumerable:!0,get:function(){return _apiBaseQuery.createApiBaseQuery}}),Object.defineProperty(exports,"createAppApi",{enumerable:!0,get:function(){return _createAppApi.createAppApi}}),Object.defineProperty(exports,"createPersistedQueries",{enumerable:!0,get:function(){return _persistedQueries.createPersistedQueries}}),Object.defineProperty(exports,"getNextPageParam",{enumerable:!0,get:function(){return _paging.getNextPageParam}}),Object.defineProperty(exports,"getPreviousPageParam",{enumerable:!0,get:function(){return _paging.getPreviousPageParam}}),Object.defineProperty(exports,"hasNextPage",{enumerable:!0,get:function(){return _paging.hasNextPage}}),Object.defineProperty(exports,"hasPreviousPage",{enumerable:!0,get:function(){return _paging.hasPreviousPage}}),Object.defineProperty(exports,"itemTag",{enumerable:!0,get:function(){return _tags.itemTag}}),Object.defineProperty(exports,"listTag",{enumerable:!0,get:function(){return _tags.listTag}}),Object.defineProperty(exports,"providesList",{enumerable:!0,get:function(){return _tags.providesList}}),Object.defineProperty(exports,"providesPagedList",{enumerable:!0,get:function(){return _tags.providesPagedList}}),Object.defineProperty(exports,"setupApiListeners",{enumerable:!0,get:function(){return _listeners.setupApiListeners}});var _apiBaseQuery=require("./apiBaseQuery.js"),_createAppApi=require("./createAppApi.js"),_listeners=require("./listeners.js"),_paging=require("./paging.js"),_persistedQueries=require("./persistedQueries.js"),_tags=require("./tags.js");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.createPersistedQueries=exports.PERSISTED_QUERIES_RESTORED=void 0;var _index=require("../logger/index.js"),_index2=require("../storage/index.js");const PERSISTED_QUERIES_RESTORED=exports.PERSISTED_QUERIES_RESTORED="@webority/mobile-core/query/persistedQueriesRestored",SAVED_VERSION=1,FULFILLED_SUFFIX="/executeQuery/fulfilled",fulfilledEndpointName=e=>{if("object"!=typeof e||null===e)return;const{type:r,meta:t}=e;if("string"!=typeof r||!r.endsWith(FULFILLED_SUFFIX))return;const s=t?.arg?.endpointName;return"string"==typeof s?s:void 0},isSavedQueries=e=>{if("object"!=typeof e||null===e)return!1;const{version:r,queries:t}=e;return 1===r&&"object"==typeof t&&null!==t&&!Array.isArray(t)},createPersistedQueries=e=>{const{storageKey:r,endpoints:t,reducerPath:s="api"}=e,i=new Set(t);let n=Promise.resolve(),o=0;const enqueue=e=>(n=n.then(e).catch(e=>_index.Logger.warn("[persistedQueries] storage step failed",e)),n);return{extractRehydrationInfo:(e,r)=>{if(e.type!==PERSISTED_QUERIES_RESTORED||r.reducerPath!==s)return;const{queries:t}=e.payload;return{queries:t,mutations:{},provided:{},subscriptions:{},config:{}}},middleware:e=>t=>n=>{const a=t(n),d=fulfilledEndpointName(n);if(void 0!==d&&i.has(d)){const t=e.getState(),n=t[s]?.queries??{},a={version:1,queries:Object.fromEntries(Object.entries(n).filter(([,e])=>void 0!==e&&i.has(e.endpointName)&&"fulfilled"===e.status))},d=o;enqueue(async()=>{if(d!==o)return;await _index2.Storage.setJson(r,a)||_index.Logger.warn("[persistedQueries] save failed")})}return a},restore:async e=>{const t=await _index2.Storage.getJson(r);isSavedQueries(t)&&e({type:PERSISTED_QUERIES_RESTORED,payload:{queries:t.queries}})},clear:()=>(o+=1,enqueue(async()=>{await _index2.Storage.removeItem(r)||_index.Logger.warn("[persistedQueries] clear failed; the saved list is still on the phone")}))}};exports.createPersistedQueries=createPersistedQueries;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";export{createApiBaseQuery}from"./apiBaseQuery.js";export{APP_API_DEFAULTS,createAppApi}from"./createAppApi.js";export{setupApiListeners}from"./listeners.js";export{buildListQuery,getNextPageParam,getPreviousPageParam,hasNextPage,hasPreviousPage}from"./paging.js";export{itemTag,LIST_TAG_ID,listTag,providesList,providesPagedList}from"./tags.js";
|
|
1
|
+
"use strict";export{createApiBaseQuery}from"./apiBaseQuery.js";export{APP_API_DEFAULTS,createAppApi}from"./createAppApi.js";export{setupApiListeners}from"./listeners.js";export{buildListQuery,getNextPageParam,getPreviousPageParam,hasNextPage,hasPreviousPage}from"./paging.js";export{createPersistedQueries,PERSISTED_QUERIES_RESTORED}from"./persistedQueries.js";export{itemTag,LIST_TAG_ID,listTag,providesList,providesPagedList}from"./tags.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{Logger}from"../logger/index.js";import{Storage}from"../storage/index.js";export const PERSISTED_QUERIES_RESTORED="@webority/mobile-core/query/persistedQueriesRestored";const SAVED_VERSION=1,FULFILLED_SUFFIX="/executeQuery/fulfilled",fulfilledEndpointName=e=>{if("object"!=typeof e||null===e)return;const{type:t,meta:r}=e;if("string"!=typeof t||!t.endsWith(FULFILLED_SUFFIX))return;const s=r?.arg?.endpointName;return"string"==typeof s?s:void 0},isSavedQueries=e=>{if("object"!=typeof e||null===e)return!1;const{version:t,queries:r}=e;return 1===t&&"object"==typeof r&&null!==r&&!Array.isArray(r)};export const createPersistedQueries=e=>{const{storageKey:t,endpoints:r,reducerPath:s="api"}=e,i=new Set(r);let o=Promise.resolve(),n=0;const enqueue=e=>(o=o.then(e).catch(e=>Logger.warn("[persistedQueries] storage step failed",e)),o);return{extractRehydrationInfo:(e,t)=>{if(e.type!==PERSISTED_QUERIES_RESTORED||t.reducerPath!==s)return;const{queries:r}=e.payload;return{queries:r,mutations:{},provided:{},subscriptions:{},config:{}}},middleware:e=>r=>o=>{const a=r(o),u=fulfilledEndpointName(o);if(void 0!==u&&i.has(u)){const r=e.getState(),o=r[s]?.queries??{},a={version:1,queries:Object.fromEntries(Object.entries(o).filter(([,e])=>void 0!==e&&i.has(e.endpointName)&&"fulfilled"===e.status))},u=n;enqueue(async()=>{if(u!==n)return;await Storage.setJson(t,a)||Logger.warn("[persistedQueries] save failed")})}return a},restore:async e=>{const r=await Storage.getJson(t);isSavedQueries(r)&&e({type:PERSISTED_QUERIES_RESTORED,payload:{queries:r.queries}})},clear:()=>(n+=1,enqueue(async()=>{await Storage.removeItem(t)||Logger.warn("[persistedQueries] clear failed; the saved list is still on the phone")}))}};
|
|
@@ -4,6 +4,8 @@ export { APP_API_DEFAULTS, createAppApi } from './createAppApi';
|
|
|
4
4
|
export { setupApiListeners } from './listeners';
|
|
5
5
|
export type { ListFilterValue, ListQuery, PagedDataObject, PagedListRequest, SortOrder } from './paging';
|
|
6
6
|
export { buildListQuery, getNextPageParam, getPreviousPageParam, hasNextPage, hasPreviousPage } from './paging';
|
|
7
|
+
export type { PersistedQueries, PersistedQueriesOptions } from './persistedQueries';
|
|
8
|
+
export { createPersistedQueries, PERSISTED_QUERIES_RESTORED } from './persistedQueries';
|
|
7
9
|
export type { ApiTag, TagId } from './tags';
|
|
8
10
|
export { itemTag, LIST_TAG_ID, listTag, providesList, providesPagedList } from './tags';
|
|
9
11
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Dispatch, Middleware, UnknownAction } from '@reduxjs/toolkit';
|
|
2
|
+
import type { CombinedState } from '@reduxjs/toolkit/query';
|
|
3
|
+
export declare const PERSISTED_QUERIES_RESTORED = "@webority/mobile-core/query/persistedQueriesRestored";
|
|
4
|
+
export interface PersistedQueriesOptions {
|
|
5
|
+
storageKey: string;
|
|
6
|
+
endpoints: readonly string[];
|
|
7
|
+
reducerPath?: string;
|
|
8
|
+
}
|
|
9
|
+
type RehydratedState = CombinedState<any, any, any>;
|
|
10
|
+
export interface PersistedQueries {
|
|
11
|
+
extractRehydrationInfo: (action: UnknownAction, meta: {
|
|
12
|
+
reducerPath: string;
|
|
13
|
+
}) => RehydratedState | undefined;
|
|
14
|
+
middleware: Middleware;
|
|
15
|
+
restore: (dispatch: Dispatch) => Promise<void>;
|
|
16
|
+
clear: () => Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Keeps the chosen RTK Query results on the phone so they show offline. Pass
|
|
20
|
+
* `extractRehydrationInfo` to `createAppApi`, add `middleware` after the api's
|
|
21
|
+
* middleware, call `restore` once after the store is created, and call `clear`
|
|
22
|
+
* on sign-out so the next person on the phone never sees the last person's data.
|
|
23
|
+
*/
|
|
24
|
+
export declare const createPersistedQueries: (options: PersistedQueriesOptions) => PersistedQueries;
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=persistedQueries.d.ts.map
|
|
@@ -4,6 +4,8 @@ export { APP_API_DEFAULTS, createAppApi } from './createAppApi.js';
|
|
|
4
4
|
export { setupApiListeners } from './listeners.js';
|
|
5
5
|
export type { ListFilterValue, ListQuery, PagedDataObject, PagedListRequest, SortOrder } from './paging.js';
|
|
6
6
|
export { buildListQuery, getNextPageParam, getPreviousPageParam, hasNextPage, hasPreviousPage } from './paging.js';
|
|
7
|
+
export type { PersistedQueries, PersistedQueriesOptions } from './persistedQueries.js';
|
|
8
|
+
export { createPersistedQueries, PERSISTED_QUERIES_RESTORED } from './persistedQueries.js';
|
|
7
9
|
export type { ApiTag, TagId } from './tags.js';
|
|
8
10
|
export { itemTag, LIST_TAG_ID, listTag, providesList, providesPagedList } from './tags.js';
|
|
9
11
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Dispatch, Middleware, UnknownAction } from '@reduxjs/toolkit';
|
|
2
|
+
import type { CombinedState } from '@reduxjs/toolkit/query';
|
|
3
|
+
export declare const PERSISTED_QUERIES_RESTORED = "@webority/mobile-core/query/persistedQueriesRestored";
|
|
4
|
+
export interface PersistedQueriesOptions {
|
|
5
|
+
storageKey: string;
|
|
6
|
+
endpoints: readonly string[];
|
|
7
|
+
reducerPath?: string;
|
|
8
|
+
}
|
|
9
|
+
type RehydratedState = CombinedState<any, any, any>;
|
|
10
|
+
export interface PersistedQueries {
|
|
11
|
+
extractRehydrationInfo: (action: UnknownAction, meta: {
|
|
12
|
+
reducerPath: string;
|
|
13
|
+
}) => RehydratedState | undefined;
|
|
14
|
+
middleware: Middleware;
|
|
15
|
+
restore: (dispatch: Dispatch) => Promise<void>;
|
|
16
|
+
clear: () => Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Keeps the chosen RTK Query results on the phone so they show offline. Pass
|
|
20
|
+
* `extractRehydrationInfo` to `createAppApi`, add `middleware` after the api's
|
|
21
|
+
* middleware, call `restore` once after the store is created, and call `clear`
|
|
22
|
+
* on sign-out so the next person on the phone never sees the last person's data.
|
|
23
|
+
*/
|
|
24
|
+
export declare const createPersistedQueries: (options: PersistedQueriesOptions) => PersistedQueries;
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=persistedQueries.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webority/mobile-core",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.7",
|
|
4
4
|
"description": "Platform layer for Webority React Native apps: HTTP clients, auth/token storage, config, logging, formatters, validators, network status, permissions, storage, version checks and over-the-air updates. No UI.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-native",
|