Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Add answers support #51

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"dependencies": {
"angular2": "2.0.0-beta.7",
"angular2-universal-preview": "0.55.4",
"angularfire2": "2.0.0-alpha.9",
"angularfire2": "2.0.0-alpha.13",
"css": "2.2.1",
"es6-promise": "3.1.2",
"es6-shim": "0.33.13",
Expand Down Expand Up @@ -47,6 +47,6 @@
"ts-node": "0.5.4",
"typescript": "1.7.5",
"typescript-node": "0.1.3",
"typings": "^0.6.8"
"typings": "^0.6.9"
}
}
4 changes: 4 additions & 0 deletions src/css/core/_header.scss
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,8 @@
margin: 0;
color: #FFF;
text-decoration: none;
padding-right: 20px;
&:last-child {
padding-right: 0;
}
}
2 changes: 2 additions & 0 deletions src/main-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ng2engine,
REQUEST_URL
} from 'angular2-universal-preview/dist/server';
import {FIREBASE_PROVIDERS} from 'angularfire2';

import {provide} from 'angular2/core';
import {APP_BASE_HREF, ROUTER_PROVIDERS} from 'angular2/router';
Expand All @@ -32,6 +33,7 @@ app.use('/', (req, res) => {
res.render('index', { App, providers: [
ROUTER_PROVIDERS,
SERVER_LOCATION_PROVIDERS,
FIREBASE_PROVIDERS,
provide(REQUEST_URL, {useValue: req.originalUrl}),
provide(APP_BASE_HREF, {useValue: `http://localhost:3000${req.baseUrl}`}),
provide(REQUEST_URL, {useValue: 'http://localhost:3000'}),
Expand Down
18 changes: 14 additions & 4 deletions src/shared-providers.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
import {provide} from 'angular2/core';
import {FIREBASE_PROVIDERS, defaultFirebase} from 'angularfire2';
import {
defaultFirebase,
firebaseAuthConfig,
AuthMethods,
AuthProviders
} from 'angularfire2';

import {AuthService} from './worker/services/Auth';
import {Backend, BackendConfig} from './worker/services/Backend';
import {QuestionService} from './worker/services/QuestionService';

import {AnswerService} from './worker/services/AnswerService';

export const SHARED_PROVIDERS = [
AuthService,
QuestionService,
FIREBASE_PROVIDERS,
defaultFirebase('answers-mobile.firebaseio.com')
AnswerService,
defaultFirebase('answers-mobile.firebaseio.com'),
firebaseAuthConfig({
method: AuthMethods.Redirect,
provider: AuthProviders.Github
})
];

12 changes: 12 additions & 0 deletions src/ui/main_ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,18 @@ import {
} from 'angular2/platform/worker_render';
import {platform, provide} from 'angular2/core';
import {BOOTSTRAP_CHANNEL} from '../shared/channels';
import {
defaultFirebase,
} from 'angularfire2';
import {WORKER_RENDER_FIREBASE_PROVIDERS} from 'angularfire2/angularfire2_worker_render';
// Need to import from providers directly
// until https://github.com/angular/angularfire2/pull/111 is merged
import {MessageBasedFirebaseAuth} from 'angularfire2/providers/web_workers/ui/auth';

let appRef = platform([WORKER_RENDER_PLATFORM])
.application([
WORKER_RENDER_FIREBASE_PROVIDERS,
defaultFirebase('answers-mobile.firebaseio.com'),
WORKER_RENDER_APPLICATION,
WORKER_RENDER_ROUTER,
provide(WORKER_SCRIPT, {useValue: '/loader.js'})
Expand All @@ -22,3 +31,6 @@ bus.from(BOOTSTRAP_CHANNEL).subscribe((message: string) => {
(<any> window).preboot.complete();
}
});

// Need to manually call start until https://github.com/angular/angular/issues/7420 is implemented
appRef.injector.get(MessageBasedFirebaseAuth).start();
32 changes: 32 additions & 0 deletions src/worker/components/AnswerList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {Component, Input} from 'angular2/core';
import {ROUTER_DIRECTIVES} from 'angular2/router';
import {Answer} from '../services/AnswerService';

const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August',
'September', 'October', 'November', 'December'];

@Component({
selector: 'answer-list',
template: `
<div class="card" *ngFor="#answer of answers">
<p>{{ answer.text }}</p>
<p class='username'>Answered by {{ answer.username }} on {{ timestampToDate (answer.timestamp) }}.</p>
</div>
`,
directives: [ROUTER_DIRECTIVES],
styles: [`
.username {
font-size: 12pt;
text-align: right;
}
`]
})
export class AnswerList {
@Input() answers: Answer[];

timestampToDate (timestamp: number): string {
let date = new Date(timestamp);
let month = months[date.getMonth()];
return `${month} ${date.getDate()}`;
}
}
52 changes: 52 additions & 0 deletions src/worker/components/CreateAnswer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {Component, Input, ChangeDetectionStrategy} from 'angular2/core';
import {AnswerService, Answer} from '../services/AnswerService';
import {FirebaseAuth} from 'angularfire2';

import * as Firebase from 'firebase';

@Component({
selector: 'create-answer',
template: `
<div [ngSwitch]="(auth | async)" class="card">
<span *ngSwitchWhen="null">
<h3>You must <a (click)="auth.login()">login</a> to answer this question.</h3>
</span>
<span *ngSwitchDefault>
<h3>Add Answer</h3>
<div class="new-answer-container">
<input type="text" placeholder="Answer Here" [(ngModel)]="newAnswer.text" />
</div>
<button (click)="addAnswer()">Save</button>
</span>
</div>
`,
styles: [
`.new-answer-container {
display: flex;
flex-direction: column
}`
]
})
export class CreateAnswer {
@Input() questionId: string;
newAnswer: Answer = {
timestamp: Firebase.ServerValue.TIMESTAMP,
text: '',
uid: '',
username: ''
};

constructor(private _answerService: AnswerService, public auth: FirebaseAuth){
this.auth.subscribe ((authData) => {
if (authData != null) {
this.newAnswer.uid = authData.uid;
this.newAnswer.username = authData.github.username;
}
});
}

addAnswer(){
this._answerService.addAnswer(this.questionId, this.newAnswer);
this.newAnswer.text = '';
}
}
22 changes: 19 additions & 3 deletions src/worker/components/header.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,38 @@
import {Component} from 'angular2/core';
import {Component, ChangeDetectionStrategy} from 'angular2/core';
import {Nav} from '../services/Nav';
import {ROUTER_DIRECTIVES} from 'angular2/router';
import {FirebaseAuth} from 'angularfire2';

@Component({
selector: 'app-header',
template: `
<img role="tab" class="header__menu js-toggle-menu" src="/images/ic_menu_24px.svg" alt="toggle_nav" (click)="openSideNav()"/>

<a class="header__title" [routerLink]="['Questions']">Angular Answers</a>
<a class="header__item" *ngIf="!(auth | async)" (click)="login()">Login</a>
<a class="header__item" *ngIf="auth | async" (click)="logout()">Logout</a>
<a class="header__item" [routerLink]="['CreateQuestion']">New Question</a>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
directives: [ROUTER_DIRECTIVES]
})
export class AppHeader {
constructor(private nav: Nav){

constructor(private nav: Nav, public auth: FirebaseAuth){
this.auth.subscribe((data) => {
console.log('auth data', data);
});
}
openSideNav(){
this.nav.open()
}

login() {
this.auth.login()
.then(() => console.log("Success!"),
(err) => console.error(err));
}

logout() {
this.auth.logout();
}
}
26 changes: 16 additions & 10 deletions src/worker/containers/createQuestion.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
import {Component} from 'angular2/core';
import {QuestionService} from '../services/QuestionService';
import {Router, RouteConfig} from 'angular2/router'
import {FirebaseAuth} from 'angularfire2';

@Component({
selector: 'create-question-container',
template: `
<div class="card">
<h3>Ask a new question</h3>
<div class="new-question-container">
<div [ngSwitch]="(auth | async)" class="card">
<span *ngSwitchWhen="null">
<h3>Please <a (click)="auth.login()">login</a> to continue.</h3>
</span>
<span *ngSwitchDefault>
<h3>Ask a new question</h3>
<div class="new-question-container">

<input type="text" placeholder="Question Title" [(ngModel)]="newQuestion.title"/>
<textarea placeholder="Ask your Question Here" [(ngModel)]="newQuestion.text"></textarea>

<input type="text" placeholder="Question Title" [(ngModel)]="newQuestion.title"/>
<textarea placeholder="Ask your Question Here" [(ngModel)]="newQuestion.text"></textarea>

</div>
<button (click)="addQuestion()">Save</button>
</div>
<button (click)="addQuestion()">Save</button>
</span>
</div>

`,
styles: [
`.new-question-container {
Expand All @@ -26,7 +31,8 @@ import {Router, RouteConfig} from 'angular2/router'
})
export class CreateQuestionContainer {
newQuestion = {};
constructor(private questionService:QuestionService, private router:Router){}
constructor(private questionService:QuestionService, private router:Router,
public auth: FirebaseAuth){}
addQuestion(){
this.questionService.addQuestion(this.newQuestion);
this.router.navigate(['../Questions']);
Expand Down
18 changes: 15 additions & 3 deletions src/worker/containers/questionDetail.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import {Component, ChangeDetectionStrategy} from 'angular2/core';
import {QuestionService} from '../services/QuestionService';
import {RouteParams} from 'angular2/router';
import {AnswerList} from '../components/AnswerList';
import {CreateAnswer} from '../components/CreateAnswer';
import {AnswerService} from '../services/AnswerService';

@Component({
selector: 'question-detail-container',
Expand All @@ -9,12 +12,21 @@ import {RouteParams} from 'angular2/router';
<h3>{{ (question | async)?.title }}</h3>
<p>{{ (question | async)?.text }}</p>
</div>
<answer-list [answers]="answers | async"></answer-list>
<create-answer [questionId]="id"></create-answer>
`,
changeDetection: ChangeDetectionStrategy.OnPush
changeDetection: ChangeDetectionStrategy.OnPush,
directives: [AnswerList, CreateAnswer]
})
export class QuestionDetailContainer {
question: any;
constructor(private questionService:QuestionService, params:RouteParams){
this.question = questionService.getQuestionById(params.get('id'));
id: string;
answers: any;

constructor(private _questionService:QuestionService, private _answerService: AnswerService,
params:RouteParams){
this.id = params.get('id');
this.question = this._questionService.getQuestionById(this.id);
this.answers = this._answerService.getAnswersByQuestionId(this.id);
}
}
8 changes: 5 additions & 3 deletions src/worker/containers/questions.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import {Component, ChangeDetectionStrategy} from 'angular2/core';
import {Nav} from '../services/Nav';
import {QuestionService} from '../services/QuestionService';
import {QuestionList} from '../components/QuestionList'
import {QuestionList} from '../components/QuestionList';
import {FirebaseAuth} from 'angularfire2';

@Component({
selector: 'home-container',
template: `
<div class="card">
<h3>Recent Questions</h3>
<h3 *ngIf='!(auth | async)'>Recent Questions</h3>
<h3 *ngIf='(auth | async)'>Hi {{(auth | async).github.displayName}}, here are some recent questions</h3>
</div>
<question-list [questions]="questionService.questions | async"></question-list>
`,
directives: [QuestionList],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class QuestionsContainer {
constructor(private questionService:QuestionService){}
constructor(private questionService:QuestionService, public auth: FirebaseAuth){}
}
2 changes: 2 additions & 0 deletions src/worker/main_worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ import {platform, provide, ComponentRef, Injector} from 'angular2/core';
import {APP_BASE_HREF} from 'angular2/router';
import {BOOTSTRAP_CHANNEL} from '../shared/channels';
import {SHARED_PROVIDERS} from '../shared-providers';
import {WORKER_APP_FIREBASE_PROVIDERS} from 'angularfire2/angularfire2_worker_app';

import {App} from './app/app';

platform([WORKER_APP_PLATFORM])
.asyncApplication(null, [
WORKER_APP_ROUTER,
WORKER_APP_APPLICATION,
WORKER_APP_FIREBASE_PROVIDERS,
provide(APP_BASE_HREF, {useValue: '/'}),
SHARED_PROVIDERS
]).then((appRef) => appRef.bootstrap(App).then((compRef: ComponentRef) => {
Expand Down
37 changes: 37 additions & 0 deletions src/worker/services/AnswerService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {AngularFire, FirebaseListObservable} from 'angularfire2';
import {Injectable} from 'angular2/core';
const ANSWERS_PATH = '/answers';

@Injectable()
export class AnswerService {
private _answersList: {[key: string]: FirebaseListObservable<any>} = {};

constructor (private _angularFire: AngularFire) {}

getAnswersByQuestionId(id:string) {
return this._lookupByQuestionId (id)
.map(l => l.map(v => v.val()));
}

addAnswer(questionId: string, newAnswer: any) {
this._lookupByQuestionId(questionId).add(newAnswer);
}

private _lookupByQuestionId(id: string): FirebaseListObservable<any> {
let observable: FirebaseListObservable<any> = null;
if (this._answersList[id])
observable = this._answersList[id];
else {
observable = this._angularFire.list(ANSWERS_PATH + `/${id}`, {preserveSnapshot: true});
this._answersList[id] = observable;
}
return observable;
}
}

export interface Answer {
text: string;
uid: string;
timestamp: number;
username: string;
}