Skip to content

Commit

Permalink
stronger typings for db endpoints, wip howto store methods
Browse files Browse the repository at this point in the history
  • Loading branch information
chrismclarke committed May 2, 2019
1 parent 94826bc commit 884f828
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 49 deletions.
30 changes: 18 additions & 12 deletions src/pages/Howto/Content/CreateHowto/CreateHowto.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { Form, Field } from 'react-final-form'
import { FieldArray } from 'react-final-form-arrays'
import arrayMutators from 'final-form-arrays'
import { IHowto, IHowtoFormInput } from 'src/models/howto.models'
import { afs } from 'src/utils/firebase'
import TEMPLATE from './TutorialTemplate'
import { stripSpecialCharacters } from 'src/utils/helpers'
import { UploadedFile } from 'src/pages/common/UploadedFile/UploadedFile'
Expand All @@ -21,16 +20,20 @@ import { TagsSelectField } from 'src/components/Form/TagsSelect.field'
import { ImageInputField } from 'src/components/Form/ImageInput.field'
import { FileInputField } from 'src/components/Form/FileInput.field'
import posed, { PoseGroup } from 'react-pose'
import { Storage, IUploadedFileMeta } from 'src/stores/storage'
import { IConvertedFileMeta } from 'src/components/ImageInput/ImageInput'
import { inject } from 'mobx-react'
import { Database } from 'src/stores/database'

