Skip to content

Commit f417133

Browse files
authored
Remote Config API (#204)
Add Remote Config
1 parent 33f1e6a commit f417133

File tree

12 files changed

+523
-312
lines changed

12 files changed

+523
-312
lines changed

reactfire/auth/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export function useIdTokenResult(user: User, forceRefresh: boolean = false) {
5252

5353
const idToken$ = from(user.getIdTokenResult(forceRefresh));
5454

55-
return useObservable(idToken$, `${user.uid}-claims`);
55+
return useObservable<any>(idToken$, `${user.uid}-claims`);
5656
}
5757

5858
export interface AuthCheckProps {

reactfire/firebaseApp/sdk.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useFirebaseApp, preloadRequest, usePreloadedRequest } from '..';
2+
23
enum SDK {
34
ANALYTICS = 'analytics',
45
AUTH = 'auth',

reactfire/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,6 @@ export * from './database';
2020
export * from './firebaseApp';
2121
export * from './firestore';
2222
export * from './performance';
23+
export * from './remote-config';
2324
export * from './storage';
2425
export * from './useObservable';

reactfire/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"@firebase/app": "^0.4.8",
3737
"@firebase/app-types": "^0.4.0",
3838
"@firebase/testing": "^0.11.4",
39+
"@rollup/plugin-node-resolve": "^7.0.0",
3940
"@testing-library/jest-dom": "^4.1.1",
4041
"@testing-library/react": "^9.3.0",
4142
"@testing-library/react-hooks": "^3.1.0",
@@ -46,7 +47,6 @@
4647
"jest": "~24.9.0",
4748
"react-test-renderer": "^16.9.0",
4849
"rollup": "^1.26.3",
49-
"@rollup/plugin-node-resolve": "^7.0.0",
5050
"typescript": "^3.4.5"
5151
}
5252
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { Observable } from 'rxjs';
2+
3+
type RemoteConfig = import('firebase/app').remoteConfig.RemoteConfig;
4+
type RemoteConfigValue = import('firebase/app').remoteConfig.Value;
5+
6+
export type AllParameters = {
7+
[key: string]: RemoteConfigValue;
8+
};
9+
10+
interface ParameterSettings<T> {
11+
remoteConfig: RemoteConfig;
12+
key: string;
13+
getter: (key: string) => T;
14+
}
15+
16+
// TODO(davideast): Replace with RxFire functions when they land
17+
function parameter$<T>({
18+
remoteConfig,
19+
key,
20+
getter
21+
}: ParameterSettings<T>): Observable<T> {
22+
return new Observable(subscriber => {
23+
remoteConfig.ensureInitialized().then(() => {
24+
// 'this' for the getter loses context in the next()
25+
// call, so it needs to be bound.
26+
subscriber.next(getter.bind(remoteConfig)(key));
27+
});
28+
});
29+
}
30+
31+
export function getValue(remoteConfig: RemoteConfig, key: string) {
32+
const getter = remoteConfig.getValue;
33+
return parameter$({ remoteConfig, key, getter });
34+
}
35+
36+
export function getString(remoteConfig: RemoteConfig, key: string) {
37+
const getter = remoteConfig.getString;
38+
return parameter$<string>({ remoteConfig, key, getter });
39+
}
40+
41+
export function getNumber(remoteConfig: RemoteConfig, key: string) {
42+
const getter = remoteConfig.getNumber;
43+
return parameter$<number>({ remoteConfig, key, getter });
44+
}
45+
46+
export function getBoolean(remoteConfig: RemoteConfig, key: string) {
47+
const getter = remoteConfig.getBoolean;
48+
return parameter$<boolean>({ remoteConfig, key, getter });
49+
}
50+
51+
export function getAll(remoteConfig: RemoteConfig) {
52+
const getter = remoteConfig.getAll;
53+
// No key is needed for getAll()
54+
return parameter$<AllParameters>({ remoteConfig, key: null, getter });
55+
}

reactfire/remote-config/index.tsx

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { useRemoteConfig } from '../firebaseApp';
2+
import { useObservable } from '../useObservable';
3+
import {
4+
getValue,
5+
getString,
6+
getBoolean,
7+
getNumber,
8+
getAll,
9+
AllParameters
10+
} from './getValue';
11+
import { Observable } from 'rxjs';
12+
13+
type RemoteConfig = import('firebase/app').remoteConfig.RemoteConfig;
14+
type RemoteConfigValue = import('firebase/app').remoteConfig.Value;
15+
type Getter$<T> = (remoteConfig: RemoteConfig, key: string) => Observable<T>;
16+
17+
/**
18+
* Helper function to construct type safe functions. Since Remote Config has
19+
* methods that return different types for values, we need to be extra safe
20+
* to make sure we are not returning improper types by accident.
21+
* @param key
22+
* @param getter
23+
* @param remoteConfig
24+
*/
25+
function typeSafeUse<T>(
26+
key: string,
27+
getter: Getter$<T>,
28+
remoteConfig?: RemoteConfig
29+
): T {
30+
remoteConfig = remoteConfig || useRemoteConfig()();
31+
const $value = getter(remoteConfig, key);
32+
return useObservable<T>($value, `remoteconfig:${key}`);
33+
}
34+
35+
/**
36+
* Accepts a key and optionally a Remote Config instance. Returns a
37+
* Remote Config Value.
38+
*
39+
* @param key The parameter key in Remote Config
40+
* @param remoteConfig Optional instance. If not provided ReactFire will either grab the default instance or lazy load.
41+
*/
42+
export function useRemoteConfigValue(
43+
key: string,
44+
remoteConfig?: RemoteConfig
45+
): RemoteConfigValue {
46+
return typeSafeUse<RemoteConfigValue>(key, getValue, remoteConfig);
47+
}
48+
49+
/**
50+
* Convience method similar to useRemoteConfigValue. Returns a `string` from a Remote Config parameter.
51+
* @param key The parameter key in Remote Config
52+
* @param remoteConfig Optional instance. If not provided ReactFire will either grab the default instance or lazy load.
53+
*/
54+
export function useRemoteConfigString(
55+
key: string,
56+
remoteConfig?: RemoteConfig
57+
): string {
58+
return typeSafeUse<string>(key, getString, remoteConfig);
59+
}
60+
61+
/**
62+
* Convience method similar to useRemoteConfigValue. Returns a `number` from a Remote Config parameter.
63+
* @param key The parameter key in Remote Config
64+
* @param remoteConfig Optional instance. If not provided ReactFire will either grab the default instance or lazy load.
65+
*/
66+
export function useRemoteConfigNumber(
67+
key: string,
68+
remoteConfig?: RemoteConfig
69+
): number {
70+
return typeSafeUse<number>(key, getNumber, remoteConfig);
71+
}
72+
73+
/**
74+
* Convience method similar to useRemoteConfigValue. Returns a `boolean` from a Remote Config parameter.
75+
* @param key The parameter key in Remote Config
76+
* @param remoteConfig Optional instance. If not provided ReactFire will either grab the default instance or lazy load.
77+
*/
78+
export function useRemoteConfigBoolean(
79+
key: string,
80+
remoteConfig?: RemoteConfig
81+
) {
82+
return typeSafeUse<boolean>(key, getBoolean, remoteConfig);
83+
}
84+
85+
/**
86+
* Convience method similar to useRemoteConfigValue. Returns allRemote Config parameters.
87+
* @param key The parameter key in Remote Config
88+
* @param remoteConfig Optional instance. If not provided ReactFire will either grab the default instance or lazy load.
89+
*/
90+
export function useRemoteConfigAll(
91+
key: string,
92+
remoteConfig?: RemoteConfig
93+
): AllParameters {
94+
return typeSafeUse<AllParameters>(key, getAll, remoteConfig);
95+
}

reactfire/useObservable/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,11 @@ export function usePreloadedRequest(preloadResult: { requestId: string }) {
4545
return request.value;
4646
}
4747

48-
export function useObservable(
49-
observable$: Observable<any>,
48+
export function useObservable<T>(
49+
observable$: Observable<T | any>,
5050
observableId: string,
51-
startWithValue?: any
52-
) {
51+
startWithValue?: T | any
52+
): T {
5353
if (!observableId) {
5454
throw new Error('cannot call useObservable without an observableId');
5555
}

sample-simple/build/favicon.ico

97.3 KB
Binary file not shown.

sample-simple/build/manifest.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"short_name": "ReactFire Squares",
3+
"name": "ReactFire Sample",
4+
"icons": [
5+
{
6+
"src": "favicon.ico",
7+
"sizes": "64x64 32x32 24x24 16x16",
8+
"type": "image/x-icon"
9+
}
10+
],
11+
"start_url": ".",
12+
"display": "standalone",
13+
"theme_color": "#000000",
14+
"background_color": "#ffffff"
15+
}

sample/src/App.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@ import AuthButton from './Auth';
33
import FirestoreCounter from './Firestore';
44
import Storage from './Storage';
55
import RealtimeDatabase from './RealtimeDatabase';
6+
import RemoteConfig from './RemoteConfig';
67
import {
78
preloadFirestoreDoc,
89
useFirebaseApp,
910
preloadUser,
1011
preloadAuth,
1112
preloadFirestore,
1213
preloadDatabase,
13-
preloadStorage
14+
preloadStorage,
15+
preloadRemoteConfig
1416
} from 'reactfire';
1517

1618
const Fire = () => (
@@ -39,7 +41,8 @@ const preloadSDKs = firebaseApp => {
3941
preloadFirestore(firebaseApp),
4042
preloadDatabase(firebaseApp),
4143
preloadStorage(firebaseApp),
42-
preloadAuth(firebaseApp)
44+
preloadAuth(firebaseApp),
45+
preloadRemoteConfig(firebaseApp)
4346
]);
4447
};
4548

@@ -85,6 +88,10 @@ const App = () => {
8588
<Card title="Realtime Database">
8689
<RealtimeDatabase />
8790
</Card>
91+
92+
<Card title="Remote Config">
93+
<RemoteConfig />
94+
</Card>
8895
</div>
8996
</>
9097
);

0 commit comments

Comments
 (0)