Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions reactfire/auth/auth.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ describe('AuthCheck', () => {
});

it('can find firebase Auth from Context', () => {
expect(() => render(<Component />)).not.toThrow();
expect(() => render(<Component> </Component>)).not.toThrow();
});

it('can use firebase Auth from props', () => {
Expand All @@ -85,7 +85,7 @@ describe('AuthCheck', () => {
});

it('renders the fallback if a user is not signed in', async () => {
const { getByTestId } = render(<Component />);
const { getByTestId } = render(<Component> </Component>);

await wait(() => expect(getByTestId('signed-out')).toBeInTheDocument());

Expand All @@ -96,13 +96,13 @@ describe('AuthCheck', () => {

it('renders children if a user is logged in', async () => {
mockFirebase.auth().updateUser({ uid: 'testuser' });
const { getByTestId } = render(<Component />);
const { getByTestId } = render(<Component> </Component>);

await wait(() => expect(getByTestId('signed-in')).toBeInTheDocument());
});

it('can switch between logged in and logged out', async () => {
const { getByTestId } = render(<Component />);
const { getByTestId } = render(<Component> </Component>);

await wait(() => expect(getByTestId('signed-out')).toBeInTheDocument());

Expand Down
2 changes: 1 addition & 1 deletion reactfire/auth/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function preloadUser(firebaseApp: firebase.app.App) {
user(auth() as firebase.auth.Auth),
'auth: user'
);
return result.request.promise;
return result.toPromise();
});
}

Expand Down
55 changes: 55 additions & 0 deletions reactfire/firebase-debug.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
[debug] [2020-02-07T01:39:18.184Z] ----------------------------------------------------------------------
[debug] [2020-02-07T01:39:18.186Z] Command: /Users/jamesdaniels/.nvm/versions/node/v10.15.3/bin/node /Users/jamesdaniels/Code/reactfire/reactfire/node_modules/.bin/firebase emulators:exec --only firestore,database jest --no-cache --verbose --detectOpenHandles --forceExit
[debug] [2020-02-07T01:39:18.186Z] CLI Version: 7.12.1
[debug] [2020-02-07T01:39:18.186Z] Platform: darwin
[debug] [2020-02-07T01:39:18.186Z] Node Version: v10.15.3
[debug] [2020-02-07T01:39:18.187Z] Time: Thu Feb 06 2020 17:39:18 GMT-0800 (Pacific Standard Time)
[debug] [2020-02-07T01:39:18.187Z] ----------------------------------------------------------------------
[debug] [2020-02-07T01:39:18.187Z]
[debug] [2020-02-07T01:39:18.196Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
[debug] [2020-02-07T01:39:18.196Z] > authorizing via signed-in user
[warn] ⚠ Could not find config (firebase.json) so using defaults.
[info] i emulators: Starting emulators: firestore, database
[warn] ⚠ No Firestore rules file specified in firebase.json, using default rules.
[info] i firestore: Serving ALL traffic (including WebChannel) on http://localhost:8080
[warn] ⚠ firestore: Support for WebChannel on a separate port (8081) is DEPRECATED and will go away soon. Please use port above instead.
[debug] [2020-02-07T01:39:18.261Z] Ignoring unsupported arg: projectId
[debug] [2020-02-07T01:39:18.261Z] Ignoring unsupported arg: auto_download
[debug] [2020-02-07T01:39:18.261Z] Starting emulator firestore with command {"binary":"java","args":["-Duser.language=en","-jar","/Users/jamesdaniels/.cache/firebase/emulators/cloud-firestore-emulator-v1.10.2.jar","--host","localhost","--port",8080,"--webchannel_port",8081],"optionalArgs":["port","webchannel_port","host","rules","functions_emulator"],"joinArgs":false}
[info] i firestore: Emulator logging to firestore-debug.log
[debug] [2020-02-07T01:39:19.679Z] API endpoint: http://localhost
[debug] [2020-02-07T01:39:19.679Z] :8080
If you are using a library that supports the FIRESTORE_EMULATOR_HOST environment variable, run:

export FIRESTORE_EMULATOR_HOST=localhost:8080

Dev App Server is now running.


[info] ✔ firestore: Emulator started at http://localhost:8080
[info] i firestore: For testing set FIRESTORE_EMULATOR_HOST=localhost:8080
[warn] ⚠ No Database rules file specified in firebase.json, using default rules.
[debug] [2020-02-07T01:39:19.809Z] Ignoring unsupported arg: projectId
[debug] [2020-02-07T01:39:19.810Z] Ignoring unsupported arg: auto_download
[debug] [2020-02-07T01:39:19.810Z] Starting emulator database with command {"binary":"java","args":["-Duser.language=en","-jar","/Users/jamesdaniels/.cache/firebase/emulators/firebase-database-emulator-v4.3.1.jar","--host","localhost","--port",9000],"optionalArgs":["port","host","functions_emulator_port","functions_emulator_host"],"joinArgs":false}
[info] i database: Emulator logging to database-debug.log
[debug] [2020-02-07T01:39:20.698Z] WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by io.netty.util.internal.ReflectionUtil (file:/Users/jamesdaniels/.cache/firebase/emulators/firebase-database-emulator-v4.3.1.jar) to field sun.nio.ch.SelectorImpl.selectedKeys
WARNING: Please consider reporting this to the maintainers of io.netty.util.internal.ReflectionUtil
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

[debug] [2020-02-07T01:39:21.781Z] 17:39:21.779 [NamespaceSystem-akka.actor.default-dispatcher-4] INFO akka.event.slf4j.Slf4jLogger - Slf4jLogger started

[debug] [2020-02-07T01:39:22.064Z] 17:39:22.064 [main] INFO com.firebase.server.forge.App$ - Listening at localhost:9000

[info] ✔ database: Emulator started at http://localhost:9000
[info] i database: For testing set FIREBASE_DATABASE_EMULATOR_HOST=localhost:9000
[info] i Running script: jest --no-cache --verbose --detectOpenHandles --forceExit
[debug] [2020-02-07T01:39:22.352Z] Running jest --no-cache --verbose --detectOpenHandles --forceExit with environment {"rvm_use_flag":"","npm_package_devDependencies_react_test_renderer":"^16.9.0","TERM_PROGRAM":"iTerm.app","rvm_bin_path":"/Users/jamesdaniels/.rvm/bin","NODE":"/Users/jamesdaniels/.nvm/versions/node/v10.15.3/bin/node","INIT_CWD":"/Users/jamesdaniels/Code/reactfire/reactfire","NVM_CD_FLAGS":"-q","rvm_quiet_flag":"","npm_package_devDependencies_typescript":"^3.4.5","npm_package_homepage":"https://firebaseopensource.com/projects/firebase/reactfire/","npm_config_version_git_tag":"true","SHELL":"/bin/zsh","TERM":"xterm-256color","rvm_gemstone_url":"","npm_package_devDependencies_jest":"~24.9.0","TMPDIR":"/var/folders/tg/0v1n9rmd38lcl75lwmq0lhhm00bnbv/T/","rvm_docs_type":"","Apple_PubSub_Socket_Render":"/private/tmp/com.apple.launchd.v9uRuPAPLm/Render","npm_package_devDependencies__babel_preset_typescript":"^7.8.3","npm_config_init_license":"MIT","TERM_PROGRAM_VERSION":"3.3.7","npm_package_scripts_emulators":"firebase emulators:start --only firestore,database","TERM_SESSION_ID":"w0t0p0:1CF4B145-11F3-4C98-BDB9-9CBFCEBE17F9","rvm_hook":"","npm_package_private":"true","npm_config_registry":"https://registry.yarnpkg.com","ZSH":"/Users/jamesdaniels/.oh-my-zsh","npm_package_repository_url":"git+https://github.com/Firebase/reactfire.git","USER":"jamesdaniels","NVM_DIR":"/Users/jamesdaniels/.nvm","npm_package_devDependencies__testing_library_react":"^9.3.0","npm_package_description":"Firebase library for React","rvm_gemstone_package_file":"","npm_package_license":"MIT","npm_package_scripts_build_dev":"tsc --watch","rvm_path":"/Users/jamesdaniels/.rvm","npm_package_devDependencies_babel_jest":"^24.9.0","npm_package_devDependencies__babel_core":"^7.8.4","SSH_AUTH_SOCK":"/private/tmp/com.apple.launchd.K4d9zd3Th3/Listeners","__CF_USER_TEXT_ENCODING":"0x5D57B:0x0:0x0","npm_package_devDependencies__types_jest":"^24.9.0","rvm_proxy":"","npm_execpath":"/Users/jamesdaniels/.nvm/versions/node/v10.15.3/lib/node_modules/yarn/bin/yarn.js","PAGER":"less","rvm_ruby_file":"","LSCOLORS":"Gxfxcxdxbxegedabagacad","npm_package_devDependencies_firebase_tools":"^7.1.0","SK_SIGNING_PLUGIN":"gnubbyagent","rvm_prefix":"/Users/jamesdaniels","rvm_silent_flag":"","PATH":"/var/folders/tg/0v1n9rmd38lcl75lwmq0lhhm00bnbv/T/yarn--1581039556844-0.049035385658451114:/Users/jamesdaniels/Code/reactfire/reactfire/node_modules/.bin:/Users/jamesdaniels/.config/yarn/link/node_modules/.bin:/Users/jamesdaniels/Code/reactfire/node_modules/.bin:/Users/jamesdaniels/.yarn/bin:/Users/jamesdaniels/.nvm/versions/node/v10.15.3/libexec/lib/node_modules/npm/bin/node-gyp-bin:/Users/jamesdaniels/.nvm/versions/node/v10.15.3/lib/node_modules/npm/bin/node-gyp-bin:/Users/jamesdaniels/.nvm/versions/node/v10.15.3/bin/node_modules/npm/bin/node-gyp-bin:/Users/jamesdaniels/Downloads/google-cloud-sdk/bin:/Users/jamesdaniels/.nvm/versions/node/v10.15.3/bin:/usr/local/git/current/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:/Users/jamesdaniels/.rvm/bin","rvm_ruby_make":"","npm_config_argv":"{\"remain\":[],\"cooked\":[\"run\",\"test\"],\"original\":[\"test\"]}","npm_package_devDependencies_rollup":"^1.26.3","_":"/Users/jamesdaniels/Code/reactfire/reactfire/node_modules/.bin/firebase","npm_package_devDependencies__babel_preset_es2015":"~7.0.0-beta.53","npm_package_devDependencies__testing_library_react_hooks":"^3.1.0","PWD":"/Users/jamesdaniels/Code/reactfire/reactfire","npm_package_devDependencies__rollup_plugin_node_resolve":"^7.0.0","npm_package_scripts_copy_package_json":"cp package.pub.json pub/reactfire/package.json","npm_package_bugs_url":"https://github.com/Firebase/reactfire/issues","npm_lifecycle_event":"test","npm_package_devDependencies_rollup_jest":"^0.0.2","LANG":"en_US.UTF-8","rvm_sdk":"","npm_package_repository_type":"git","npm_package_types":"index.d.ts","npm_package_name":"reactfire-dev","ITERM_PROFILE":"Default","npm_package_devDependencies__babel_preset_react":"^7.8.3","npm_package_devDependencies__babel_preset_env":"^7.8.4","npm_package_dependencies_rxjs":"^6.4.0","npm_package_scripts_build":"rm -rf pub && tsc && yarn copy-package-json && cp ../README.md pub/reactfire/README.md && cp ../LICENSE pub/reactfire/LICENSE && rollup -c","npm_config_version_commit_hooks":"true","XPC_FLAGS":"0x0","npm_config_bin_links":"true","npm_package_devDependencies__firebase_app_types":"^0.4.0","npm_package_scripts_test_dev":"jest --verbose --watch","npm_package_main":"index.js","XPC_SERVICE_NAME":"0","npm_package_scripts_watch":"yarn build && tsc --watch","npm_package_version":"2.0.0","rvm_version":"1.29.9 (latest)","COLORFGBG":"7;0","HOME":"/Users/jamesdaniels","SHLVL":"2","rvm_pretty_print_flag":"","rvm_script_name":"","npm_package_devDependencies_rollup_plugin_babel":"^4.3.3","rvm_ruby_mode":"","npm_package_scripts_test":"firebase emulators:exec --only firestore,database \"jest --no-cache --verbose --detectOpenHandles --forceExit\"","LC_TERMINAL_VERSION":"3.3.7","npm_config_save_prefix":"^","npm_config_strict_ssl":"true","npm_package_devDependencies_firebase_functions_test":"^0.1.6","npm_config_version_git_message":"v%s","npm_package_devDependencies__firebase_testing":"^0.11.4","ITERM_SESSION_ID":"w0t0p0:1CF4B145-11F3-4C98-BDB9-9CBFCEBE17F9","LOGNAME":"jamesdaniels","LESS":"-R","YARN_WRAP_OUTPUT":"false","rvm_alias_expanded":"","npm_lifecycle_script":"firebase emulators:exec --only firestore,database \"jest --no-cache --verbose --detectOpenHandles --forceExit\"","LC_CTYPE":"en_US.UTF-8","NVM_BIN":"/Users/jamesdaniels/.nvm/versions/node/v10.15.3/bin","rvm_nightly_flag":"","npm_config_version_git_sign":"","npm_config_ignore_scripts":"","npm_config_user_agent":"yarn/1.21.1 npm/? node/v10.15.3 darwin x64","rvm_ruby_make_install":"","LC_TERMINAL":"iTerm2","rvm_niceness":"","rvm_ruby_bits":"","npm_package_dependencies_rxfire":"^3.6.6","rvm_bin_flag":"","npm_package_devDependencies__firebase_app":"^0.4.8","npm_config_init_version":"1.0.0","npm_config_ignore_optional":"","rvm_only_path_flag":"","COLORTERM":"truecolor","npm_node_execpath":"/Users/jamesdaniels/.nvm/versions/node/v10.15.3/bin/node","npm_package_devDependencies__testing_library_jest_dom":"^4.1.1","npm_config_version_tag_prefix":"v","FIREBASE_DATABASE_EMULATOR_HOST":"localhost:9000","FIRESTORE_EMULATOR_HOST":"localhost:8080","FIREBASE_FIRESTORE_EMULATOR_ADDRESS":"localhost:8080"}
[debug] [2020-02-07T01:39:26.361Z] Feb 06, 2020 5:39:26 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
INFO: Detected HTTP/2 connection.

[debug] [2020-02-07T01:39:27.571Z] Feb 06, 2020 5:39:27 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
INFO: Detected HTTP/2 connection.

20 changes: 10 additions & 10 deletions reactfire/firebaseApp/sdk.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { useFirebaseApp, preloadRequest, usePreloadedRequest } from '..';
import { useFirebaseApp } from '..';
import { preloadObservable, useObservable } from '../useObservable';
import { from } from 'rxjs';

type RemoteConfig = import('firebase/app').remoteConfig.RemoteConfig;
type Storage = import('firebase/app').storage.Storage;
Expand Down Expand Up @@ -98,7 +100,7 @@ function fetchSDK(
.then(() => settingsCallback(firebaseApp[sdk]))
.then(() => firebaseApp[sdk]);
}
preloadRequest(() => sdkPromise, `firebase-sdk-${sdk}`);
preloadObservable(from(sdkPromise), `firebase-sdk-${sdk}`);

return sdkPromise;
}
Expand All @@ -107,12 +109,7 @@ function useSDK(sdk: SDK, firebaseApp?: firebase.app.App) {
firebaseApp = firebaseApp || useFirebaseApp();

// use the request cache so we don't issue multiple fetches for the sdk
const result = preloadRequest(
() => fetchSDK(sdk, firebaseApp),
`firebase-sdk-${sdk}`
);

return usePreloadedRequest(result);
return useObservable(from(fetchSDK(sdk, firebaseApp)), `firebase-sdk-${sdk}`);
}

export function preloadAuth(
Expand All @@ -123,7 +120,7 @@ export function preloadAuth(
}

export function useAuth(firebaseApp?: firebase.app.App) {
return useSDK(SDK.AUTH, firebaseApp);
return useSDK(SDK.AUTH, firebaseApp) as () => firebase.auth.Auth;
}

export function preloadAnalytics(firebaseApp: firebase.app.App) {
Expand Down Expand Up @@ -197,7 +194,10 @@ export function preloadRemoteConfig(
}

export function useRemoteConfig(firebaseApp?: firebase.app.App) {
return useSDK(SDK.REMOTE_CONFIG, firebaseApp);
return useSDK(
SDK.REMOTE_CONFIG,
firebaseApp
) as () => firebase.remoteConfig.RemoteConfig;
}

export function preloadStorage(
Expand Down
11 changes: 6 additions & 5 deletions reactfire/firestore/firestore.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ describe('Firestore', () => {
await act(() => ref.add(mockData2));

const ReadFirestoreCollection = () => {
const collection = useFirestoreCollection(ref);
const collection = useFirestoreCollection(ref) as any;

return (
<ul data-testid="readSuccess">
Expand Down Expand Up @@ -151,11 +151,12 @@ describe('Firestore', () => {
await act(() => ref.add(mockData2));

const ReadFirestoreCollection = () => {
const list = (useFirestoreCollection(ref) as firestore.QuerySnapshot)
.docs;
const filteredList = (useFirestoreCollection(
const list = ((useFirestoreCollection(
ref
) as any) as firestore.QuerySnapshot).docs;
const filteredList = ((useFirestoreCollection(
filteredRef
) as firestore.QuerySnapshot).docs;
) as any) as firestore.QuerySnapshot).docs;

// filteredList's length should be 1 since we only added one value that matches its query
expect(filteredList.length).toEqual(1);
Expand Down
3 changes: 3 additions & 0 deletions reactfire/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
// "preset": "rollup-jest"
};
11 changes: 7 additions & 4 deletions reactfire/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@
"rxjs": "^6.4.0"
},
"devDependencies": {
"@babel/core": "^7.4.3",
"@babel/preset-env": "^7.4.3",
"@babel/preset-react": "^7.0.0",
"@babel/preset-typescript": "^7.3.3",
"@babel/core": "^7.8.4",
"@babel/preset-env": "^7.8.4",
"@babel/preset-react": "^7.8.3",
"@babel/preset-typescript": "^7.8.3",
"@firebase/app": "^0.4.8",
"@firebase/app-types": "^0.4.0",
"@firebase/testing": "^0.11.4",
Expand All @@ -42,11 +42,14 @@
"@testing-library/react-hooks": "^3.1.0",
"@types/jest": "^24.9.0",
"babel-jest": "^24.9.0",
"@babel/preset-es2015": "~7.0.0-beta.53",
"firebase-functions-test": "^0.1.6",
"firebase-tools": "^7.1.0",
"jest": "~24.9.0",
"react-test-renderer": "^16.9.0",
"rollup": "^1.26.3",
"rollup-plugin-babel": "^4.3.3",
"rollup-jest": "^0.0.2",
"typescript": "^3.4.5"
}
}
4 changes: 2 additions & 2 deletions reactfire/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@
"outDir": "pub/reactfire",
"declaration": true,
"skipLibCheck": true
},
"files": ["index.ts"]
}
//"files": ["index.ts"]
}
98 changes: 98 additions & 0 deletions reactfire/useObservable/behaviorReplaySubject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { Observable, Subject, Subscription, Subscriber } from 'rxjs';
import { tap, share, first } from 'rxjs/operators';

export class BehaviorReplaySubject<T> extends Subject<T> {
private _value: T | undefined;
private _hasValue = false;
private _timeoutHandler: NodeJS.Timeout | undefined;
private _innerSubscriber: any;
private _firstEmission: Promise<void>;
private _resolveFirstEmission: () => void;
private _error: any = undefined;
private _innerObservable: Observable<T>;

constructor(innerObservable: Observable<T>, private _timeoutWindow: number) {
super();
this._firstEmission = new Promise<void>(
resolve => (this._resolveFirstEmission = resolve)
);
this._innerObservable = innerObservable.pipe(
tap(
v => {
this._next(v);
},
e => {
// save the error, so that we can raise on subscription or .value
// resolve the promise, so suspense tries again
this._error = e;
this._resolveFirstEmission();
}
),
share()
);
// warm up the observable
this._innerObservable.pipe(first()).subscribe();
}

get hasValue(): boolean {
// hasValue returns true if there's an error too
// so that after we resolve the promise & useObservable is called again
// we won't throw again
return this._hasValue || !!this._error;
}

get value(): T {
// throw on .value since the first().subscribe would otherwise
// absorb it, clear the error for retry
if (this._error) {
const error = this._error;
this._error = undefined;
if (!this._hasValue) {
// if we cheated around hasValue let's reset the suspense promise too
this._firstEmission = new Promise<void>(
resolve => (this._resolveFirstEmission = resolve)
);
}
throw error;
}
return this._value;
}

get firstEmission(): Promise<void> {
return this._firstEmission;
}

private _next(value: T) {
this._hasValue = true;
this._value = value;
this._resolveFirstEmission();
}

private _reset() {
// set a timeout for reseting the cache, subscriptions will cancel the timeout
// and reschedule again on unsubscribe
this._timeoutHandler = setTimeout(() => {
this._hasValue = false;
this._value = undefined;
this._error = undefined;
this._firstEmission = new Promise<void>(
resolve => (this._resolveFirstEmission = resolve)
);
}, this._timeoutWindow);
}

_subscribe(subscriber: Subscriber<T>): Subscription {
// throw the error if there is one
if (this._error) {
// reset, so they can retry
const error = this._error;
this._error = undefined;
throw error;
}
if (this._timeoutHandler) {
clearTimeout(this._timeoutHandler);
}
this._innerSubscriber = this._innerObservable.subscribe(subscriber);
return this._innerSubscriber.add(this._reset);
}
}
Loading