mirror of
https://github.com/zhigang1992/reactfire.git
synced 2026-06-16 21:21:32 +08:00
refactor: organize per-product
now that we understand the api better, I'm breaking the components and hooks into per-product folders so that they're easier to read/test/maintain
This commit is contained in:
70
reactfire/auth/auth.test.tsx
Normal file
70
reactfire/auth/auth.test.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import { cleanup, render } from '@testing-library/react';
|
||||
import { auth } from 'firebase/app';
|
||||
import 'jest-dom/extend-expect';
|
||||
import * as React from 'react';
|
||||
import { AuthCheck } from '.';
|
||||
import { FirebaseAppProvider } from '..';
|
||||
|
||||
const mockAuth = jest.fn(() => {
|
||||
return {
|
||||
onIdTokenChanged: jest.fn()
|
||||
};
|
||||
});
|
||||
|
||||
const mockFirebase = {
|
||||
auth: mockAuth
|
||||
};
|
||||
|
||||
const Provider = ({ children }) => (
|
||||
<FirebaseAppProvider firebaseApp={mockFirebase}>
|
||||
{children}
|
||||
</FirebaseAppProvider>
|
||||
);
|
||||
|
||||
describe('AuthCheck', () => {
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('can find firebase Auth from Context', () => {
|
||||
expect(() =>
|
||||
render(
|
||||
<Provider>
|
||||
<React.Suspense fallback={'loading'}>
|
||||
<AuthCheck fallback={'loading'}>{'children'}</AuthCheck>
|
||||
</React.Suspense>
|
||||
</Provider>
|
||||
)
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('can use firebase Auth from props', () => {
|
||||
expect(() =>
|
||||
render(
|
||||
<React.Suspense fallback={'loading'}>
|
||||
<AuthCheck
|
||||
fallback={<h1>not signed in</h1>}
|
||||
auth={(mockFirebase.auth() as unknown) as auth.Auth}
|
||||
>
|
||||
{'signed in'}
|
||||
</AuthCheck>
|
||||
</React.Suspense>
|
||||
)
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
test.todo('renders the fallback if a user is not signed in');
|
||||
|
||||
test.todo('renders children if a user is logged in');
|
||||
|
||||
test.todo('checks requiredClaims');
|
||||
});
|
||||
|
||||
describe('useUser', () => {
|
||||
test.todo('can find firebase.auth() from Context');
|
||||
|
||||
test.todo('throws an error if firebase.auth() is not available');
|
||||
|
||||
test.todo('returns the same value as firebase.auth().currentUser()');
|
||||
});
|
||||
@@ -1,16 +1,9 @@
|
||||
import { auth, User } from 'firebase/app';
|
||||
import * as React from 'react';
|
||||
import { auth, performance, User } from 'firebase/app';
|
||||
import { useUser, useFirebaseApp } from './index';
|
||||
const { Suspense, useState, useLayoutEffect } = React;
|
||||
import { user } from 'rxfire/auth';
|
||||
import { useObservable, useFirebaseApp, ReactFireOptions } from '..';
|
||||
|
||||
export interface SuspensePerfProps {
|
||||
children: React.ReactNode;
|
||||
traceId: string;
|
||||
fallback: React.ReactNode;
|
||||
firePerf?: performance.Performance; // TODO(jeff): Add firePerf here when it's available
|
||||
}
|
||||
|
||||
function getPerfFromContext(): performance.Performance {
|
||||
function getAuthFromContext(): auth.Auth {
|
||||
const firebaseApp = useFirebaseApp();
|
||||
|
||||
if (!firebaseApp) {
|
||||
@@ -19,39 +12,34 @@ function getPerfFromContext(): performance.Performance {
|
||||
);
|
||||
}
|
||||
|
||||
const perfFunc = firebaseApp.performance;
|
||||
const authFunc = firebaseApp.auth;
|
||||
|
||||
if (!perfFunc || !perfFunc()) {
|
||||
if (!authFunc || !authFunc()) {
|
||||
throw new Error(
|
||||
"No perf object off of Firebase. Did you forget to import 'firebase/performance' in a component?"
|
||||
"No auth object off of Firebase. Did you forget to import 'firebase/auth' in a component?"
|
||||
);
|
||||
}
|
||||
|
||||
return perfFunc();
|
||||
return authFunc();
|
||||
}
|
||||
|
||||
export function SuspenseWithPerf({
|
||||
children,
|
||||
traceId,
|
||||
fallback,
|
||||
firePerf
|
||||
}: SuspensePerfProps) {
|
||||
firePerf = firePerf || getPerfFromContext();
|
||||
const trace = React.useMemo(() => firePerf.trace(traceId), [traceId]);
|
||||
/**
|
||||
* Subscribe to Firebase auth state changes, including token refresh
|
||||
*
|
||||
* @param auth - the [firebase.auth](https://firebase.google.com/docs/reference/js/firebase.auth) object
|
||||
* @param options
|
||||
*/
|
||||
export function useUser<T = unknown>(
|
||||
auth?: auth.Auth,
|
||||
options?: ReactFireOptions<T>
|
||||
): User | T {
|
||||
auth = auth || getAuthFromContext();
|
||||
|
||||
const Fallback = () => {
|
||||
useLayoutEffect(() => {
|
||||
trace.start();
|
||||
|
||||
return () => {
|
||||
trace.stop();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return <>{fallback}</>;
|
||||
};
|
||||
|
||||
return <Suspense fallback={<Fallback />}>{children}</Suspense>;
|
||||
return useObservable(
|
||||
user(auth),
|
||||
'user',
|
||||
options ? options.startWithValue : undefined
|
||||
);
|
||||
}
|
||||
|
||||
export interface AuthCheckProps {
|
||||
@@ -69,7 +57,7 @@ export function AuthCheck({
|
||||
}: AuthCheckProps): React.ReactNode {
|
||||
const user = useUser<User>(auth);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
React.useLayoutEffect(() => {
|
||||
// TODO(jeff) see if this actually works
|
||||
if (requiredClaims) {
|
||||
throw user.getIdTokenResult().then(idTokenResult => {
|
||||
11
reactfire/database/database.test.tsx
Normal file
11
reactfire/database/database.test.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import 'jest-dom/extend-expect';
|
||||
|
||||
describe('Realtime Database (RTDB)', () => {
|
||||
describe('useDatabaseObject', () => {
|
||||
test.todo("returns the same value as ref.on('value')");
|
||||
});
|
||||
|
||||
describe('useDatabaseList', () => {
|
||||
test.todo("returns the same value as ref.on('value')");
|
||||
});
|
||||
});
|
||||
37
reactfire/database/index.tsx
Normal file
37
reactfire/database/index.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { database } from 'firebase/app';
|
||||
import { list, object, QueryChange } from 'rxfire/database';
|
||||
import { ReactFireOptions, useObservable } from '..';
|
||||
|
||||
/**
|
||||
* Subscribe to a Realtime Database object
|
||||
*
|
||||
* @param ref - Reference to the DB object you want to listen to
|
||||
* @param options
|
||||
*/
|
||||
export function useDatabaseObject<T = unknown>(
|
||||
ref: database.Reference,
|
||||
options?: ReactFireOptions<T>
|
||||
): QueryChange | T {
|
||||
return useObservable(
|
||||
object(ref),
|
||||
ref.toString(),
|
||||
options ? options.startWithValue : undefined
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to a Realtime Database list
|
||||
*
|
||||
* @param ref - Reference to the DB List you want to listen to
|
||||
* @param options
|
||||
*/
|
||||
export function useDatabaseList<T = { [key: string]: unknown }>(
|
||||
ref: database.Reference | database.Query,
|
||||
options?: ReactFireOptions<T[]>
|
||||
): QueryChange[] | T[] {
|
||||
return useObservable(
|
||||
list(ref),
|
||||
ref.toString(),
|
||||
options ? options.startWithValue : undefined
|
||||
);
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import { renderHook, act } from '@testing-library/react-hooks';
|
||||
import { render, cleanup } from '@testing-library/react';
|
||||
import * as React from 'react';
|
||||
import 'jest-dom/extend-expect';
|
||||
import { FirebaseAppProvider } from './index';
|
||||
import { cleanup, render } from '@testing-library/react';
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import * as firebase from 'firebase/app';
|
||||
import { useFirebaseApp } from './firebaseContext';
|
||||
import 'jest-dom/extend-expect';
|
||||
import * as React from 'react';
|
||||
import { useFirebaseApp } from '.';
|
||||
import { FirebaseAppProvider } from './index';
|
||||
|
||||
afterEach(cleanup);
|
||||
|
||||
13
reactfire/firestore/firestore.test.tsx
Normal file
13
reactfire/firestore/firestore.test.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { renderHook, act } from '@testing-library/react-hooks';
|
||||
import * as React from 'react';
|
||||
import 'jest-dom/extend-expect';
|
||||
|
||||
describe('Firestore', () => {
|
||||
describe('useFirestoreDoc', () => {
|
||||
test.todo('returns the same value as ref.onSnapshot()');
|
||||
});
|
||||
|
||||
describe('useFirestoreCollection', () => {
|
||||
test.todo('returns the same value as ref.onSnapshot()');
|
||||
});
|
||||
});
|
||||
37
reactfire/firestore/index.tsx
Normal file
37
reactfire/firestore/index.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { firestore } from 'firebase/app';
|
||||
import { doc, fromCollectionRef } from 'rxfire/firestore';
|
||||
import { ReactFireOptions, useObservable } from '..';
|
||||
|
||||
/**
|
||||
* Suscribe to Firestore Document changes
|
||||
*
|
||||
* @param ref - Reference to the document you want to listen to
|
||||
* @param options
|
||||
*/
|
||||
export function useFirestoreDoc<T = unknown>(
|
||||
ref: firestore.DocumentReference,
|
||||
options?: ReactFireOptions<T>
|
||||
): firestore.DocumentSnapshot | T {
|
||||
return useObservable(
|
||||
doc(ref),
|
||||
ref.path,
|
||||
options ? options.startWithValue : undefined
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to a Firestore collection
|
||||
*
|
||||
* @param ref - Reference to the collection you want to listen to
|
||||
* @param options
|
||||
*/
|
||||
export function useFirestoreCollection<T = { [key: string]: unknown }>(
|
||||
ref: firestore.CollectionReference,
|
||||
options?: ReactFireOptions<T[]>
|
||||
): firestore.QuerySnapshot | T[] {
|
||||
return useObservable(
|
||||
fromCollectionRef(ref),
|
||||
ref.path,
|
||||
options ? options.startWithValue : undefined
|
||||
);
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import { renderHook, act } from '@testing-library/react-hooks';
|
||||
import * as React from 'react';
|
||||
import 'jest-dom/extend-expect';
|
||||
|
||||
describe('useUser', () => {
|
||||
test.todo('can find firebase.auth() from Context');
|
||||
|
||||
test.todo('throws an error if firebase.auth() is not available');
|
||||
|
||||
test.todo('returns the same value as firebase.auth().currentUser()');
|
||||
});
|
||||
|
||||
describe('Firestore', () => {
|
||||
describe('useFirestoreDoc', () => {
|
||||
test.todo('returns the same value as ref.onSnapshot()');
|
||||
});
|
||||
|
||||
describe('useFirestoreCollection', () => {
|
||||
test.todo('returns the same value as ref.onSnapshot()');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Realtime Database (RTDB)', () => {
|
||||
describe('useDatabaseObject', () => {
|
||||
test.todo("returns the same value as ref.on('value')");
|
||||
});
|
||||
|
||||
describe('useDatabaseList', () => {
|
||||
test.todo("returns the same value as ref.on('value')");
|
||||
});
|
||||
});
|
||||
|
||||
describe('Storage', () => {
|
||||
describe('useStorageTask', () => {
|
||||
test.todo('returns the same value as uploadTask');
|
||||
});
|
||||
|
||||
describe('useStorageDownloadURL', () => {
|
||||
test.todo('returns the same value as getDownloadURL');
|
||||
});
|
||||
});
|
||||
@@ -1,179 +1,11 @@
|
||||
import { auth, firestore, User, database, storage } from 'firebase/app';
|
||||
import { user } from 'rxfire/auth';
|
||||
import { fromCollectionRef, doc } from 'rxfire/firestore';
|
||||
import { object, list, QueryChange } from 'rxfire/database';
|
||||
import { useObservable } from './util/use-observable';
|
||||
import { getDownloadURL } from 'rxfire/storage';
|
||||
import { Observable, from } from 'rxjs';
|
||||
import { useFirebaseApp } from './firebaseContext';
|
||||
|
||||
export interface ReactFireOptions<T = unknown> {
|
||||
startWithValue: T;
|
||||
}
|
||||
|
||||
function getAuthFromContext(): auth.Auth {
|
||||
const firebaseApp = useFirebaseApp();
|
||||
|
||||
if (!firebaseApp) {
|
||||
throw new Error(
|
||||
'Firebase not found in context. Either pass it directly to a reactfire hook, or wrap your component in a FirebaseAppProvider'
|
||||
);
|
||||
}
|
||||
|
||||
const authFunc = firebaseApp.auth;
|
||||
|
||||
if (!authFunc || !authFunc()) {
|
||||
throw new Error(
|
||||
"No auth object off of Firebase. Did you forget to import 'firebase/auth' in a component?"
|
||||
);
|
||||
}
|
||||
|
||||
return authFunc();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to Firebase auth state changes, including token refresh
|
||||
*
|
||||
* @param auth - the [firebase.auth](https://firebase.google.com/docs/reference/js/firebase.auth) object
|
||||
* @param options
|
||||
*/
|
||||
export function useUser<T = unknown>(
|
||||
auth?: auth.Auth,
|
||||
options?: ReactFireOptions<T>
|
||||
): User | T {
|
||||
auth = auth || getAuthFromContext();
|
||||
|
||||
return useObservable(
|
||||
user(auth),
|
||||
'user',
|
||||
options ? options.startWithValue : undefined
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Suscribe to Firestore Document changes
|
||||
*
|
||||
* @param ref - Reference to the document you want to listen to
|
||||
* @param options
|
||||
*/
|
||||
export function useFirestoreDoc<T = unknown>(
|
||||
ref: firestore.DocumentReference,
|
||||
options?: ReactFireOptions<T>
|
||||
): firestore.DocumentSnapshot | T {
|
||||
return useObservable(
|
||||
doc(ref),
|
||||
ref.path,
|
||||
options ? options.startWithValue : undefined
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to a Firestore collection
|
||||
*
|
||||
* @param ref - Reference to the collection you want to listen to
|
||||
* @param options
|
||||
*/
|
||||
export function useFirestoreCollection<T = { [key: string]: unknown }>(
|
||||
ref: firestore.CollectionReference,
|
||||
options?: ReactFireOptions<T[]>
|
||||
): firestore.QuerySnapshot | T[] {
|
||||
return useObservable(
|
||||
fromCollectionRef(ref),
|
||||
ref.path,
|
||||
options ? options.startWithValue : undefined
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to a Realtime Database object
|
||||
*
|
||||
* @param ref - Reference to the DB object you want to listen to
|
||||
* @param options
|
||||
*/
|
||||
export function useDatabaseObject<T = unknown>(
|
||||
ref: database.Reference,
|
||||
options?: ReactFireOptions<T>
|
||||
): QueryChange | T {
|
||||
return useObservable(
|
||||
object(ref),
|
||||
ref.toString(),
|
||||
options ? options.startWithValue : undefined
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to a Realtime Database list
|
||||
*
|
||||
* @param ref - Reference to the DB List you want to listen to
|
||||
* @param options
|
||||
*/
|
||||
export function useDatabaseList<T = { [key: string]: unknown }>(
|
||||
ref: database.Reference | database.Query,
|
||||
options?: ReactFireOptions<T[]>
|
||||
): QueryChange[] | T[] {
|
||||
return useObservable(
|
||||
list(ref),
|
||||
ref.toString(),
|
||||
options ? options.startWithValue : undefined
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* modified version of rxFire's _fromTask
|
||||
*
|
||||
* @param task
|
||||
*/
|
||||
function _fromTask(task: storage.UploadTask) {
|
||||
return new Observable<storage.UploadTaskSnapshot>(subscriber => {
|
||||
const progress = (snap: storage.UploadTaskSnapshot) => {
|
||||
return subscriber.next(snap);
|
||||
};
|
||||
const error = e => subscriber.error(e);
|
||||
const complete = () => {
|
||||
return subscriber.complete();
|
||||
};
|
||||
task.on('state_changed', progress, error, complete);
|
||||
|
||||
// I REMOVED THE UNSUBSCRIBE RETURN BECAUSE IT CANCELS THE UPLOAD
|
||||
// https://github.com/firebase/firebase-js-sdk/issues/1659
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to the progress of a storage task
|
||||
*
|
||||
* @param task - the task you want to listen to
|
||||
* @param ref - reference to the blob the task is acting on
|
||||
* @param options
|
||||
*/
|
||||
export function useStorageTask<T = unknown>(
|
||||
task: storage.UploadTask,
|
||||
ref: storage.Reference,
|
||||
options?: ReactFireOptions<T>
|
||||
): storage.UploadTaskSnapshot | T {
|
||||
return useObservable(
|
||||
_fromTask(task),
|
||||
'upload' + ref.toString(),
|
||||
options ? options.startWithValue : undefined
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to a storage ref's download URL
|
||||
*
|
||||
* @param ref - reference to the blob you want to download
|
||||
* @param options
|
||||
*/
|
||||
export function useStorageDownloadURL<T = string>(
|
||||
ref: storage.Reference,
|
||||
options?: ReactFireOptions<T>
|
||||
): string | T {
|
||||
return useObservable(
|
||||
getDownloadURL(ref),
|
||||
'download' + ref.toString(),
|
||||
options ? options.startWithValue : undefined
|
||||
);
|
||||
}
|
||||
|
||||
export { SuspenseWithPerf, AuthCheck } from './components';
|
||||
export { FirebaseAppProvider, useFirebaseApp } from './firebaseContext';
|
||||
export * from './auth';
|
||||
export * from './database';
|
||||
export * from './firebaseApp';
|
||||
export * from './firestore';
|
||||
export * from './performance';
|
||||
export * from './storage';
|
||||
export * from './useObservable';
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"build-dev": "tsc",
|
||||
"test-dev": "jest --verbose --watch",
|
||||
"test": "jest --no-cache",
|
||||
"watch": "tsc index.ts --lib DOM,ES2018 --watch --sourceMap --declaration --jsx react",
|
||||
"watch": "tsc --watch",
|
||||
"build": "rm -rf pub && tsc && cp package.pub.json pub/reactfire/package.json && cp ../README.md pub/reactfire/README.md"
|
||||
},
|
||||
"repository": {
|
||||
|
||||
54
reactfire/performance/index.tsx
Normal file
54
reactfire/performance/index.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import { performance } from 'firebase/app';
|
||||
import * as React from 'react';
|
||||
import { useFirebaseApp } from '..';
|
||||
|
||||
export interface SuspensePerfProps {
|
||||
children: React.ReactNode;
|
||||
traceId: string;
|
||||
fallback: React.ReactNode;
|
||||
firePerf?: performance.Performance;
|
||||
}
|
||||
|
||||
function getPerfFromContext(): performance.Performance {
|
||||
const firebaseApp = useFirebaseApp();
|
||||
|
||||
if (!firebaseApp) {
|
||||
throw new Error(
|
||||
'Firebase not found in context. Either pass it directly to a reactfire hook, or wrap your component in a FirebaseAppProvider'
|
||||
);
|
||||
}
|
||||
|
||||
const perfFunc = firebaseApp.performance;
|
||||
|
||||
if (!perfFunc || !perfFunc()) {
|
||||
throw new Error(
|
||||
"No perf object off of Firebase. Did you forget to import 'firebase/performance' in a component?"
|
||||
);
|
||||
}
|
||||
|
||||
return perfFunc();
|
||||
}
|
||||
|
||||
export function SuspenseWithPerf({
|
||||
children,
|
||||
traceId,
|
||||
fallback,
|
||||
firePerf
|
||||
}: SuspensePerfProps) {
|
||||
firePerf = firePerf || getPerfFromContext();
|
||||
const trace = React.useMemo(() => firePerf.trace(traceId), [traceId]);
|
||||
|
||||
const Fallback = () => {
|
||||
React.useLayoutEffect(() => {
|
||||
trace.start();
|
||||
|
||||
return () => {
|
||||
trace.stop();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return <>{fallback}</>;
|
||||
};
|
||||
|
||||
return <React.Suspense fallback={<Fallback />}>{children}</React.Suspense>;
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import { of, Subject, Observable, observable } from 'rxjs';
|
||||
import { render, waitForElement, cleanup, act } from '@testing-library/react';
|
||||
import * as React from 'react';
|
||||
import { act, cleanup, render, waitForElement } from '@testing-library/react';
|
||||
import { performance } from 'firebase/app';
|
||||
import 'jest-dom/extend-expect';
|
||||
import { SuspenseWithPerf, AuthCheck } from './components';
|
||||
import { FirebaseAppProvider } from './firebaseContext';
|
||||
import { auth, performance, User } from 'firebase/app';
|
||||
import * as React from 'react';
|
||||
import { Subject } from 'rxjs';
|
||||
import { SuspenseWithPerf } from '.';
|
||||
import { FirebaseAppProvider } from '../firebaseApp';
|
||||
|
||||
const traceStart = jest.fn();
|
||||
const traceEnd = jest.fn();
|
||||
@@ -18,15 +18,8 @@ const mockPerf = jest.fn(() => {
|
||||
return { trace: createTrace };
|
||||
});
|
||||
|
||||
const mockAuth = jest.fn(() => {
|
||||
return {
|
||||
onIdTokenChanged: jest.fn()
|
||||
};
|
||||
});
|
||||
|
||||
const mockFirebase = {
|
||||
performance: mockPerf,
|
||||
auth: mockAuth
|
||||
performance: mockPerf
|
||||
};
|
||||
|
||||
const Provider = ({ children }) => (
|
||||
@@ -180,43 +173,3 @@ describe('SuspenseWithPerf', () => {
|
||||
expect(createTrace).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('AuthCheck', () => {
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('can find firebase Auth from Context', () => {
|
||||
expect(() =>
|
||||
render(
|
||||
<Provider>
|
||||
<React.Suspense fallback={'loading'}>
|
||||
<AuthCheck fallback={'loading'}>{'children'}</AuthCheck>
|
||||
</React.Suspense>
|
||||
</Provider>
|
||||
)
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('can use firebase Auth from props', () => {
|
||||
expect(() =>
|
||||
render(
|
||||
<React.Suspense fallback={'loading'}>
|
||||
<AuthCheck
|
||||
fallback={<h1>not signed in</h1>}
|
||||
auth={(mockFirebase.auth() as unknown) as auth.Auth}
|
||||
>
|
||||
{'signed in'}
|
||||
</AuthCheck>
|
||||
</React.Suspense>
|
||||
)
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
test.todo('renders the fallback if a user is not signed in');
|
||||
|
||||
test.todo('renders children if a user is logged in');
|
||||
|
||||
test.todo('checks requiredClaims');
|
||||
});
|
||||
61
reactfire/storage/index.tsx
Normal file
61
reactfire/storage/index.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import { storage } from 'firebase/app';
|
||||
import { getDownloadURL } from 'rxfire/storage';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ReactFireOptions, useObservable } from '..';
|
||||
|
||||
/**
|
||||
* modified version of rxFire's _fromTask
|
||||
*
|
||||
* @param task
|
||||
*/
|
||||
function _fromTask(task: storage.UploadTask) {
|
||||
return new Observable<storage.UploadTaskSnapshot>(subscriber => {
|
||||
const progress = (snap: storage.UploadTaskSnapshot) => {
|
||||
return subscriber.next(snap);
|
||||
};
|
||||
const error = e => subscriber.error(e);
|
||||
const complete = () => {
|
||||
return subscriber.complete();
|
||||
};
|
||||
task.on('state_changed', progress, error, complete);
|
||||
|
||||
// I REMOVED THE UNSUBSCRIBE RETURN BECAUSE IT CANCELS THE UPLOAD
|
||||
// https://github.com/firebase/firebase-js-sdk/issues/1659
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to the progress of a storage task
|
||||
*
|
||||
* @param task - the task you want to listen to
|
||||
* @param ref - reference to the blob the task is acting on
|
||||
* @param options
|
||||
*/
|
||||
export function useStorageTask<T = unknown>(
|
||||
task: storage.UploadTask,
|
||||
ref: storage.Reference,
|
||||
options?: ReactFireOptions<T>
|
||||
): storage.UploadTaskSnapshot | T {
|
||||
return useObservable(
|
||||
_fromTask(task),
|
||||
'upload' + ref.toString(),
|
||||
options ? options.startWithValue : undefined
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to a storage ref's download URL
|
||||
*
|
||||
* @param ref - reference to the blob you want to download
|
||||
* @param options
|
||||
*/
|
||||
export function useStorageDownloadURL<T = string>(
|
||||
ref: storage.Reference,
|
||||
options?: ReactFireOptions<T>
|
||||
): string | T {
|
||||
return useObservable(
|
||||
getDownloadURL(ref),
|
||||
'download' + ref.toString(),
|
||||
options ? options.startWithValue : undefined
|
||||
);
|
||||
}
|
||||
11
reactfire/storage/storage.test.tsx
Normal file
11
reactfire/storage/storage.test.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import 'jest-dom/extend-expect';
|
||||
|
||||
describe('Storage', () => {
|
||||
describe('useStorageTask', () => {
|
||||
test.todo('returns the same value as uploadTask');
|
||||
});
|
||||
|
||||
describe('useStorageDownloadURL', () => {
|
||||
test.todo('returns the same value as getDownloadURL');
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { Observable } from 'rxjs';
|
||||
import { startWith } from 'rxjs/operators';
|
||||
import { ObservablePromiseCache } from './request-cache';
|
||||
import { ObservablePromiseCache } from './requestCache';
|
||||
|
||||
const requestCache = new ObservablePromiseCache();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useObservable } from './use-observable';
|
||||
import { useObservable } from '.';
|
||||
import { renderHook, act } from '@testing-library/react-hooks';
|
||||
import { of, Subject, Observable, observable } from 'rxjs';
|
||||
import { delay } from 'rxjs/operators';
|
||||
Reference in New Issue
Block a user