export interface IState {
interface IState {
formValues: IHowtoFormInput
formSaved: boolean
_docID: string
_uploadPath: string
_toDocsList: boolean
}
interface IProps extends RouteComponentProps<any> {}
interface IInjectedProps extends IProps {
howtoStore: HowtoStore
}

const AnimationContainer = posed.div({
enter: { x: 0, opacity: 1, delay: 300 },
Expand All @@ -40,17 +43,13 @@ const AnimationContainer = posed.div({
// validation - return undefined if no error (i.e. valid)
const required = (value: any) => (value ? undefined : 'Required')

export class CreateHowto extends React.PureComponent<
RouteComponentProps<any>,
IState
> {
@inject('howtoStore')
export class CreateHowto extends React.Component<IProps, IState> {
uploadRefs: { [key: string]: UploadedFile | null } = {}
store = new HowtoStore()
constructor(props: any) {
super(props)
// generate unique id for db and storage references and assign to state
const databaseRef = afs.collection('documentation').doc()
const docID = databaseRef.id
const docID = this.store.generateID()
this.state = {
formValues: { ...TEMPLATE.TESTING_VALUES, id: docID } as IHowtoFormInput,
formSaved: false,
Expand All @@ -60,9 +59,16 @@ export class CreateHowto extends React.PureComponent<
}
}

get injected() {
return this.props as IInjectedProps
}
get store() {
return this.injected.howtoStore
}

public onSubmit = async (formValues: IHowtoFormInput) => {
console.log('submitting')
// this.injected.store
this.store.uploadHowTo(formValues, this.state._docID)
}

public validateTitle = async (value: any, meta?: FieldState) => {
Expand Down
16 changes: 0 additions & 16 deletions src/pages/Howto/Content/Howto/Howto.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,22 +60,6 @@ export class Howto extends React.Component<
: undefined
}

// public renderMultipleImages(step: IHowtoStep) {
// const preloadedImages: any[] = []
// for (const image of step.images) {
// const imageObj = new Image()
// imageObj.src = image.downloadUrl
// preloadedImages.push({
// src: imageObj.src,
// })
// }
// return preloadedImages.map((image: any, index: number) => (
// <div className="step__image">
// <img src={image.src} />
// </div>
// ))
// }

public render() {
const { howto, isLoading } = this.state
if (howto) {
Expand Down
35 changes: 23 additions & 12 deletions src/stores/Howto/howto.store.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export class HowtoStore {
@action
public async getDocList() {
const ref = await afs
.collection('documentation')
.collection('howtos')
.orderBy('_created', 'desc')
.get()

Expand All @@ -26,7 +26,7 @@ export class HowtoStore {
@action
public async getDocBySlug(slug: string) {
const ref = afs
.collection('documentation')
.collection('howtos')
.where('slug', '==', slug)
.limit(1)
const collection = await ref.get()
Expand All @@ -40,12 +40,16 @@ export class HowtoStore {

public isSlugUnique = async (slug: string) => {
try {
await Database.checkSlugUnique('documentation', slug)
await Database.checkSlugUnique('howtos', slug)
} catch (e) {
return 'How-to titles must be unique, please try being more specific'
}
}

public generateID = () => {
return Database.generateDocId('howtos')
}

public async uploadHowTo(values: IHowtoFormInput, id: string) {
const slug = stripSpecialCharacters(values.tutorial_title)
// present uploading modal
Expand All @@ -69,15 +73,22 @@ export class HowtoStore {
private async uploadStepImgs(steps: IHowtoStep[], id: string) {
const firstStep = steps[0]
const stepImages = firstStep.images as IConvertedFileMeta[]
const promises = stepImages.map(async img => {
const meta = await Storage.uploadFile(
`uploads/howTos/${id}`,
img.name,
img.photoData,
)
return meta
})
const imgMeta = await Promise.all(promises)
// NOTE - outer loop could be a map and done in parallel also
for (const step of steps) {
console.log('uploading step', step)
const promises = stepImages.map(async img => {
console.log('uploading image', img)
const meta = await Storage.uploadFile(
`uploads/howTos/${id}`,
img.name,
img.photoData,
)
return meta
})
console.log('running all promises')
const imgMeta = await Promise.all(promises)
console.log('imgMeta', imgMeta)
}

// const promises = steps.map(step =>
// step.images.map(async img => {
Expand Down
6 changes: 3 additions & 3 deletions src/stores/common/module.store.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BehaviorSubject, Subscription } from 'rxjs'
import { Database } from '../database'
import { Database, IDBEndpoints } from '../database'

/* The module store contains common methods used across modules that access specfic
collections on the database
Expand All @@ -13,14 +13,14 @@ export class ModuleStore {
private activeCollectionSubscription = new Subscription()

// when a module store is initiated automatically load the docs in the collection
constructor(public basePath: string) {
constructor(public basePath: IDBEndpoints) {
this.getCollection(basePath)
}

// when accessing a collection want to call the database getCollection method which
// efficiently checks the cache first and emits any subsequent updates
// we will stop subscribing
public getCollection(path: string) {
public getCollection(path: IDBEndpoints) {
this.allDocs$.next([])
this.activeCollectionSubscription.unsubscribe()
this.activeCollectionSubscription = Database.getCollection(path).subscribe(
Expand Down
21 changes: 15 additions & 6 deletions src/stores/database.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class Database {
/****************************************************************************** */

// get a group of docs. returns an observable, first pulling from local cache and then searching for updates
public static getCollection(path: string) {
public static getCollection(path: IDBEndpoints) {
const collection$ = new Subject<any[]>()
this._emitCollectionUpdates(path, collection$)
return collection$
Expand Down Expand Up @@ -57,7 +57,7 @@ export class Database {
}

public static async queryCollection(
collectionPath: string,
collectionPath: IDBEndpoints,
field: string,
operation: firestore.WhereFilterOp,
value: string,
Expand All @@ -74,14 +74,17 @@ export class Database {
/****************************************************************************** */

// instantiate a blank document to generate an id
public static generateId(collectionPath: string) {
public static generateDocId(collectionPath: IDBEndpoints) {
return afs.collection(collectionPath).doc().id
}
public static generateTimestamp(date?: Date) {
return firestore.Timestamp.fromDate(date ? date : new Date())
}

public static async checkSlugUnique(collectionPath: string, slug: string) {
public static async checkSlugUnique(
collectionPath: IDBEndpoints,
slug: string,
) {
const matches = await this.queryCollection(
collectionPath,
'slug',
Expand All @@ -95,12 +98,12 @@ export class Database {
}
}
// creates standard set of meta fields applied to all docs
public static generateDocMeta(collectionPath: string, docID?: string) {
public static generateDocMeta(collectionPath: IDBEndpoints, docID?: string) {
const user = auth().currentUser
const meta: IDbDoc = {
_created: this.generateTimestamp(),
_deleted: false,
_id: docID ? docID : this.generateId(collectionPath),
_id: docID ? docID : this.generateDocId(collectionPath),
_modified: this.generateTimestamp(),
_createdBy: user ? (user.displayName as string) : 'anonymous',
}
Expand Down Expand Up @@ -173,3 +176,9 @@ export class Database {
return Object.values(json)
}
}

/****************************************************************************** *
Interfaces
/****************************************************************************** */

export type IDBEndpoints = 'howtos' | 'users' | 'discussions' | 'tags'

0 comments on commit 884f828

Please sign in to comment.