From b0f87e0cf74992a577b74808a9c87993b9405917 Mon Sep 17 00:00:00 2001 From: Derek Burgman Date: Mon, 25 Apr 2022 02:10:28 -0500 Subject: [PATCH] checkpoint: demo --- .../src/app/function/auth/auth.function.ts | 1 + apps/demo-firebase/src/lib/collection.ts | 7 +- .../src/lib/guestbook/guestbook.query.ts | 5 + .../src/lib/guestbook/guestbook.ts | 121 ++++++++++++++++++ apps/demo-firebase/src/lib/guestbook/index.ts | 2 + apps/demo-firebase/src/lib/index.ts | 1 + .../src/lib/profile/profile.api.ts | 2 +- .../src/app/container/layout.component.html | 4 + .../src/app/container/layout.component.ts | 14 ++ .../demo/container/home.component.html | 5 +- .../demo/container/layout.component.ts | 16 ++- .../guestbook/container/layout.component.html | 1 + .../guestbook/container/layout.component.ts | 6 + .../guestbook/container/list.component.html | 7 + .../guestbook/container/list.component.ts | 10 ++ .../container/list.right.component.html | 5 + .../container/list.right.component.ts | 6 + .../app/modules/guestbook/guestbook.module.ts | 22 ++++ .../app/modules/guestbook/guestbook.router.ts | 27 ++++ .../profile/container/layout.component.html | 3 + .../profile/container/layout.component.ts | 6 + .../profile/container/profile.component.html | 1 + .../profile/container/profile.component.ts | 6 + .../app/modules/profile/profile.module.ts | 20 +++ .../app/modules/profile/profile.router.ts | 21 +++ .../modules/shared/demo.app.shared.module.ts | 13 ++ .../guestbook.entry.form.component.ts} | 0 .../guestbook.entry.list.component.ts | 43 +++++++ .../component/guestbook.list.component.ts | 43 +++++++ .../shared/guestbook/guestbook.module.ts | 23 ++++ .../shared/guestbook/guestbook.service.ts | 8 ++ .../component/profile.form.component.ts | 0 .../profile.username.form.component.ts | 0 .../modules/shared/profile/profile.module.ts | 12 ++ .../layout/container/two.component.html | 6 +- .../modules/doc/modules/layout/doc.layout.ts | 2 +- .../demo/src/firebase/root.firebase.module.ts | 8 +- .../src/lib/router/anchor/anchor.directive.ts | 9 ++ .../src/lib/auth/login/login.terms.ts | 1 - .../src/lib/firebase/emulators.ts | 4 + .../firestore/firebase.firestore.module.ts | 33 +++++ .../src/lib/firestore/firebase.firestore.ts | 6 + .../firestore/firestore.context.service.ts | 15 +++ .../dbx-firebase/src/lib/firestore/index.ts | 3 + packages/dbx-firebase/src/lib/index.ts | 2 + packages/dbx-firebase/src/lib/model/index.ts | 2 + .../dbx-firebase/src/lib/model/list/index.ts | 2 + .../lib/model/list/model.list.directive.ts | 74 +++++++++++ .../src/lib/model/list/model.list.module.ts | 6 + .../src/lib/model/list/model.list.ts | 20 +++ .../src/lib/model/model.module.ts | 0 .../src/lib/layout/column/_column.scss | 10 +- .../layout/column/one/one.column.component.ts | 4 +- .../column/two/two.column.component.spec.ts | 4 +- .../layout/column/two/two.column.component.ts | 4 +- .../column/two/two.column.head.component.ts | 4 +- .../two/two.column.right.component.html | 6 +- .../column/two/two.column.right.component.ts | 4 +- .../src/lib/client/firestore/firestore.ts | 2 +- .../lib/common/firestore/query/iterator.ts | 2 +- .../src/lib/iterator/iteration.accumulator.ts | 2 +- 61 files changed, 662 insertions(+), 34 deletions(-) create mode 100644 apps/demo-firebase/src/lib/guestbook/guestbook.query.ts create mode 100644 apps/demo-firebase/src/lib/guestbook/guestbook.ts create mode 100644 apps/demo-firebase/src/lib/guestbook/index.ts create mode 100644 apps/demo/src/app/modules/demo/modules/app/modules/guestbook/container/layout.component.html create mode 100644 apps/demo/src/app/modules/demo/modules/app/modules/guestbook/container/layout.component.ts create mode 100644 apps/demo/src/app/modules/demo/modules/app/modules/guestbook/container/list.component.html create mode 100644 apps/demo/src/app/modules/demo/modules/app/modules/guestbook/container/list.component.ts create mode 100644 apps/demo/src/app/modules/demo/modules/app/modules/guestbook/container/list.right.component.html create mode 100644 apps/demo/src/app/modules/demo/modules/app/modules/guestbook/container/list.right.component.ts create mode 100644 apps/demo/src/app/modules/demo/modules/app/modules/guestbook/guestbook.module.ts create mode 100644 apps/demo/src/app/modules/demo/modules/app/modules/guestbook/guestbook.router.ts create mode 100644 apps/demo/src/app/modules/demo/modules/app/modules/profile/container/layout.component.html create mode 100644 apps/demo/src/app/modules/demo/modules/app/modules/profile/container/layout.component.ts create mode 100644 apps/demo/src/app/modules/demo/modules/app/modules/profile/container/profile.component.html create mode 100644 apps/demo/src/app/modules/demo/modules/app/modules/profile/container/profile.component.ts create mode 100644 apps/demo/src/app/modules/demo/modules/app/modules/profile/profile.module.ts create mode 100644 apps/demo/src/app/modules/demo/modules/app/modules/profile/profile.router.ts create mode 100644 apps/demo/src/app/modules/demo/modules/shared/demo.app.shared.module.ts rename apps/{demo-firebase/src/lib/profile/username.ts => demo/src/app/modules/demo/modules/shared/guestbook/component/guestbook.entry.form.component.ts} (100%) create mode 100644 apps/demo/src/app/modules/demo/modules/shared/guestbook/component/guestbook.entry.list.component.ts create mode 100644 apps/demo/src/app/modules/demo/modules/shared/guestbook/component/guestbook.list.component.ts create mode 100644 apps/demo/src/app/modules/demo/modules/shared/guestbook/guestbook.module.ts create mode 100644 apps/demo/src/app/modules/demo/modules/shared/guestbook/guestbook.service.ts create mode 100644 apps/demo/src/app/modules/demo/modules/shared/profile/component/profile.form.component.ts create mode 100644 apps/demo/src/app/modules/demo/modules/shared/profile/component/profile.username.form.component.ts create mode 100644 apps/demo/src/app/modules/demo/modules/shared/profile/profile.module.ts create mode 100644 packages/dbx-firebase/src/lib/firestore/firebase.firestore.module.ts create mode 100644 packages/dbx-firebase/src/lib/firestore/firebase.firestore.ts create mode 100644 packages/dbx-firebase/src/lib/firestore/firestore.context.service.ts create mode 100644 packages/dbx-firebase/src/lib/firestore/index.ts create mode 100644 packages/dbx-firebase/src/lib/model/index.ts create mode 100644 packages/dbx-firebase/src/lib/model/list/index.ts create mode 100644 packages/dbx-firebase/src/lib/model/list/model.list.directive.ts create mode 100644 packages/dbx-firebase/src/lib/model/list/model.list.module.ts create mode 100644 packages/dbx-firebase/src/lib/model/list/model.list.ts create mode 100644 packages/dbx-firebase/src/lib/model/model.module.ts diff --git a/apps/demo-api/src/app/function/auth/auth.function.ts b/apps/demo-api/src/app/function/auth/auth.function.ts index f78279601..872036a25 100644 --- a/apps/demo-api/src/app/function/auth/auth.function.ts +++ b/apps/demo-api/src/app/function/auth/auth.function.ts @@ -9,6 +9,7 @@ export const initUserOnCreate = onEventWithDemoNestContext((withNest functions.auth.user().onCreate(withNest(async (nest, data: UserRecord, context) => { const uid = data.uid; + console.log('Init user: ', uid, context); if (uid) { await nest.profileActions.initProfileForUid(uid); diff --git a/apps/demo-firebase/src/lib/collection.ts b/apps/demo-firebase/src/lib/collection.ts index fbb13c718..c0955309a 100644 --- a/apps/demo-firebase/src/lib/collection.ts +++ b/apps/demo-firebase/src/lib/collection.ts @@ -1,13 +1,18 @@ import { FirestoreContext } from '@dereekb/firebase'; +import { guestbookEntryFirestoreCollectionFactory, GuestbookEntryFirestoreCollectionFactory, guestbookFirestoreCollection, GuestbookFirestoreCollection, GuestbookFirestoreCollections } from './guestbook'; import { profileFirestoreCollection, ProfileFirestoreCollection, ProfileFirestoreCollections, profilePrivateDataFirestoreCollectionFactory, ProfilePrivateDataFirestoreCollectionFactory } from './profile/profile'; -export abstract class DemoFirestoreCollections implements ProfileFirestoreCollections { +export abstract class DemoFirestoreCollections implements ProfileFirestoreCollections, GuestbookFirestoreCollections { + abstract readonly guestbookFirestoreCollection: GuestbookFirestoreCollection; + abstract readonly guestbookEntryCollectionFactory: GuestbookEntryFirestoreCollectionFactory; abstract readonly profileFirestoreCollection: ProfileFirestoreCollection; abstract readonly profilePrivateDataCollectionFactory: ProfilePrivateDataFirestoreCollectionFactory; } export function makeDemoFirestoreCollections(firestoreContext: FirestoreContext): DemoFirestoreCollections { return { + guestbookFirestoreCollection: guestbookFirestoreCollection(firestoreContext), + guestbookEntryCollectionFactory: guestbookEntryFirestoreCollectionFactory(firestoreContext), profileFirestoreCollection: profileFirestoreCollection(firestoreContext), profilePrivateDataCollectionFactory: profilePrivateDataFirestoreCollectionFactory(firestoreContext) }; diff --git a/apps/demo-firebase/src/lib/guestbook/guestbook.query.ts b/apps/demo-firebase/src/lib/guestbook/guestbook.query.ts new file mode 100644 index 000000000..c08f7a4e3 --- /dev/null +++ b/apps/demo-firebase/src/lib/guestbook/guestbook.query.ts @@ -0,0 +1,5 @@ +import { FirestoreQueryConstraint, where } from "@dereekb/firebase"; + +export function guestbookEntryWithUsername(username: string): FirestoreQueryConstraint { + return where('username', '==', username); +} diff --git a/apps/demo-firebase/src/lib/guestbook/guestbook.ts b/apps/demo-firebase/src/lib/guestbook/guestbook.ts new file mode 100644 index 000000000..efa5415a9 --- /dev/null +++ b/apps/demo-firebase/src/lib/guestbook/guestbook.ts @@ -0,0 +1,121 @@ +import { CollectionReference, AbstractFirestoreDocument, makeSnapshotConverterFunctions, firestoreString, firestoreDate, FirestoreCollection, UserRelatedById, DocumentReferenceRef, FirestoreContext, FirestoreCollectionWithParent, firestoreBoolean } from "@dereekb/firebase"; + +export interface GuestbookFirestoreCollections { + guestbookFirestoreCollection: GuestbookFirestoreCollection; + guestbookEntryCollectionFactory: GuestbookEntryFirestoreCollectionFactory; +} + +// MARK: Guestbook +export interface Guestbook { + /** + * Whether or not this guestbook should show up in the list. + * + * If not active, this item is still considered locked. + */ + active: boolean; + /** + * Guestbook name + */ + name: string; + /** + * Whether or not this guestbook and it's entries can still be edited. + */ + locked: boolean; + /** + * Last date the guestbook was updated at. + */ + lockedAt: Date; +} + +export interface GuestbookRef extends DocumentReferenceRef { } + +export class GuestbookDocument extends AbstractFirestoreDocument { } + +export const guestbookCollectionPath = 'guestbook'; + +export const guestbookConverter = makeSnapshotConverterFunctions({ + fields: { + active: firestoreBoolean(), + name: firestoreString(), + locked: firestoreBoolean(), + lockedAt: firestoreDate() + } +}); + +export function guestbookCollectionReference(context: FirestoreContext): CollectionReference { + return context.collection(guestbookCollectionPath).withConverter(guestbookConverter); +} + +export type GuestbookFirestoreCollection = FirestoreCollection; + +export function guestbookFirestoreCollection(firestoreContext: FirestoreContext): GuestbookFirestoreCollection { + return firestoreContext.firestoreCollection({ + itemsPerPage: 50, + collection: guestbookCollectionReference(firestoreContext), + makeDocument: (accessor, documentAccessor) => new GuestbookDocument(accessor, documentAccessor), + firestoreContext + }); +} + +// MARK: Guestbook Entry +export interface GuestbookEntry extends UserRelatedById { + /** + * Arbitrary word without spaces + */ + word: string; + /** + * Guestbook message. + */ + message: string; + /** + * Arbitrary string for signature + */ + signed: string; + /** + * Date the entry was last updated at. + */ + updatedAt: Date; + /** + * Date the entry was originally created at. + */ + createdAt: Date; +} + +export interface GuestbookEntryRef extends DocumentReferenceRef { } + +export class GuestbookEntryDocument extends AbstractFirestoreDocument { } + +export const guestbookEntryCollectionPath = 'guestbookEntry'; + +export const guestbookEntryConverter = makeSnapshotConverterFunctions({ + fields: { + word: firestoreString(), + message: firestoreString(), + signed: firestoreString(), + updatedAt: firestoreDate(), + createdAt: firestoreDate({ saveDefaultAsNow: true }) + } +}); + +export function guestbookEntryCollectionReferenceFactory(context: FirestoreContext): (guestbook: GuestbookDocument) => CollectionReference { + return (guestbook: GuestbookDocument) => { + return context.subcollection(guestbook.documentRef, guestbookEntryCollectionPath).withConverter(guestbookEntryConverter); + }; +} + +export type GuestbookEntryFirestoreCollection = FirestoreCollectionWithParent; +export type GuestbookEntryFirestoreCollectionFactory = (parent: GuestbookDocument) => GuestbookEntryFirestoreCollection; + +export function guestbookEntryFirestoreCollectionFactory(firestoreContext: FirestoreContext): GuestbookEntryFirestoreCollectionFactory { + const factory = guestbookEntryCollectionReferenceFactory(firestoreContext); + + return (parent: GuestbookDocument) => { + return firestoreContext.firestoreCollectionWithParent({ + itemsPerPage: 50, + collection: factory(parent), + makeDocument: (accessor, documentAccessor) => new GuestbookEntryDocument(accessor, documentAccessor), + firestoreContext, + parent + }); + } +} diff --git a/apps/demo-firebase/src/lib/guestbook/index.ts b/apps/demo-firebase/src/lib/guestbook/index.ts new file mode 100644 index 000000000..571b3b8d1 --- /dev/null +++ b/apps/demo-firebase/src/lib/guestbook/index.ts @@ -0,0 +1,2 @@ +export * from './guestbook'; +export * from './guestbook.query'; diff --git a/apps/demo-firebase/src/lib/index.ts b/apps/demo-firebase/src/lib/index.ts index 8b6009e19..8a1db0c12 100644 --- a/apps/demo-firebase/src/lib/index.ts +++ b/apps/demo-firebase/src/lib/index.ts @@ -1,2 +1,3 @@ +export * from './guestbook'; export * from './profile'; export * from './collection'; diff --git a/apps/demo-firebase/src/lib/profile/profile.api.ts b/apps/demo-firebase/src/lib/profile/profile.api.ts index 8924e9054..13e89a8d0 100644 --- a/apps/demo-firebase/src/lib/profile/profile.api.ts +++ b/apps/demo-firebase/src/lib/profile/profile.api.ts @@ -1,6 +1,6 @@ import { Profile } from './profile'; import { Expose } from "class-transformer"; -import { FirebaseFunctionMap, FirebaseFunctionMapFactory, firebaseFunctionMapFactory, FirebaseFunctionMapFunction, FirebaseFunctionTypeConfigMap } from "@dereekb/firebase"; +import { FirebaseFunctionMap, firebaseFunctionMapFactory, FirebaseFunctionTypeConfigMap } from "@dereekb/firebase"; import { IsNotEmpty, IsOptional, IsString, MaxLength } from "class-validator"; export class SetProfileUsernameParams { diff --git a/apps/demo/src/app/container/layout.component.html b/apps/demo/src/app/container/layout.component.html index b0b774533..ad571dc78 100644 --- a/apps/demo/src/app/container/layout.component.html +++ b/apps/demo/src/app/container/layout.component.html @@ -12,6 +12,10 @@ + + + + diff --git a/apps/demo/src/app/container/layout.component.ts b/apps/demo/src/app/container/layout.component.ts index 938165b50..294302e11 100644 --- a/apps/demo/src/app/container/layout.component.ts +++ b/apps/demo/src/app/container/layout.component.ts @@ -1,6 +1,7 @@ import { DbxStyleService } from '@dereekb/dbx-web'; import { ClickableAnchor } from '@dereekb/dbx-core'; import { Component } from '@angular/core'; +import { environment } from '../../environments/environment'; @Component({ templateUrl: './layout.component.html', @@ -31,4 +32,17 @@ export class AppLayoutComponent { constructor(readonly dbxStyleService: DbxStyleService) { } + get showEmulatorButton() { + return (environment.firebase.emulators.useEmulators === true); + } + + get emulator(): ClickableAnchor { + const ui = environment.firebase.emulators?.ui; + + return (ui) ? { + url: `http://${ui.host ?? 'localhost'}:${ui.port}`, + target: '_blank' + } : {}; + } + } diff --git a/apps/demo/src/app/modules/demo/container/home.component.html b/apps/demo/src/app/modules/demo/container/home.component.html index 8c87ad9d9..7ab7291dc 100644 --- a/apps/demo/src/app/modules/demo/container/home.component.html +++ b/apps/demo/src/app/modules/demo/container/home.component.html @@ -2,8 +2,11 @@

Demo App

-

This is the public view of the demo app.

+

This is the public view of the demo app. Anyone can see this page whether or not they are logged in.

You can login or go to the app here.

+ + +
diff --git a/apps/demo/src/app/modules/demo/container/layout.component.ts b/apps/demo/src/app/modules/demo/container/layout.component.ts index c6aafff8a..26df8f2cc 100644 --- a/apps/demo/src/app/modules/demo/container/layout.component.ts +++ b/apps/demo/src/app/modules/demo/container/layout.component.ts @@ -12,21 +12,29 @@ import { DbxFirebaseAuthService } from '@dereekb/dbx-firebase'; export class DemoLayoutComponent { readonly everyoneAnchors = [{ - title: 'Home', + title: 'Public Home', ref: 'demo.home', icon: 'home' }]; readonly adminAnchors = [{ - title: 'Home', + title: 'Admin Home', ref: 'demo.home', icon: 'home' }]; readonly userAnchors = [{ - title: 'Home', - ref: 'demo.home', + title: 'App Home', + ref: 'demo.app.home', icon: 'home' + }, { + title: 'Guest Book', + ref: 'demo.app.guestbook.list', + icon: 'list' + }, { + title: 'Your Profile', + ref: 'demo.app.profile', + icon: 'person' }]; readonly navAnchors$: Observable = of({ diff --git a/apps/demo/src/app/modules/demo/modules/app/modules/guestbook/container/layout.component.html b/apps/demo/src/app/modules/demo/modules/app/modules/guestbook/container/layout.component.html new file mode 100644 index 000000000..5105d5113 --- /dev/null +++ b/apps/demo/src/app/modules/demo/modules/app/modules/guestbook/container/layout.component.html @@ -0,0 +1 @@ + diff --git a/apps/demo/src/app/modules/demo/modules/app/modules/guestbook/container/layout.component.ts b/apps/demo/src/app/modules/demo/modules/app/modules/guestbook/container/layout.component.ts new file mode 100644 index 000000000..4961e76be --- /dev/null +++ b/apps/demo/src/app/modules/demo/modules/app/modules/guestbook/container/layout.component.ts @@ -0,0 +1,6 @@ +import { Component } from '@angular/core'; + +@Component({ + templateUrl: './layout.component.html' +}) +export class DemoGuestbookLayoutComponent {} diff --git a/apps/demo/src/app/modules/demo/modules/app/modules/guestbook/container/list.component.html b/apps/demo/src/app/modules/demo/modules/app/modules/guestbook/container/list.component.html new file mode 100644 index 000000000..3ef13f6f1 --- /dev/null +++ b/apps/demo/src/app/modules/demo/modules/app/modules/guestbook/container/list.component.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/demo/src/app/modules/demo/modules/app/modules/guestbook/container/list.component.ts b/apps/demo/src/app/modules/demo/modules/app/modules/guestbook/container/list.component.ts new file mode 100644 index 000000000..c36bf9dcc --- /dev/null +++ b/apps/demo/src/app/modules/demo/modules/app/modules/guestbook/container/list.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; + +@Component({ + templateUrl: './list.component.html' +}) +export class DemoGuestbookListPageComponent { + + constructor() { } + +} diff --git a/apps/demo/src/app/modules/demo/modules/app/modules/guestbook/container/list.right.component.html b/apps/demo/src/app/modules/demo/modules/app/modules/guestbook/container/list.right.component.html new file mode 100644 index 000000000..e79ddca79 --- /dev/null +++ b/apps/demo/src/app/modules/demo/modules/app/modules/guestbook/container/list.right.component.html @@ -0,0 +1,5 @@ + + +

View Guest Book

+
+
diff --git a/apps/demo/src/app/modules/demo/modules/app/modules/guestbook/container/list.right.component.ts b/apps/demo/src/app/modules/demo/modules/app/modules/guestbook/container/list.right.component.ts new file mode 100644 index 000000000..0044510a0 --- /dev/null +++ b/apps/demo/src/app/modules/demo/modules/app/modules/guestbook/container/list.right.component.ts @@ -0,0 +1,6 @@ +import { Component } from '@angular/core'; + +@Component({ + templateUrl: './list.right.component.html' +}) +export class DemoGuestbookListPageRightComponent {} diff --git a/apps/demo/src/app/modules/demo/modules/app/modules/guestbook/guestbook.module.ts b/apps/demo/src/app/modules/demo/modules/app/modules/guestbook/guestbook.module.ts new file mode 100644 index 000000000..631a810a2 --- /dev/null +++ b/apps/demo/src/app/modules/demo/modules/app/modules/guestbook/guestbook.module.ts @@ -0,0 +1,22 @@ +import { NgModule } from '@angular/core'; +import { UIRouterModule } from '@uirouter/angular'; +import { STATES } from './guestbook.router'; +import { DemoAppSharedModule } from '../../../shared/demo.app.shared.module'; +import { DemoGuestbookListPageRightComponent } from './container/list.right.component'; +import { DemoGuestbookListPageComponent } from './container/list.component'; +import { DemoGuestbookLayoutComponent } from './container/layout.component'; + +@NgModule({ + imports: [ + DemoAppSharedModule, + UIRouterModule.forChild({ + states: STATES + }) + ], + declarations: [ + DemoGuestbookLayoutComponent, + DemoGuestbookListPageComponent, + DemoGuestbookListPageRightComponent + ], +}) +export class DemoGuestbookModule { } diff --git a/apps/demo/src/app/modules/demo/modules/app/modules/guestbook/guestbook.router.ts b/apps/demo/src/app/modules/demo/modules/app/modules/guestbook/guestbook.router.ts new file mode 100644 index 000000000..92aa272ab --- /dev/null +++ b/apps/demo/src/app/modules/demo/modules/app/modules/guestbook/guestbook.router.ts @@ -0,0 +1,27 @@ +import { Ng2StateDeclaration } from '@uirouter/angular'; +import { DemoGuestbookLayoutComponent } from './container/layout.component'; +import { DemoGuestbookListPageComponent } from './container/list.component'; +import { DemoGuestbookListPageRightComponent } from './container/list.right.component'; + +export const layoutState: Ng2StateDeclaration = { + url: '/guestbook', + name: 'demo.app.guestbook', + redirectTo: 'demo.app.guestbook.list', + component: DemoGuestbookLayoutComponent +}; + +export const guestbookListState: Ng2StateDeclaration = { + name: 'demo.app.guestbook.list', + component: DemoGuestbookListPageComponent +}; + +export const guestbookListRightState: Ng2StateDeclaration = { + url: '/:id', + name: 'demo.app.guestbook.list.guestbook', + component: DemoGuestbookListPageRightComponent +}; + +export const STATES: Ng2StateDeclaration[] = [ + layoutState, + guestbookListState +]; diff --git a/apps/demo/src/app/modules/demo/modules/app/modules/profile/container/layout.component.html b/apps/demo/src/app/modules/demo/modules/app/modules/profile/container/layout.component.html new file mode 100644 index 000000000..16752665e --- /dev/null +++ b/apps/demo/src/app/modules/demo/modules/app/modules/profile/container/layout.component.html @@ -0,0 +1,3 @@ + + + diff --git a/apps/demo/src/app/modules/demo/modules/app/modules/profile/container/layout.component.ts b/apps/demo/src/app/modules/demo/modules/app/modules/profile/container/layout.component.ts new file mode 100644 index 000000000..45fceeddc --- /dev/null +++ b/apps/demo/src/app/modules/demo/modules/app/modules/profile/container/layout.component.ts @@ -0,0 +1,6 @@ +import { Component } from '@angular/core'; + +@Component({ + templateUrl: './layout.component.html' +}) +export class DemoProfileLayoutComponent {} diff --git a/apps/demo/src/app/modules/demo/modules/app/modules/profile/container/profile.component.html b/apps/demo/src/app/modules/demo/modules/app/modules/profile/container/profile.component.html new file mode 100644 index 000000000..183aa7dd0 --- /dev/null +++ b/apps/demo/src/app/modules/demo/modules/app/modules/profile/container/profile.component.html @@ -0,0 +1 @@ +

Profile

diff --git a/apps/demo/src/app/modules/demo/modules/app/modules/profile/container/profile.component.ts b/apps/demo/src/app/modules/demo/modules/app/modules/profile/container/profile.component.ts new file mode 100644 index 000000000..f7ef888da --- /dev/null +++ b/apps/demo/src/app/modules/demo/modules/app/modules/profile/container/profile.component.ts @@ -0,0 +1,6 @@ +import { Component } from '@angular/core'; + +@Component({ + templateUrl: './profile.component.html' +}) +export class DemoProfileViewComponent {} diff --git a/apps/demo/src/app/modules/demo/modules/app/modules/profile/profile.module.ts b/apps/demo/src/app/modules/demo/modules/app/modules/profile/profile.module.ts new file mode 100644 index 000000000..c6c862b70 --- /dev/null +++ b/apps/demo/src/app/modules/demo/modules/app/modules/profile/profile.module.ts @@ -0,0 +1,20 @@ +import { DemoProfileViewComponent } from './container/profile.component'; +import { DemoProfileLayoutComponent } from './container/layout.component'; +import { NgModule } from '@angular/core'; +import { UIRouterModule } from '@uirouter/angular'; +import { STATES } from './profile.router'; +import { AppSharedModule } from '@/shared/app.shared.module'; + +@NgModule({ + imports: [ + AppSharedModule, + UIRouterModule.forChild({ + states: STATES + }) + ], + declarations: [ + DemoProfileLayoutComponent, + DemoProfileViewComponent + ], +}) +export class DemoProfileModule { } diff --git a/apps/demo/src/app/modules/demo/modules/app/modules/profile/profile.router.ts b/apps/demo/src/app/modules/demo/modules/app/modules/profile/profile.router.ts new file mode 100644 index 000000000..39ff423ca --- /dev/null +++ b/apps/demo/src/app/modules/demo/modules/app/modules/profile/profile.router.ts @@ -0,0 +1,21 @@ +import { Ng2StateDeclaration } from '@uirouter/angular'; +import { DemoProfileLayoutComponent } from './container/layout.component'; +import { DemoProfileViewComponent } from './container/profile.component'; + +export const layoutState: Ng2StateDeclaration = { + url: '/profile', + name: 'demo.app.profile', + redirectTo: 'demo.app.profile.view', + component: DemoProfileLayoutComponent +}; + +export const profileViewState: Ng2StateDeclaration = { + name: 'demo.app.profile.view', + url: '/view', + component: DemoProfileViewComponent +}; + +export const STATES: Ng2StateDeclaration[] = [ + layoutState, + profileViewState +]; diff --git a/apps/demo/src/app/modules/demo/modules/shared/demo.app.shared.module.ts b/apps/demo/src/app/modules/demo/modules/shared/demo.app.shared.module.ts new file mode 100644 index 000000000..9674ca3e0 --- /dev/null +++ b/apps/demo/src/app/modules/demo/modules/shared/demo.app.shared.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; +import { AppSharedModule } from '@/shared/app.shared.module'; +import { DemoSharedProfileModule } from './profile/profile.module'; +import { DemoSharedGuestbookModule } from './guestbook/guestbook.module'; + +@NgModule({ + exports: [ + AppSharedModule, + DemoSharedGuestbookModule, + DemoSharedProfileModule + ] +}) +export class DemoAppSharedModule { } diff --git a/apps/demo-firebase/src/lib/profile/username.ts b/apps/demo/src/app/modules/demo/modules/shared/guestbook/component/guestbook.entry.form.component.ts similarity index 100% rename from apps/demo-firebase/src/lib/profile/username.ts rename to apps/demo/src/app/modules/demo/modules/shared/guestbook/component/guestbook.entry.form.component.ts diff --git a/apps/demo/src/app/modules/demo/modules/shared/guestbook/component/guestbook.entry.list.component.ts b/apps/demo/src/app/modules/demo/modules/shared/guestbook/component/guestbook.entry.list.component.ts new file mode 100644 index 000000000..52cd129ce --- /dev/null +++ b/apps/demo/src/app/modules/demo/modules/shared/guestbook/component/guestbook.entry.list.component.ts @@ -0,0 +1,43 @@ +import { GuestbookEntry } from '@dereekb/demo-firebase'; +import { Component } from "@angular/core"; +import { AbstractDbxSelectionListWrapperDirective, AbstractDbxValueListViewItemComponent, AbstractDbxSelectionListViewDirective, DEFAULT_LIST_WRAPPER_DIRECTIVE_TEMPLATE, DbxSelectionValueListViewConfig, ProvideDbxListView, DEFAULT_DBX_SELECTION_VALUE_LIST_DIRECTIVE_TEMPLATE, DbxValueAsListItem } from "@dereekb/dbx-web"; +import { of } from "rxjs"; + +export type GuestbookEntryWithSelection = DbxValueAsListItem; + +@Component({ + selector: 'demo-guestbook-entry-list', + template: DEFAULT_LIST_WRAPPER_DIRECTIVE_TEMPLATE +}) +export class DemoGuestbookEntryListComponent extends AbstractDbxSelectionListWrapperDirective { + + constructor() { + super({ + componentClass: DemoGuestbookEntryListViewComponent, + defaultSelectionMode: 'view' + }); + } + +} + +@Component({ + template: DEFAULT_DBX_SELECTION_VALUE_LIST_DIRECTIVE_TEMPLATE, + providers: ProvideDbxListView(DemoGuestbookEntryListViewComponent) +}) +export class DemoGuestbookEntryListViewComponent extends AbstractDbxSelectionListViewDirective { + + readonly config: DbxSelectionValueListViewConfig = { + componentClass: DemoGuestbookEntryListViewItemComponent, + mapValuesToItemValues: (x) => of(x.map((y) => ({ ...y, icon: y.icon, itemValue: y }))) + }; + +} + +@Component({ + template: ` +
+

GuestbookEntry

+
+ ` +}) +export class DemoGuestbookEntryListViewItemComponent extends AbstractDbxValueListViewItemComponent { } diff --git a/apps/demo/src/app/modules/demo/modules/shared/guestbook/component/guestbook.list.component.ts b/apps/demo/src/app/modules/demo/modules/shared/guestbook/component/guestbook.list.component.ts new file mode 100644 index 000000000..59bef8c2f --- /dev/null +++ b/apps/demo/src/app/modules/demo/modules/shared/guestbook/component/guestbook.list.component.ts @@ -0,0 +1,43 @@ +import { Guestbook } from '@dereekb/demo-firebase'; +import { Component } from "@angular/core"; +import { AbstractDbxSelectionListWrapperDirective, AbstractDbxValueListViewItemComponent, AbstractDbxSelectionListViewDirective, DEFAULT_LIST_WRAPPER_DIRECTIVE_TEMPLATE, DbxSelectionValueListViewConfig, ProvideDbxListView, DEFAULT_DBX_SELECTION_VALUE_LIST_DIRECTIVE_TEMPLATE, DbxValueAsListItem } from "@dereekb/dbx-web"; +import { of } from "rxjs"; + +export type GuestbookWithSelection = DbxValueAsListItem; + +@Component({ + selector: 'demo-guestbook-list', + template: DEFAULT_LIST_WRAPPER_DIRECTIVE_TEMPLATE +}) +export class DemoGuestbookListComponent extends AbstractDbxSelectionListWrapperDirective { + + constructor() { + super({ + componentClass: DemoGuestbookListViewComponent, + defaultSelectionMode: 'view' + }); + } + +} + +@Component({ + template: DEFAULT_DBX_SELECTION_VALUE_LIST_DIRECTIVE_TEMPLATE, + providers: ProvideDbxListView(DemoGuestbookListViewComponent) +}) +export class DemoGuestbookListViewComponent extends AbstractDbxSelectionListViewDirective { + + readonly config: DbxSelectionValueListViewConfig = { + componentClass: DemoGuestbookListViewItemComponent, + mapValuesToItemValues: (x) => of(x.map((y) => ({ ...y, icon: y.icon, itemValue: y }))) + }; + +} + +@Component({ + template: ` +
+

Guestbook

+
+ ` +}) +export class DemoGuestbookListViewItemComponent extends AbstractDbxValueListViewItemComponent { } diff --git a/apps/demo/src/app/modules/demo/modules/shared/guestbook/guestbook.module.ts b/apps/demo/src/app/modules/demo/modules/shared/guestbook/guestbook.module.ts new file mode 100644 index 000000000..2683c510a --- /dev/null +++ b/apps/demo/src/app/modules/demo/modules/shared/guestbook/guestbook.module.ts @@ -0,0 +1,23 @@ +import { NgModule } from '@angular/core'; +import { AppSharedModule } from '@/shared/app.shared.module'; +import { DemoGuestbookListComponent, DemoGuestbookListViewComponent, DemoGuestbookListViewItemComponent } from './component/guestbook.list.component'; +import { DemoGuestbookEntryListComponent, DemoGuestbookEntryListViewComponent, DemoGuestbookEntryListViewItemComponent } from './component/guestbook.entry.list.component'; + +@NgModule({ + imports: [ + AppSharedModule + ], + declarations: [ + DemoGuestbookListComponent, + DemoGuestbookListViewComponent, + DemoGuestbookListViewItemComponent, + DemoGuestbookEntryListComponent, + DemoGuestbookEntryListViewComponent, + DemoGuestbookEntryListViewItemComponent + ], + exports: [ + DemoGuestbookListComponent, + DemoGuestbookEntryListComponent + ] +}) +export class DemoSharedGuestbookModule { } diff --git a/apps/demo/src/app/modules/demo/modules/shared/guestbook/guestbook.service.ts b/apps/demo/src/app/modules/demo/modules/shared/guestbook/guestbook.service.ts new file mode 100644 index 000000000..b7b40dfb6 --- /dev/null +++ b/apps/demo/src/app/modules/demo/modules/shared/guestbook/guestbook.service.ts @@ -0,0 +1,8 @@ +import { Injectable } from "@nestjs/common"; + +@Injectable() +export class DemoGuestbookService { + + constructor() { } + +} diff --git a/apps/demo/src/app/modules/demo/modules/shared/profile/component/profile.form.component.ts b/apps/demo/src/app/modules/demo/modules/shared/profile/component/profile.form.component.ts new file mode 100644 index 000000000..e69de29bb diff --git a/apps/demo/src/app/modules/demo/modules/shared/profile/component/profile.username.form.component.ts b/apps/demo/src/app/modules/demo/modules/shared/profile/component/profile.username.form.component.ts new file mode 100644 index 000000000..e69de29bb diff --git a/apps/demo/src/app/modules/demo/modules/shared/profile/profile.module.ts b/apps/demo/src/app/modules/demo/modules/shared/profile/profile.module.ts new file mode 100644 index 000000000..bff25462c --- /dev/null +++ b/apps/demo/src/app/modules/demo/modules/shared/profile/profile.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core'; +import { AppSharedModule } from '@/shared/app.shared.module'; + +@NgModule({ + imports: [ + AppSharedModule + ], + declarations: [ + + ], +}) +export class DemoSharedProfileModule { } diff --git a/apps/demo/src/app/modules/doc/modules/layout/container/two.component.html b/apps/demo/src/app/modules/doc/modules/layout/container/two.component.html index 15d9e994a..2a409f6e0 100644 --- a/apps/demo/src/app/modules/doc/modules/layout/container/two.component.html +++ b/apps/demo/src/app/modules/doc/modules/layout/container/two.component.html @@ -2,18 +2,18 @@ -

Basic Example

- +
Left
Right
-
+
diff --git a/apps/demo/src/app/modules/doc/modules/layout/doc.layout.ts b/apps/demo/src/app/modules/doc/modules/layout/doc.layout.ts index eb55b6439..5c4c3c2ea 100644 --- a/apps/demo/src/app/modules/doc/modules/layout/doc.layout.ts +++ b/apps/demo/src/app/modules/doc/modules/layout/doc.layout.ts @@ -32,7 +32,7 @@ export const DOC_LAYOUT_ROUTES = [{ }, { icon: 'view_column', title: 'Two Columns', - detail: 'dbx-two-columns', + detail: 'dbx-two-column', ref: 'doc.layout.two' }]; diff --git a/apps/demo/src/firebase/root.firebase.module.ts b/apps/demo/src/firebase/root.firebase.module.ts index b9a7532ef..a5bf00c13 100644 --- a/apps/demo/src/firebase/root.firebase.module.ts +++ b/apps/demo/src/firebase/root.firebase.module.ts @@ -1,12 +1,18 @@ -import { DbxFirebaseEmulatorModule, DbxFirebaseDefaultProvidersModule, DbxFirebaseAuthModule } from '@dereekb/dbx-firebase'; +import { FirestoreContext } from '@dereekb/firebase'; +import { DbxFirebaseFirestoreCollectionModule, DbxFirebaseEmulatorModule, DbxFirebaseDefaultProvidersModule, DbxFirebaseAuthModule } from '@dereekb/dbx-firebase'; import { NgModule } from '@angular/core'; import { environment } from '../environments/environment'; +import { DemoFirestoreCollections, makeDemoFirestoreCollections } from '@dereekb/demo-firebase'; @NgModule({ imports: [ // dbx-firebase DbxFirebaseEmulatorModule.forRoot(environment.firebase.emulators), DbxFirebaseDefaultProvidersModule.forRoot(environment.firebase), + DbxFirebaseFirestoreCollectionModule.forRoot({ + appCollectionClass: DemoFirestoreCollections, + collectionFactory: (firestoreContext: FirestoreContext) => makeDemoFirestoreCollections(firestoreContext) + }), DbxFirebaseAuthModule.forRoot({ delegateFactory: undefined // todo }) diff --git a/packages/dbx-core/src/lib/router/anchor/anchor.directive.ts b/packages/dbx-core/src/lib/router/anchor/anchor.directive.ts index d099bc703..57869ee1d 100644 --- a/packages/dbx-core/src/lib/router/anchor/anchor.directive.ts +++ b/packages/dbx-core/src/lib/router/anchor/anchor.directive.ts @@ -4,6 +4,7 @@ import { BehaviorSubject, combineLatest, Observable, delay } from 'rxjs'; import { Directive, Input, OnDestroy } from '@angular/core'; import { Maybe } from '@dereekb/util'; import { AnchorType, ClickableAnchor, anchorTypeForAnchor, DbxAnchor } from './anchor'; +import { SegueRefOrSegueRefRouterLink, asSegueRef } from '../segue'; /** * Abstract anchor directive. @@ -31,6 +32,14 @@ export class AbstractDbxAnchorDirective) { + this.anchor = asSegueRef(ref) as T; + } + @Input() public get anchor(): Maybe { return this._anchor.value; diff --git a/packages/dbx-firebase/src/lib/auth/login/login.terms.ts b/packages/dbx-firebase/src/lib/auth/login/login.terms.ts index 87f5694f1..34a847b25 100644 --- a/packages/dbx-firebase/src/lib/auth/login/login.terms.ts +++ b/packages/dbx-firebase/src/lib/auth/login/login.terms.ts @@ -1,5 +1,4 @@ - export abstract class DbxFirebaseLoginTermsConfig { abstract readonly tosUrl: string; abstract readonly privacyUrl: string; diff --git a/packages/dbx-firebase/src/lib/firebase/emulators.ts b/packages/dbx-firebase/src/lib/firebase/emulators.ts index 37c937218..c517602a1 100644 --- a/packages/dbx-firebase/src/lib/firebase/emulators.ts +++ b/packages/dbx-firebase/src/lib/firebase/emulators.ts @@ -19,6 +19,10 @@ export interface DbxFirebaseEmulatorsConfig { * Default host to target. Defaults to localhost if not provided. */ host?: string; + /** + * emulator UI configuration + */ + ui?: DbxFirebaseEmulatorConfig; /** * Auth emulator configuration */ diff --git a/packages/dbx-firebase/src/lib/firestore/firebase.firestore.module.ts b/packages/dbx-firebase/src/lib/firestore/firebase.firestore.module.ts new file mode 100644 index 000000000..5b424337c --- /dev/null +++ b/packages/dbx-firebase/src/lib/firestore/firebase.firestore.module.ts @@ -0,0 +1,33 @@ +import { ModuleWithProviders, NgModule } from "@angular/core"; +import { firebaseFirestoreContextFactory, FirestoreContext } from "@dereekb/firebase"; +import { DBX_FIRESTORE_CONTEXT_TOKEN } from "./firebase.firestore"; +import { Firestore } from "@angular/fire/firestore"; +import { ClassLikeType } from "@dereekb/util"; + +export interface DbxFirebaseFirestoreCollectionModuleConfig { + appCollectionClass: ClassLikeType; + collectionFactory: (context: FirestoreContext) => T; +} + +/** + * Used to initialize the FirestoreCollection for a DbxFirebase app. + */ +@NgModule() +export class DbxFirebaseFirestoreCollectionModule { + + static forRoot(config: DbxFirebaseFirestoreCollectionModuleConfig): ModuleWithProviders { + return { + ngModule: DbxFirebaseFirestoreCollectionModule, + providers: [{ + provide: DBX_FIRESTORE_CONTEXT_TOKEN, + useFactory: firebaseFirestoreContextFactory, + deps: [Firestore] + }, { + provide: config.appCollectionClass, + useFactory: config.collectionFactory, + deps: [DBX_FIRESTORE_CONTEXT_TOKEN] + }] + }; + } + +} diff --git a/packages/dbx-firebase/src/lib/firestore/firebase.firestore.ts b/packages/dbx-firebase/src/lib/firestore/firebase.firestore.ts new file mode 100644 index 000000000..e5f2718a0 --- /dev/null +++ b/packages/dbx-firebase/src/lib/firestore/firebase.firestore.ts @@ -0,0 +1,6 @@ +import { InjectionToken } from '@angular/core'; + +/** + * Token to access the FirestoreContext value. + */ +export const DBX_FIRESTORE_CONTEXT_TOKEN = new InjectionToken('DBX_FIRESTORE_CONTEXT_TOKEN'); diff --git a/packages/dbx-firebase/src/lib/firestore/firestore.context.service.ts b/packages/dbx-firebase/src/lib/firestore/firestore.context.service.ts new file mode 100644 index 000000000..404118e6b --- /dev/null +++ b/packages/dbx-firebase/src/lib/firestore/firestore.context.service.ts @@ -0,0 +1,15 @@ +import { Inject, Injectable } from '@angular/core'; +import { FirestoreContext } from '@dereekb/firebase'; +import { DBX_FIRESTORE_CONTEXT_TOKEN } from './firebase.firestore'; + +/** + * Service that provides access to the app's FirestoreContext. + */ +@Injectable({ + providedIn: 'root' +}) +export class DbxFirestoreContextService { + + constructor(@Inject(DBX_FIRESTORE_CONTEXT_TOKEN) readonly firestoreContext: FirestoreContext) { } + +} diff --git a/packages/dbx-firebase/src/lib/firestore/index.ts b/packages/dbx-firebase/src/lib/firestore/index.ts new file mode 100644 index 000000000..38a7cf95c --- /dev/null +++ b/packages/dbx-firebase/src/lib/firestore/index.ts @@ -0,0 +1,3 @@ +export * from './firebase.firestore'; +export * from './firebase.firestore.module'; +export * from './firestore.context.service'; diff --git a/packages/dbx-firebase/src/lib/index.ts b/packages/dbx-firebase/src/lib/index.ts index 59e6e4384..232d79f4c 100644 --- a/packages/dbx-firebase/src/lib/index.ts +++ b/packages/dbx-firebase/src/lib/index.ts @@ -1,2 +1,4 @@ export * from './auth'; export * from './firebase'; +export * from './firestore'; +export * from './model'; diff --git a/packages/dbx-firebase/src/lib/model/index.ts b/packages/dbx-firebase/src/lib/model/index.ts new file mode 100644 index 000000000..d0bd8870a --- /dev/null +++ b/packages/dbx-firebase/src/lib/model/index.ts @@ -0,0 +1,2 @@ +export * from './list'; +// export * from './model.module'; diff --git a/packages/dbx-firebase/src/lib/model/list/index.ts b/packages/dbx-firebase/src/lib/model/list/index.ts new file mode 100644 index 000000000..751f4181a --- /dev/null +++ b/packages/dbx-firebase/src/lib/model/list/index.ts @@ -0,0 +1,2 @@ +export * from './model.list.directive'; +export * from './model.list.module'; diff --git a/packages/dbx-firebase/src/lib/model/list/model.list.directive.ts b/packages/dbx-firebase/src/lib/model/list/model.list.directive.ts new file mode 100644 index 000000000..afc74a411 --- /dev/null +++ b/packages/dbx-firebase/src/lib/model/list/model.list.directive.ts @@ -0,0 +1,74 @@ +import { itemAccumulator, iterationCurrentPageListLoadingState, PageListLoadingState } from '@dereekb/rxjs'; +import { cleanupDestroyable, filterMaybe, listLoadingStateContext, useFirst } from '@dereekb/rxjs'; +import { BehaviorSubject, combineLatest, map, shareReplay, distinctUntilChanged, first, Subject, debounce, debounceTime, throttleTime, switchMap, Observable } from 'rxjs'; +import { Directive, OnDestroy, OnInit } from "@angular/core"; +import { DbxListView } from "@dereekb/dbx-web"; +import { FirestoreCollection, FirestoreQueryConstraint } from '@dereekb/firebase'; +import { ArrayOrValue, Maybe } from '@dereekb/util'; +import { DbxFirebaseModelList } from './model.list'; + +/** + * Directive that hooks into a DbxListView to pass data for rendering models. + */ +@Directive({ + selector: 'dbxFirebaseModelList' +}) +export class DbxFirebaseModelListDirective implements DbxFirebaseModelList, OnInit, OnDestroy { + + private _collection = new BehaviorSubject>>(undefined); + + private _limit = new BehaviorSubject>(undefined); + private _constraints = new BehaviorSubject>>(undefined); + private _reset = new Subject(); + + readonly collection$ = this._collection.pipe(filterMaybe()); + readonly constraints$ = this._constraints.pipe(distinctUntilChanged()); + + readonly iteratorFilter$ = combineLatest([this._limit.pipe(distinctUntilChanged()), this.constraints$]).pipe( + map(([limit, constraints]) => ({ limit, constraints })), + shareReplay(1) + ); + + readonly firestoreIteration$ = combineLatest([this.collection$, this.iteratorFilter$, this._reset]).pipe( + throttleTime(100, undefined, { trailing: true }), // prevent rapid changes and executing filters too quickly. + map(([collection, iteratorFilter]) => collection.firestoreIteration(iteratorFilter)), + cleanupDestroyable(), // cleanup the iteration + shareReplay(1) + ); + + readonly accumulator$ = this.firestoreIteration$.pipe( + map(x => itemAccumulator(x)), + cleanupDestroyable(), + shareReplay(1) + ); + + readonly pageLoadingState$: Observable> = this.accumulator$.pipe( + switchMap(x => iterationCurrentPageListLoadingState(x) as Observable>), + shareReplay(1) + ); + + readonly stateContext = listLoadingStateContext({ obs: this.pageLoadingState$, showLoadingOnNoValue: false }); + + constructor(readonly dbxListView: DbxListView) { } + + ngOnInit(): void { + this.dbxListView.setListContext(this.stateContext); + } + + ngOnDestroy(): void { + this._collection.complete(); + this._constraints.complete(); + this._limit.complete(); + this._reset.complete(); + } + + // MARK: DbxFirebaseModelList + next() { + useFirst(this.firestoreIteration$, (x) => x.next()); + } + + reset() { + this._reset.next(); + } + +} diff --git a/packages/dbx-firebase/src/lib/model/list/model.list.module.ts b/packages/dbx-firebase/src/lib/model/list/model.list.module.ts new file mode 100644 index 000000000..f2b55070c --- /dev/null +++ b/packages/dbx-firebase/src/lib/model/list/model.list.module.ts @@ -0,0 +1,6 @@ +import { NgModule } from "@angular/core"; + +@NgModule({ + imports: [] +}) +export class DbxFirebaseModelListModule { } diff --git a/packages/dbx-firebase/src/lib/model/list/model.list.ts b/packages/dbx-firebase/src/lib/model/list/model.list.ts new file mode 100644 index 000000000..56f147baa --- /dev/null +++ b/packages/dbx-firebase/src/lib/model/list/model.list.ts @@ -0,0 +1,20 @@ +import { FirestoreItemPageIterationInstance, FirestoreQueryConstraint } from "@dereekb/firebase"; +import { Maybe, ArrayOrValue } from "@dereekb/util"; +import { Observable } from "rxjs"; + +export abstract class DbxFirebaseModelList { + + abstract readonly constraints$: Observable>>; + abstract readonly firestoreIteration$: Observable>; + + /** + * Loads more items. + */ + abstract next(): void; + + /** + * Resets the list. + */ + abstract reset(): void; + +} diff --git a/packages/dbx-firebase/src/lib/model/model.module.ts b/packages/dbx-firebase/src/lib/model/model.module.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/dbx-web/src/lib/layout/column/_column.scss b/packages/dbx-web/src/lib/layout/column/_column.scss index 6bd58d708..e2596f326 100644 --- a/packages/dbx-web/src/lib/layout/column/_column.scss +++ b/packages/dbx-web/src/lib/layout/column/_column.scss @@ -9,7 +9,7 @@ $two-columns-right-padding-size: 6px; // MARK: Mixin @mixin core() { - .dbx-two-columns { + .dbx-two-column { height: 100%; max-width: 100%; display: flex; @@ -55,7 +55,7 @@ $two-columns-right-padding-size: 6px; } } - .dbx-two-columns-head { + .dbx-two-column-head { padding: 0 6px; border-bottom: 1px solid rgba(0, 0, 0, 0.14); height: $two-column-navbar-height; @@ -73,10 +73,10 @@ $two-columns-right-padding-size: 6px; } } - .dbx-two-columns-right { + .dbx-two-column-right { height: 100%; - .dbx-two-columns-right-content { + .dbx-two-column-right-content { height: calc(100% - #{$two-column-navbar-height}); overflow-x: hidden; overflow-y: auto; @@ -101,7 +101,7 @@ $two-columns-right-padding-size: 6px; @mixin color($theme-config) { - .dbx-two-columns { + .dbx-two-column { .left-column { @include theming.if-small-screen($theme-config) { width: 100% !important; diff --git a/packages/dbx-web/src/lib/layout/column/one/one.column.component.ts b/packages/dbx-web/src/lib/layout/column/one/one.column.component.ts index 52eaeb72b..c0855b955 100644 --- a/packages/dbx-web/src/lib/layout/column/one/one.column.component.ts +++ b/packages/dbx-web/src/lib/layout/column/one/one.column.component.ts @@ -8,10 +8,10 @@ import { TwoColumnsContextStore } from '../two'; selector: 'dbx-one-column', template: ` - + - + `, exportAs: 'columns', diff --git a/packages/dbx-web/src/lib/layout/column/two/two.column.component.spec.ts b/packages/dbx-web/src/lib/layout/column/two/two.column.component.spec.ts index a36f1a1a0..fc2387a0e 100644 --- a/packages/dbx-web/src/lib/layout/column/two/two.column.component.spec.ts +++ b/packages/dbx-web/src/lib/layout/column/two/two.column.component.spec.ts @@ -30,14 +30,14 @@ describe('DbxTwoColumnsComponent', () => { @Component({ template: ` - +

Left Content

Right Content

-
+ `, providers: ProvideTwoColumnsContext() }) diff --git a/packages/dbx-web/src/lib/layout/column/two/two.column.component.ts b/packages/dbx-web/src/lib/layout/column/two/two.column.component.ts index ae6e71020..bd5c45113 100644 --- a/packages/dbx-web/src/lib/layout/column/two/two.column.component.ts +++ b/packages/dbx-web/src/lib/layout/column/two/two.column.component.ts @@ -21,11 +21,11 @@ export interface DbxTwoColumnsViewState { * Requires a TwoColumnsContextStore to be provided. */ @Component({ - selector: 'dbx-two-columns', + selector: 'dbx-two-column', templateUrl: './two.column.component.html', exportAs: 'columns', host: { - "class": "dbx-two-columns", + "class": "dbx-two-column", "[class]": "{ 'right-shown': v.showRight, 'full-left': v.fullLeft, 'two-column-reverse-sizing': v.reverseSizing, 'dbx-section-page-two': v.inSectionPage }" }, changeDetection: ChangeDetectionStrategy.OnPush diff --git a/packages/dbx-web/src/lib/layout/column/two/two.column.head.component.ts b/packages/dbx-web/src/lib/layout/column/two/two.column.head.component.ts index 4f302a4fb..cdaeb5ac3 100644 --- a/packages/dbx-web/src/lib/layout/column/two/two.column.head.component.ts +++ b/packages/dbx-web/src/lib/layout/column/two/two.column.head.component.ts @@ -4,9 +4,9 @@ import { Component, Input } from '@angular/core'; * Two Columns related component that sits at the top of the content bodies and wraps content. */ @Component({ - selector: 'dbx-two-columns-head', + selector: 'dbx-two-column-head', template: ` -
+
` diff --git a/packages/dbx-web/src/lib/layout/column/two/two.column.right.component.html b/packages/dbx-web/src/lib/layout/column/two/two.column.right.component.html index 3dee3bc77..241b93ed8 100644 --- a/packages/dbx-web/src/lib/layout/column/two/two.column.right.component.html +++ b/packages/dbx-web/src/lib/layout/column/two/two.column.right.component.html @@ -1,4 +1,4 @@ - +