-

-
{{username}}
+
+
![avatar]()
+

+
{{user.username}}
- Update Picture
+ Update Picture
Change Username
+ Change Email
Change Password
Support
Logout
+
{{ jobDescription }}
+
+
+
diff --git a/src/app/pages/account/account.module.ts b/src/app/pages/account/account.module.ts
index da13bb878..ba5a81e63 100644
--- a/src/app/pages/account/account.module.ts
+++ b/src/app/pages/account/account.module.ts
@@ -4,6 +4,9 @@ import { IonicModule } from '@ionic/angular';
import { AccountPage } from './account';
import { AccountPageRoutingModule } from './account-routing.module';
+import { UploadImageComponent } from '../upload-image/upload-image.component';
+import { FileSizePipe } from '../../pipe/file-size.pipe';
+import { DropZoneDirective } from '../../directive/drop-zone.directive';
@NgModule({
imports: [
@@ -13,6 +16,9 @@ import { AccountPageRoutingModule } from './account-routing.module';
],
declarations: [
AccountPage,
+ UploadImageComponent,
+ FileSizePipe,
+ DropZoneDirective
]
})
export class AccountModule { }
diff --git a/src/app/pages/account/account.ts b/src/app/pages/account/account.ts
index d65456d0a..10999eaa0 100644
--- a/src/app/pages/account/account.ts
+++ b/src/app/pages/account/account.ts
@@ -1,46 +1,73 @@
import { AfterViewInit, Component, ViewEncapsulation } from '@angular/core';
import { Router } from '@angular/router';
-
import { AlertController } from '@ionic/angular';
import { UserData } from '../../providers/user-data';
-
+import { User } from '../../models';
@Component({
selector: 'page-account',
templateUrl: 'account.html',
styleUrls: ['./account.scss'],
+ encapsulation: ViewEncapsulation.None
})
export class AccountPage implements AfterViewInit {
- username: string;
+ users: User[];
+ user: User;
+ succeed: boolean;
+ loadImage: boolean;
+ jobDescription: string;
- constructor(
- public alertCtrl: AlertController,
- public router: Router,
- public userData: UserData
- ) { }
+ constructor(public alertCtrl: AlertController,
+ public router: Router,
+ public userProvider: UserData) {}
ngAfterViewInit() {
- this.getUsername();
+ this.userProvider.getUsers().subscribe(
+ users => this.users = users
+ );
+ this.userProvider.getUserId().then(id => {
+ this.userProvider.getUserById(id).then(data => { this.user = data; });
+ });
}
- updatePicture() {
- console.log('Clicked to update picture');
+ updatePicture(path: string) {
+ const oldUrl = this.user.avatar;
+ const id = this.user.id;
+
+ this.user.avatar = path;
+ this.userProvider.updateUser(this.user);
+
+ this.loadImage = false;
+ this.user = null;
+ this.userProvider.getUserById(id).then(data => { this.user = data; });
+ this.userProvider.deleteUrl(oldUrl);
+ }
+
+ onExit() {
+ this.loadImage = false;
}
// Present an alert with the current username populated
// clicking OK will update the username and display it
// clicking Cancel will close the alert and do nothing
async changeUsername() {
- const alert = await this.alertCtrl.create({
+ this.succeed = false;
+ const changeForm = await this.alertCtrl.create({
header: 'Change Username',
buttons: [
'Cancel',
{
text: 'Ok',
handler: (data: any) => {
- this.userData.setUsername(data.username);
- this.getUsername();
+ if (this.isTheValueUsed(data.username)) {
+ alert(data.username + ' was used already. Try another.');
+ } else {
+ this.user.username = data.username;
+ this.userProvider.updateUser(this.user);
+ this.succeed = true;
+ this.jobDescription = 'Username has been changed.';
+ }
}
}
],
@@ -48,26 +75,102 @@ export class AccountPage implements AfterViewInit {
{
type: 'text',
name: 'username',
- value: this.username,
- placeholder: 'username'
+ value: this.user.username,
+ placeholder: 'new username'
+ }
+ ]
+ });
+ await changeForm.present();
+ }
+
+ async changeEmail() {
+ this.succeed = false;
+ const changeForm = await this.alertCtrl.create({
+ header: 'Change Email',
+ buttons: [
+ 'Cancel',
+ {
+ text: 'Ok',
+ handler: (data: any) => {
+ if (this.isTheValueUsed(data.email)) {
+ alert(data.email + ' was used already. Try another.');
+ } else {
+ this.user.email = data.email;
+ this.userProvider.updateUser(this.user);
+ this.succeed = true;
+ this.jobDescription = 'Email has been changed.';
+ }
+ }
+ }
+ ],
+ inputs: [
+ {
+ type: 'email',
+ name: 'email',
+ value: this.user.email,
+ placeholder: 'new email'
}
]
});
- await alert.present();
+ await changeForm.present();
}
- getUsername() {
- this.userData.getUsername().then((username) => {
- this.username = username;
+ async changePassword() {
+ this.succeed = false;
+ const changeForm = await this.alertCtrl.create({
+ header: 'Change Password',
+ buttons: [
+ 'Cancel',
+ {
+ text: 'Ok',
+ handler: (data: any) => {
+ if (this.user.password !== data.currentPW) {
+ alert('Current password does not match your password.');
+ } else if (data.newPW.length < 4) {
+ alert('Password should be more than 3 characters.');
+ } else if (data.newPW !== data.confirmPW) {
+ alert('New password does not match Confirm password.');
+ } else {
+ this.user.password = data.newPW;
+ this.userProvider.updateUser(this.user);
+ this.succeed = true;
+ this.jobDescription = 'Password has been changed.';
+ }
+ }
+ }
+ ],
+ inputs: [
+ {
+ type: 'password',
+ name: 'newPW',
+ placeholder: 'new password'
+ },
+ {
+ type: 'password',
+ name: 'confirmPW',
+ placeholder: 'confirm password'
+ },
+ {
+ type: 'password',
+ name: 'currentPW',
+ placeholder: 'current password'
+ }
+ ]
});
+ await changeForm.present();
}
- changePassword() {
- console.log('Clicked to change password');
+ isTheValueUsed(value: string) {
+ if (value.indexOf('@') < 0) {
+ return this.users.find(
+ user => user.username.toLowerCase() === value.toLowerCase());
+ }
+ return this.users.find(
+ user => user.email.toLowerCase() === value.toLowerCase());
}
logout() {
- this.userData.logout();
+ this.userProvider.logout();
this.router.navigateByUrl('/login');
}
diff --git a/src/app/pages/login/login.html b/src/app/pages/login/login.html
index 4b7d92aa8..b228c32a3 100644
--- a/src/app/pages/login/login.html
+++ b/src/app/pages/login/login.html
@@ -1,5 +1,5 @@
-
+
@@ -17,25 +17,25 @@
Username
-
-
+
Username is required
Password
-
+
-
+
Password is required
diff --git a/src/app/pages/login/login.ts b/src/app/pages/login/login.ts
index eca7d56e2..6f747691d 100644
--- a/src/app/pages/login/login.ts
+++ b/src/app/pages/login/login.ts
@@ -1,34 +1,53 @@
-import { Component, ViewEncapsulation } from '@angular/core';
+import { Component, ViewEncapsulation, OnInit } from '@angular/core';
import { NgForm } from '@angular/forms';
import { Router } from '@angular/router';
import { UserData } from '../../providers/user-data';
-
-import { UserOptions } from '../../interfaces/user-options';
-
-
+import { User } from '../../models';
@Component({
selector: 'page-login',
templateUrl: 'login.html',
styleUrls: ['./login.scss'],
+ encapsulation: ViewEncapsulation.None
})
-export class LoginPage {
- login: UserOptions = { username: '', password: '' };
- submitted = false;
-
- constructor(
- public userData: UserData,
- public router: Router
- ) { }
+export class LoginPage implements OnInit {
+ username: '';
+ password: '' ;
+ users: User[];
+
+ constructor(public userData: UserData,
+ public router: Router,
+ private userProvider: UserData) { }
+
+ ngOnInit() {
+ this.userProvider.getUsers().subscribe(
+ (data: User[]) => { this.users = data; }
+ );
+ }
onLogin(form: NgForm) {
- this.submitted = true;
-
if (form.valid) {
- this.userData.login(this.login.username);
- this.router.navigateByUrl('/app/tabs/schedule');
+ const user = this.findUser(this.username.toLowerCase().trim());
+ if (user) {
+ if (user.password === this.password) {
+ this.userData.login(user);
+ // this.router.navigateByUrl('/app/tabs/schedule')
+ this.router.navigateByUrl('/app/tabs/(schedule:schedule)');
+ } else {
+ alert('Invalid password. Try again.');
+ }
+ } else {
+ alert('User not found. Try again.');
+ }
+ }
+ }
+
+ findUser(data: string) {
+ if (data.indexOf('@') > -1) {
+ return this.users.find(user => user.email.toLowerCase() === data);
}
+ return this.users.find(user => user.username.toLowerCase() === data);
}
onSignup() {
diff --git a/src/app/pages/map/map.html b/src/app/pages/map/map.html
index 68d390b2f..b234023b6 100644
--- a/src/app/pages/map/map.html
+++ b/src/app/pages/map/map.html
@@ -1,5 +1,5 @@
-
+
diff --git a/src/app/pages/map/map.ts b/src/app/pages/map/map.ts
index 27a531f97..e01a24bc7 100644
--- a/src/app/pages/map/map.ts
+++ b/src/app/pages/map/map.ts
@@ -1,35 +1,41 @@
-import { Component, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
+import { Component, ElementRef, ViewChild, ViewEncapsulation } from '@angular/core';
+
import { ConferenceData } from '../../providers/conference-data';
+
import { Platform } from '@ionic/angular';
+declare var google: any;
+
@Component({
selector: 'page-map',
templateUrl: 'map.html',
- styleUrls: ['./map.scss']
+ styleUrls: ['./map.scss'],
+ encapsulation: ViewEncapsulation.None
})
-export class MapPage implements AfterViewInit {
+export class MapPage {
+
@ViewChild('mapCanvas') mapElement: ElementRef;
- constructor(public confData: ConferenceData, public platform: Platform) {}
+ constructor(
+ public dataProvider: ConferenceData,
+ public platform: Platform
+ ) { }
- async ngAfterViewInit() {
- const googleMaps = await getGoogleMaps(
- 'AIzaSyB8pf6ZdFQj5qw7rc_HSGrhUwQKfIe9ICw'
- );
- this.confData.getMap().subscribe((mapData: any) => {
+ ionViewDidEnter() {
+ this.dataProvider.getMap().subscribe((mapData: any) => {
const mapEle = this.mapElement.nativeElement;
- const map = new googleMaps.Map(mapEle, {
- center: mapData.find((d: any) => d.center),
+ const map = new google.maps.Map(mapEle, {
+ center: mapData.find((loc: any) => loc.center),
zoom: 16
});
mapData.forEach((markerData: any) => {
- const infoWindow = new googleMaps.InfoWindow({
+ const infoWindow = new google.maps.InfoWindow({
content: `${markerData.name}
`
});
- const marker = new googleMaps.Marker({
+ const marker = new google.maps.Marker({
position: markerData,
map,
title: markerData.name
@@ -40,33 +46,9 @@ export class MapPage implements AfterViewInit {
});
});
- googleMaps.event.addListenerOnce(map, 'idle', () => {
+ google.maps.event.addListenerOnce(map, 'idle', () => {
mapEle.classList.add('show-map');
});
});
}
}
-
-function getGoogleMaps(apiKey: string): Promise {
- const win = window as any;
- const googleModule = win.google;
- if (googleModule && googleModule.maps) {
- return Promise.resolve(googleModule.maps);
- }
-
- return new Promise((resolve, reject) => {
- const script = document.createElement('script');
- script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&v=3.31`;
- script.async = true;
- script.defer = true;
- document.body.appendChild(script);
- script.onload = () => {
- const googleModule2 = win.google;
- if (googleModule2 && googleModule2.maps) {
- resolve(googleModule2.maps);
- } else {
- reject('google maps not available');
- }
- };
- });
-}
diff --git a/src/app/pages/schedule-filter/schedule-filter.html b/src/app/pages/schedule-filter/schedule-filter.html
index ffafd4cd0..4bb361d3c 100644
--- a/src/app/pages/schedule-filter/schedule-filter.html
+++ b/src/app/pages/schedule-filter/schedule-filter.html
@@ -1,5 +1,5 @@
-
+
Cancel
@@ -19,12 +19,11 @@
Tracks
-
+
{{track.name}}
-
+
-
diff --git a/src/app/pages/schedule-filter/schedule-filter.ts b/src/app/pages/schedule-filter/schedule-filter.ts
index 816631471..e2bc7692d 100644
--- a/src/app/pages/schedule-filter/schedule-filter.ts
+++ b/src/app/pages/schedule-filter/schedule-filter.ts
@@ -2,51 +2,51 @@ import { AfterViewInit, Component, ViewEncapsulation } from '@angular/core';
import { ModalController } from '@ionic/angular';
import { ConferenceData } from '../../providers/conference-data';
-
+import { UserData } from '../../providers/user-data';
+import { User } from '../../models';
@Component({
selector: 'page-schedule-filter',
templateUrl: 'schedule-filter.html',
styleUrls: ['./schedule-filter.scss'],
+ encapsulation: ViewEncapsulation.None
})
export class ScheduleFilterPage implements AfterViewInit {
-
- tracks: {name: string, isChecked: boolean}[] = [];
+ user: User;
+ trackFilter: { name: string, isChecked: boolean }[] = [];
constructor(
- public confData: ConferenceData,
- public modalCtrl: ModalController
+ public dataProvider: ConferenceData,
+ public modalCtrl: ModalController,
+ public userProvider: UserData
) { }
// TODO use the ionViewDidEnter event
ngAfterViewInit() {
- // passed in array of track names that should be excluded (unchecked)
- const excludedTrackNames = []; // this.navParams.data.excludedTracks;
-
- this.confData.getTracks().subscribe((trackNames: string[]) => {
- trackNames.forEach(trackName => {
- this.tracks.push({
- name: trackName,
- isChecked: (excludedTrackNames.indexOf(trackName) === -1)
- });
- });
+ this.userProvider.getUser().then(user => {
+ this.user = user;
+ this.trackFilter = user.trackFilter;
});
}
resetFilters() {
// reset all of the toggles to be checked
- this.tracks.forEach(track => {
+ this.trackFilter.forEach(track => {
track.isChecked = true;
});
}
applyFilters() {
+ // update user's trackFilter
+ this.user.trackFilter = this.trackFilter;
+ this.userProvider.updateUser(this.user);
+
// Pass back a new array of track names to exclude
- const excludedTrackNames = this.tracks.filter(c => !c.isChecked).map(c => c.name);
+ const excludedTrackNames = this.trackFilter.filter(c => !c.isChecked).map(c => c.name);
this.dismiss(excludedTrackNames);
}
- dismiss(data?: any) {
+ dismiss(data: any) {
// using the injected ModalController this page
// can "dismiss" itself and pass back data
this.modalCtrl.dismiss(data);
diff --git a/src/app/pages/schedule-track/schedule-track.html b/src/app/pages/schedule-track/schedule-track.html
new file mode 100644
index 000000000..a499c53c6
--- /dev/null
+++ b/src/app/pages/schedule-track/schedule-track.html
@@ -0,0 +1,25 @@
+
+
+
+ One Track Session
+
+
+
+ Cancel
+
+
+
+
+
+
+
+
+ Tracks
+
+
+
+ {{track.name}}
+
+
+
+
diff --git a/src/app/pages/schedule-track/schedule-track.scss b/src/app/pages/schedule-track/schedule-track.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/app/pages/schedule-track/schedule-track.ts b/src/app/pages/schedule-track/schedule-track.ts
new file mode 100644
index 000000000..f985cb83d
--- /dev/null
+++ b/src/app/pages/schedule-track/schedule-track.ts
@@ -0,0 +1,41 @@
+import { AfterViewInit, Component, ViewEncapsulation } from '@angular/core';
+import { ModalController } from '@ionic/angular';
+
+import { ConferenceData } from '../../providers/conference-data';
+import { UserData } from '../../providers/user-data';
+import { Track } from '../../models';
+
+@Component({
+ selector: 'page-schedule-track',
+ templateUrl: './schedule-track.html',
+ styleUrls: ['./schedule-track.scss'],
+ encapsulation: ViewEncapsulation.None
+
+})
+export class ScheduleTrackPage implements AfterViewInit {
+ tracks: Track[] = [];
+
+ constructor(private dataProvider: ConferenceData,
+ public modalCtrl: ModalController) { }
+
+ ngAfterViewInit() {
+ this.dataProvider.getTracks().subscribe(
+ (tracks: Track[]) => { this.tracks = tracks; }
+ );
+ }
+
+ applyTrack(name: string) {
+ // Pass back a new array of track names to exclude
+ const excludedTracks = [];
+ this.tracks.forEach(
+ (track) => { if (track.name !== name ) { excludedTracks.push(track.name); }
+ });
+ this.dismiss(excludedTracks);
+ }
+
+ dismiss(data: any) {
+ // using the injected ModalController this page
+ // can "dismiss" itself and pass back data
+ this.modalCtrl.dismiss(data);
+ }
+}
diff --git a/src/app/pages/schedule/schedule.html b/src/app/pages/schedule/schedule.html
index 348eafce6..d04a30178 100644
--- a/src/app/pages/schedule/schedule.html
+++ b/src/app/pages/schedule/schedule.html
@@ -1,13 +1,16 @@
-
+
-
+
All
+
+ Track
+
Favorites
@@ -21,41 +24,45 @@
-
+
-
-
-
-
- {{group.time}}
-
-
-
-
-
+
+
+
+
- {{session.name}}
-
- {{session.timeStart}} — {{session.timeEnd}}: {{session.location}}
-
+ {{daily.date}} : {{group.partOfDay}}
-
-
-
- Favorite
-
-
- Remove
-
-
-
+
+
+
+
+
+ {{session.name}}
+
+ {{session.timeStart}} — {{session.timeEnd}}: {{session.location}}
+
+
+
+
+
+ Favorite
+
+
+ Remove
+
+
+
+
- 0">
- No Sessions Found
+ 0">
+ No Sessions Found
+ You may
+ Login or Signup!
diff --git a/src/app/pages/schedule/schedule.module.ts b/src/app/pages/schedule/schedule.module.ts
index 77ba69818..90f545e15 100644
--- a/src/app/pages/schedule/schedule.module.ts
+++ b/src/app/pages/schedule/schedule.module.ts
@@ -5,6 +5,7 @@ import { IonicModule } from '@ionic/angular';
import { SchedulePage } from './schedule';
import { ScheduleFilterPage } from '../schedule-filter/schedule-filter';
+import { ScheduleTrackPage } from '../schedule-track/schedule-track';
import { SchedulePageRoutingModule } from './schedule-routing.module';
@NgModule({
@@ -16,10 +17,12 @@ import { SchedulePageRoutingModule } from './schedule-routing.module';
],
declarations: [
SchedulePage,
- ScheduleFilterPage
+ ScheduleFilterPage,
+ ScheduleTrackPage
],
entryComponents: [
- ScheduleFilterPage
+ ScheduleFilterPage,
+ ScheduleTrackPage
]
})
export class ScheduleModule { }
diff --git a/src/app/pages/schedule/schedule.scss b/src/app/pages/schedule/schedule.scss
index 0f366a99d..6cc1ffc80 100644
--- a/src/app/pages/schedule/schedule.scss
+++ b/src/app/pages/schedule/schedule.scss
@@ -17,3 +17,13 @@ $categories: (
padding-left: 10px;
}
}
+
+.notFound {
+ display: inline;
+ text-align: center;
+}
+.notFound h4 {
+ margin-top: 70px;
+ font-size: 24px;
+}
+
diff --git a/src/app/pages/schedule/schedule.ts b/src/app/pages/schedule/schedule.ts
index d603f6e41..d29b3f3f2 100644
--- a/src/app/pages/schedule/schedule.ts
+++ b/src/app/pages/schedule/schedule.ts
@@ -1,41 +1,69 @@
-import { Component, ViewChild, OnInit } from '@angular/core';
+import { Component, ViewChild, OnInit, ViewEncapsulation } from '@angular/core';
import { Router } from '@angular/router';
-import { AlertController, IonList, LoadingController, ModalController, ToastController } from '@ionic/angular';
+import { AlertController, List, LoadingController, ModalController, ToastController } from '@ionic/angular';
import { ScheduleFilterPage } from '../schedule-filter/schedule-filter';
+import { ScheduleTrackPage } from '../schedule-track/schedule-track';
import { ConferenceData } from '../../providers/conference-data';
import { UserData } from '../../providers/user-data';
+import { User, Session, PartOfDay } from '../../models';
@Component({
selector: 'page-schedule',
templateUrl: 'schedule.html',
styleUrls: ['./schedule.scss'],
+ encapsulation: ViewEncapsulation.None
})
export class SchedulePage implements OnInit {
// Gets a reference to the list element
- @ViewChild('scheduleList') scheduleList: IonList;
+ @ViewChild('scheduleList') scheduleList: List;
- dayIndex = 0;
+ user: User;
queryText = '';
- segment = 'all';
- excludeTracks: any = [];
- shownSessions: any = [];
- groups: any = [];
- confDate: string;
+ segment = '';
+ excludeTracks: any[] = [];
+
+ start = '2019-01-10';
+ end = '2019-01-15';
+ changePeriod = true;
+
+ partsOfDay: PartOfDay[];
+ sessions: Session[];
+ schedule: {
+ date: string,
+ groups: {
+ indexKey: number
+ partOfDay: string,
+ sessions: any[]
+ }[]
+ }[] = [];
constructor(
public alertCtrl: AlertController,
- public confData: ConferenceData,
+ public dataProvider: ConferenceData,
public loadingCtrl: LoadingController,
public modalCtrl: ModalController,
public router: Router,
public toastCtrl: ToastController,
- public user: UserData
+ public userProvider: UserData
) { }
ngOnInit() {
// this.app.setTitle('Schedule');
- this.updateSchedule();
+ this.userProvider.isLoggedIn().then(loggedIn => {
+ if (loggedIn) {
+ this.userProvider.getUser().then(user => {
+ user.trackFilter.forEach(track => {
+ if (!track.isChecked) { this.excludeTracks.push(track.name); }
+ });
+ this.user = user;
+ this.dataProvider.getPartsOfDay().subscribe(
+ response => { this.partsOfDay = response ; }
+ );
+ this.updateSchedule();
+ });
+ }
+ });
}
updateSchedule() {
@@ -44,12 +72,104 @@ export class SchedulePage implements OnInit {
this.scheduleList.closeSlidingItems();
}
- this.confData.getTimeline(this.dayIndex, this.queryText, this.excludeTracks, this.segment).subscribe((data: any) => {
- this.shownSessions = data.shownSessions;
- this.groups = data.groups;
+ if (this.schedule.length === 0 || this.changePeriod) {
+ this.changePeriod = false;
+ this.dataProvider.getSessionInPeriod(this.start, this.end).subscribe(
+ (response: Session[]) => {
+ this.sessions = response;
+ this.buildSchedule();
+ });
+ }
+ }
+
+ getFilterOption() {
+ return {
+ queryText: this.queryText.toLowerCase().trim(),
+ segment: this.segment,
+ excludeTracks: this.excludeTracks
+ };
+ }
+
+ buildSchedule() {
+ const filterOption = this.getFilterOption();
+ this.sessions.forEach(session => {
+ session = this.dataProvider.filterSession(session, filterOption);
+ const partOfDay = this.partsOfDay.find(part => part.timeFrom <= session.timeStart && part.timeTo >= session.timeStart);
+ const newGroup = {
+ indexKey: partOfDay.indexKey,
+ partOfDay: partOfDay.name,
+ sessions: [session]
+ };
+ const newItem = { date: session.date, groups: [newGroup]};
+
+ const sIndex = this.schedule.findIndex(item => item.date === session.date);
+ if (sIndex < 0) {
+ this.schedule.push(newItem);
+ } else {
+ const gIndex = this.schedule[sIndex].groups.findIndex(
+ group => group.partOfDay === partOfDay.name
+ );
+ if (gIndex < 0) {
+ this.schedule[sIndex].groups.push(newGroup);
+ } else {
+ this.schedule[sIndex].groups[gIndex].sessions.push(session);
+ }
+ }
+ });
+ this.schedule.sort((a, b) => {
+ if (a.date > b.date) { return 1; }
+ return -1;
+ });
+ this.schedule.forEach(item => {
+ item.groups.sort((a, b) => a.indexKey - b.indexKey);
+ item.groups.forEach(group => {
+ group.sessions.sort((a, b) => {
+ if (a.timeStart > b.timeStart) { return 1; }
+ return -1;
+ });
+ });
+ });
+ }
+
+ updateFilter() {
+ const filterOption = this.getFilterOption();
+ this.schedule.forEach(daily => {
+ daily.groups.forEach(group => {
+ group.sessions.forEach(session => {
+ session = this.dataProvider.filterSession(session, filterOption);
+ });
+ });
});
}
+ processBySegment() {
+ if (this.segment === 'one') {
+ this.chooseTrack();
+ } else if (this.segment === 'all') {
+ this.excludeTracks = [];
+ this.updateFilter();
+ } else if (this.segment === 'favorites') {
+ this.updateFilter();
+ }
+ }
+
+ async chooseTrack() {
+ const modal = await this.modalCtrl.create({
+ component: ScheduleTrackPage,
+ componentProps: { excludedTracks: this.excludeTracks }
+ });
+ await modal.present();
+
+ const { data } = await modal.onWillDismiss();
+ if (data) {
+ this.excludeTracks = data;
+ this.segment = 'user';
+ this.updateFilter();
+ } else {
+ this.segment = '';
+ }
+ }
+
async presentFilter() {
const modal = await this.modalCtrl.create({
component: ScheduleFilterPage,
@@ -60,18 +180,25 @@ export class SchedulePage implements OnInit {
const { data } = await modal.onWillDismiss();
if (data) {
this.excludeTracks = data;
- this.updateSchedule();
+ this.segment = 'user';
+ this.updateFilter();
}
}
- async addFavorite(slidingItem: HTMLIonItemSlidingElement, sessionData: any) {
- if (this.user.hasFavorite(sessionData.name)) {
+ goToSessionDetail(sessionData: any) {
+ // go to the session detail page
+ // and pass in the session data
+ this.router.navigateByUrl(`app/tabs/(schedule:session/${sessionData.id})`);
+ }
+
+async addFavorite(slidingItem: HTMLIonItemSlidingElement, sessionData: any) {
+ if (this.userProvider.hasFavorite(sessionData.name)) {
// woops, they already favorited it! What shall we do!?
// prompt them to remove it
this.removeFavorite(slidingItem, sessionData, 'Favorite already added');
} else {
// remember this session as a user favorite
- this.user.addFavorite(sessionData.name);
+ this.userProvider.addFavorite(sessionData.name);
// create an alert instance
const alert = await this.alertCtrl.create({
@@ -107,7 +234,7 @@ export class SchedulePage implements OnInit {
text: 'Remove',
handler: () => {
// they want to remove this session from their favorites
- this.user.removeFavorite(sessionData.name);
+ this.userProvider.removeFavorite(sessionData.name);
this.updateSchedule();
// close the sliding item and hide the option buttons
diff --git a/src/app/pages/session-detail/session-detail.html b/src/app/pages/session-detail/session-detail.html
index 34e3dafb6..2a77aef36 100644
--- a/src/app/pages/session-detail/session-detail.html
+++ b/src/app/pages/session-detail/session-detail.html
@@ -1,7 +1,7 @@
-
+
-
+
{{session?.name}}
diff --git a/src/app/pages/session-detail/session-detail.ts b/src/app/pages/session-detail/session-detail.ts
index 80b0c7daa..898f2bdee 100644
--- a/src/app/pages/session-detail/session-detail.ts
+++ b/src/app/pages/session-detail/session-detail.ts
@@ -1,8 +1,9 @@
import { Component } from '@angular/core';
-
-import { ConferenceData } from '../../providers/conference-data';
import { ActivatedRoute } from '@angular/router';
+
import { UserData } from '../../providers/user-data';
+import { SessionData } from '../../providers/session-data';
+import { Session, User } from '../../models';
@Component({
selector: 'page-session-detail',
@@ -10,52 +11,46 @@ import { UserData } from '../../providers/user-data';
templateUrl: 'session-detail.html'
})
export class SessionDetailPage {
- session: any;
+ session: Session;
+ user: User;
isFavorite = false;
- defaultHref = '';
+
constructor(
- private dataProvider: ConferenceData,
+ private sessionProvider: SessionData,
private userProvider: UserData,
private route: ActivatedRoute
) {}
+
+ ionViewWillEnter() {
+ const id = this.route.snapshot.paramMap.get('sessionId');
+ this.sessionProvider.getSession(id)
+ .then( data => {
+ data.id = id;
+ this.session = data;
+ this.userProvider.getUser().then(user => {
+ this.user = user;
+ this.user.favorites.forEach(favorite => {
+ if (favorite.id === this.session.id) { this.isFavorite = true; }
+ });
+ });
+ }
+ );
+ }
+
sessionClick(item: string) {
console.log('Clicked', item);
}
+
toggleFavorite() {
- if (this.userProvider.hasFavorite(this.session.name)) {
- this.userProvider.removeFavorite(this.session.name);
- this.isFavorite = false;
+ if (this.isFavorite) {
+ const index = this.user.favorites.findIndex(f => f.id === this.session.id);
+ if (index > -1) {
+ this.user.favorites.splice(index, 1);
+ }
} else {
- this.userProvider.addFavorite(this.session.name);
- this.isFavorite = true;
+ this.user.favorites.push({id: this.session.id, name: this.session.name });
}
- }
- ionViewWillEnter() {
- this.dataProvider.load().subscribe((data: any) => {
- if (
- data &&
- data.schedule &&
- data.schedule[0] &&
- data.schedule[0].groups
- ) {
- const sessionId = this.route.snapshot.paramMap.get('sessionId');
- for (const group of data.schedule[0].groups) {
- if (group && group.sessions) {
- for (const session of group.sessions) {
- if (session && session.id === sessionId) {
- this.session = session;
- this.isFavorite = this.userProvider.hasFavorite(
- this.session.name
- );
- break;
- }
- }
- }
- }
- }
- });
- }
- ionViewDidEnter() {
- this.defaultHref = `/app/tabs/schedule`;
+ this.userProvider.updateUser(this.user);
+ this.isFavorite = !this.isFavorite;
}
}
diff --git a/src/app/pages/signup/signup.html b/src/app/pages/signup/signup.html
index 38c5bed9c..fd0f2fdeb 100644
--- a/src/app/pages/signup/signup.html
+++ b/src/app/pages/signup/signup.html
@@ -1,5 +1,5 @@
-
+
@@ -26,9 +26,21 @@
+
+ Email
+
+
+
+
+
+ Email is required
+
+
+
Password
-
+
@@ -36,6 +48,22 @@
Password is required
+
+
+ Confirm Password
+
+
+
+
+
+ Confirm Password is required
+
+
+
+
+ Password does not match with Confirm Password
+
+
diff --git a/src/app/pages/signup/signup.ts b/src/app/pages/signup/signup.ts
index 1bc639081..627623bc4 100644
--- a/src/app/pages/signup/signup.ts
+++ b/src/app/pages/signup/signup.ts
@@ -1,33 +1,65 @@
-import { Component, ViewEncapsulation } from '@angular/core';
+import { Component, ViewEncapsulation, OnInit } from '@angular/core';
import { NgForm } from '@angular/forms';
import { Router } from '@angular/router';
import { UserData } from '../../providers/user-data';
-
-import { UserOptions } from '../../interfaces/user-options';
-
-
+import { ConferenceData } from '../../providers/conference-data';
+import { User, Track } from '../../models';
@Component({
selector: 'page-signup',
templateUrl: 'signup.html',
styleUrls: ['./signup.scss'],
+ encapsulation: ViewEncapsulation.None
})
-export class SignupPage {
- signup: UserOptions = { username: '', password: '' };
+export class SignupPage implements OnInit {
+ users: User[];
+ signup: User = {
+ username: '', password: '', email: '', favorites: [], trackFilter: []
+ };
+ tracks: Track[];
+ confirmPassword = '';
submitted = false;
- constructor(
- public router: Router,
- public userData: UserData
- ) {}
+ constructor(public router: Router,
+ public userProvider: UserData,
+ public dataProvider: ConferenceData) {}
+
+ ngOnInit() {
+ this.userProvider.getUsers().subscribe(
+ (data: User[]) => { this.users = data; }
+ );
+ this.dataProvider.getTracks().subscribe(
+ (tracks: Track[]) => { this.tracks = tracks; }
+ );
+ }
onSignup(form: NgForm) {
this.submitted = true;
-
- if (form.valid) {
- this.userData.signup(this.signup.username);
- this.router.navigateByUrl('/app/tabs/schedule');
+ if (form.valid && this.signup.password === this.confirmPassword) {
+ if (this.isNameUsed(this.signup.username)) {
+ alert('Name is already taken.');
+ } else if (this.isEmailUsed(this.signup.email)) {
+ alert('Email is already taken.');
+ } else {
+ this.setTrackFilter();
+ this.userProvider.signup(this.signup);
+ this.router.navigateByUrl('/app/tabs/(speakers:speakers)');
+ }
}
}
+
+ setTrackFilter() {
+ this.tracks.forEach(track => {
+ this.signup.trackFilter.push({ name: track.name, isChecked: true });
+ });
+ }
+
+ isNameUsed(name) {
+ return this.users.find(ur => ur.username.toLowerCase() === name.toLowerCase());
+ }
+
+ isEmailUsed(email) {
+ return this.users.find(ur => ur.email.toLowerCase() === email.toLowerCase());
+ }
}
diff --git a/src/app/pages/speaker-detail/speaker-detail.html b/src/app/pages/speaker-detail/speaker-detail.html
index d50199ab3..1eb361bd7 100644
--- a/src/app/pages/speaker-detail/speaker-detail.html
+++ b/src/app/pages/speaker-detail/speaker-detail.html
@@ -1,7 +1,7 @@
-
+
-
+
{{speaker?.name}}
@@ -12,16 +12,20 @@
-
+
-
+
-
+
{{speaker?.about}}
+ {{speaker?.location}}
+ {{speaker?.email}}
+ {{speaker?.phone}}
+ Sessions involved: {{speaker?.sessions.length}}
diff --git a/src/app/pages/speaker-detail/speaker-detail.ts b/src/app/pages/speaker-detail/speaker-detail.ts
index fb148a176..29729b5d5 100644
--- a/src/app/pages/speaker-detail/speaker-detail.ts
+++ b/src/app/pages/speaker-detail/speaker-detail.ts
@@ -1,32 +1,55 @@
import { Component, ViewEncapsulation } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
-import { ConferenceData } from '../../providers/conference-data';
+import { InAppBrowser } from '@ionic-native/in-app-browser/ngx';
+
+import { SpeakerData } from '../../providers/speaker-data';
+import { Speaker } from '../../models';
@Component({
selector: 'page-speaker-detail',
templateUrl: 'speaker-detail.html',
styleUrls: ['./speaker-detail.scss'],
+ encapsulation: ViewEncapsulation.None
})
export class SpeakerDetailPage {
- speaker: any;
+ speaker: Speaker;
constructor(
- private dataProvider: ConferenceData,
+ private speakerProvider: SpeakerData,
private router: Router,
- private route: ActivatedRoute
+ private route: ActivatedRoute,
+ public inAppBrowser: InAppBrowser
) {}
ionViewWillEnter() {
- this.dataProvider.load().subscribe((data: any) => {
- const speakerId = this.route.snapshot.paramMap.get('speakerId');
- if (data && data.speakers) {
- for (const speaker of data.speakers) {
- if (speaker && speaker.id === speakerId) {
- this.speaker = speaker;
- break;
- }
- }
- }
- });
+ const id = this.route.snapshot.paramMap.get('speakerId');
+ this.speakerProvider.getSpeaker(id)
+ .then( data => { this.speaker = data; }
+ );
+ }
+
+ goToSessionDetail(session: any) {
+ this.router.navigateByUrl(`app/tabs/(schedule:session/${session.id})`);
+ }
+
+ goToSpeakerTwitter() {
+ this.inAppBrowser.create(
+ `https://twitter.com/${this.speaker.twitter}`,
+ '_blank'
+ );
+ }
+
+ goToSpeakergithub() {
+ this.inAppBrowser.create(
+ `https://github.com/${this.speaker.github}`,
+ '_blank'
+ );
+ }
+
+ goToSpeakerInstagram() {
+ this.inAppBrowser.create(
+ `https://instagram.com/${this.speaker.instagram}`,
+ '_blank'
+ );
}
}
diff --git a/src/app/pages/speaker-list/speaker-list.html b/src/app/pages/speaker-list/speaker-list.html
index 05cd974a4..89bdf002e 100644
--- a/src/app/pages/speaker-list/speaker-list.html
+++ b/src/app/pages/speaker-list/speaker-list.html
@@ -1,5 +1,5 @@
-
+
@@ -14,7 +14,7 @@
-
+
@@ -24,11 +24,11 @@
-
+
{{session.name}}
-
+
About {{speaker.name}}
diff --git a/src/app/pages/speaker-list/speaker-list.ts b/src/app/pages/speaker-list/speaker-list.ts
index e50e5debd..ae6b943ec 100644
--- a/src/app/pages/speaker-list/speaker-list.ts
+++ b/src/app/pages/speaker-list/speaker-list.ts
@@ -1,32 +1,35 @@
import { Component, ViewEncapsulation } from '@angular/core';
import { Router } from '@angular/router';
import { InAppBrowser } from '@ionic-native/in-app-browser/ngx';
-import { ActionSheetController } from '@ionic/angular';
+import { ActionSheetController, AlertController } from '@ionic/angular';
-import { ConferenceData } from '../../providers/conference-data';
+import { Speaker } from '../../models';
+import { SpeakerData } from '../../providers/speaker-data';
@Component({
selector: 'page-speaker-list',
templateUrl: 'speaker-list.html',
styleUrls: ['./speaker-list.scss'],
+ encapsulation: ViewEncapsulation.None
})
export class SpeakerListPage {
- speakers: any[] = [];
+ speakers: Speaker[];
constructor(
public actionSheetCtrl: ActionSheetController,
- public confData: ConferenceData,
+ public alertCtrl: AlertController,
+ private speakerProvider: SpeakerData,
public inAppBrowser: InAppBrowser,
public router: Router
) {}
ionViewDidEnter() {
- this.confData.getSpeakers().subscribe((speakers: any[]) => {
- this.speakers = speakers;
- });
+ this.speakerProvider.getSpeakers().subscribe(
+ speakers => { this.speakers = speakers; }
+ );
}
- goToSpeakerTwitter(speaker: any) {
+ goToSpeakerTwitter(speaker: Speaker) {
this.inAppBrowser.create(
`https://twitter.com/${speaker.twitter}`,
'_blank'
diff --git a/src/app/pages/support/support.html b/src/app/pages/support/support.html
index 278e9856f..44e69e5e2 100644
--- a/src/app/pages/support/support.html
+++ b/src/app/pages/support/support.html
@@ -1,14 +1,13 @@
-
+
Support
-
-
+
@@ -26,9 +25,12 @@
Support message is required
-
Submit
+
+ You need to login first!
+
+
diff --git a/src/app/pages/support/support.scss b/src/app/pages/support/support.scss
index e6e125144..ca558fcfd 100644
--- a/src/app/pages/support/support.scss
+++ b/src/app/pages/support/support.scss
@@ -1,13 +1,19 @@
-.support-logo {
- padding: 20px 0;
- min-height: 200px;
- text-align: center;
-}
+page-support {
+ .support-logo {
+ padding: 20px 0;
+ min-height: 200px;
+ text-align: center;
+ }
-.support-logo img {
- max-width: 150px;
-}
+ .support-logo img {
+ max-width: 150px;
+ }
-.list {
- margin-bottom: 0;
-}
+ .list {
+ margin-bottom: 0;
+ }
+
+ .support-msg h4 {
+ margin: 200px 80px;
+ }
+}
\ No newline at end of file
diff --git a/src/app/pages/support/support.ts b/src/app/pages/support/support.ts
index 4d55f8e5d..d32db0846 100644
--- a/src/app/pages/support/support.ts
+++ b/src/app/pages/support/support.ts
@@ -1,38 +1,41 @@
import { Component, ViewEncapsulation } from '@angular/core';
import { NgForm } from '@angular/forms';
+import { Storage } from '@ionic/storage';
import { AlertController, ToastController } from '@ionic/angular';
-
+import { UserData } from '../../providers/user-data';
+import { SupportData } from '../../providers/support-data';
@Component({
selector: 'page-support',
templateUrl: 'support.html',
styleUrls: ['./support.scss'],
+ encapsulation: ViewEncapsulation.None
})
export class SupportPage {
+ isLoggedIn = false;
submitted = false;
supportMessage: string;
- constructor(
- public alertCtrl: AlertController,
- public toastCtrl: ToastController
- ) { }
+ constructor(public alertCtrl: AlertController,
+ public toastCtrl: ToastController,
+ private userData: UserData,
+ private supportData: SupportData,
+ public storage: Storage) {
+ }
async ionViewDidEnter() {
- const toast = await this.toastCtrl.create({
- message: 'This does not actually send a support request.',
- duration: 3000
- });
- await toast.present();
+ this.userData.isLoggedIn()
+ .then(res => this.isLoggedIn = res);
}
async submit(form: NgForm) {
this.submitted = true;
if (form.valid) {
+ this.supportData.addSupport(this.supportMessage);
this.supportMessage = '';
this.submitted = false;
-
const toast = await this.toastCtrl.create({
message: 'Your support request has been sent.',
duration: 3000
@@ -40,26 +43,4 @@ export class SupportPage {
await toast.present();
}
}
-
- // If the user enters text in the support question and then navigates
- // without submitting first, ask if they meant to leave the page
- // async ionViewCanLeave(): Promise {
- // // If the support message is empty we should just navigate
- // if (!this.supportMessage || this.supportMessage.trim().length === 0) {
- // return true;
- // }
-
- // return new Promise((resolve: any, reject: any) => {
- // const alert = await this.alertCtrl.create({
- // title: 'Leave this page?',
- // message: 'Are you sure you want to leave this page? Your support message will not be submitted.',
- // buttons: [
- // { text: 'Stay', handler: reject },
- // { text: 'Leave', role: 'cancel', handler: resolve }
- // ]
- // });
-
- // await alert.present();
- // });
- // }
}
diff --git a/src/app/pages/tabs-page/tabs-page-routing.module.ts b/src/app/pages/tabs-page/tabs-page-routing.module.ts
index fa31409c5..6ebfeac42 100644
--- a/src/app/pages/tabs-page/tabs-page-routing.module.ts
+++ b/src/app/pages/tabs-page/tabs-page-routing.module.ts
@@ -1,7 +1,14 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
+
import { TabsPage } from './tabs-page';
+
+import { AboutPage } from '../about/about';
+import { MapPage } from '../map/map';
import { SchedulePage } from '../schedule/schedule';
+import { SessionDetailPage } from '../session-detail/session-detail';
+import { SpeakerDetailPage } from '../speaker-detail/speaker-detail';
+import { SpeakerListPage } from '../speaker-list/speaker-list';
const routes: Routes = [
@@ -9,58 +16,44 @@ const routes: Routes = [
path: 'tabs',
component: TabsPage,
children: [
+ // tab one
{
path: 'schedule',
- children: [
- {
- path: '',
- component: SchedulePage,
- },
- {
- path: 'session/:sessionId',
- loadChildren: '../session-detail/session-detail.module#SessionDetailModule'
- }
- ]
+ component: SchedulePage,
+ outlet: 'schedule'
+ },
+ {
+ path: 'session/:sessionId',
+ component: SessionDetailPage,
+ outlet: 'schedule'
},
+ // tab two
{
path: 'speakers',
- children: [
- {
- path: '',
- loadChildren: '../speaker-list/speaker-list.module#SpeakerListModule'
- },
- {
- path: 'session/:sessionId',
- loadChildren: '../session-detail/session-detail.module#SessionDetailModule'
- },
- {
- path: 'speaker-details/:speakerId',
- loadChildren: '../speaker-detail/speaker-detail.module#SpeakerDetailModule'
- }
- ]
+ component: SpeakerListPage,
+ outlet: 'speakers'
},
{
- path: 'map',
- children: [
- {
- path: '',
- loadChildren: '../map/map.module#MapModule'
- }
- ]
+ path: 'session/:sessionId',
+ component: SessionDetailPage,
+ outlet: 'speakers'
},
{
- path: 'about',
- children: [
- {
- path: '',
- loadChildren: '../about/about.module#AboutModule'
- }
- ]
+ path: 'speaker-details/:speakerId',
+ component: SpeakerDetailPage,
+ outlet: 'speakers'
},
+ // tab three
{
- path: '',
- redirectTo: '/app/tabs/schedule',
- pathMatch: 'full'
+ path: 'map',
+ component: MapPage,
+ outlet: 'map'
+ },
+ // tab four
+ {
+ path: 'about',
+ component: AboutPage,
+ outlet: 'about'
}
]
}
@@ -71,4 +64,3 @@ const routes: Routes = [
exports: [RouterModule]
})
export class TabsPageRoutingModule { }
-
diff --git a/src/app/pages/tabs-page/tabs-page.html b/src/app/pages/tabs-page/tabs-page.html
index d7da8656e..70e29fbb2 100644
--- a/src/app/pages/tabs-page/tabs-page.html
+++ b/src/app/pages/tabs-page/tabs-page.html
@@ -1,25 +1,41 @@
-
+
Schedule
-
+
Speakers
-
+
Map
-
+
About
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/pages/tutorial/tutorial.ts b/src/app/pages/tutorial/tutorial.ts
index 79fc63ca3..9e6751bf7 100644
--- a/src/app/pages/tutorial/tutorial.ts
+++ b/src/app/pages/tutorial/tutorial.ts
@@ -1,7 +1,7 @@
import { Component, ViewChild, ViewEncapsulation } from '@angular/core';
import { Router } from '@angular/router';
-import { MenuController, IonSlides } from '@ionic/angular';
+import { MenuController, Slides } from '@ionic/angular';
import { Storage } from '@ionic/storage';
@@ -9,11 +9,12 @@ import { Storage } from '@ionic/storage';
selector: 'page-tutorial',
templateUrl: 'tutorial.html',
styleUrls: ['./tutorial.scss'],
+ encapsulation: ViewEncapsulation.None
})
export class TutorialPage {
showSkip = true;
- @ViewChild('slides') slides: IonSlides;
+ @ViewChild('slides') slides: Slides;
constructor(
public menu: MenuController,
@@ -23,7 +24,7 @@ export class TutorialPage {
startApp() {
this.router
- .navigateByUrl('/app/tabs/schedule')
+ .navigateByUrl('/app/tabs/(speakers:speakers)')
.then(() => this.storage.set('ion_did_tutorial', 'true'));
}
diff --git a/src/app/pages/upload-image/upload-image.component.html b/src/app/pages/upload-image/upload-image.component.html
new file mode 100644
index 000000000..38bbdb835
--- /dev/null
+++ b/src/app/pages/upload-image/upload-image.component.html
@@ -0,0 +1,26 @@
+Upload User's Avatar
+
+
+
Image Drop Zone
+
Drag and Drop a File
+
+
+
+
+
+
+
+
+ {{ pct | number }}%
+
+
+
+ {{ snap.bytesTransferred | fileSize }} of {{ snap.totalBytes | fileSize }}
+
diff --git a/src/app/pages/upload-image/upload-image.component.scss b/src/app/pages/upload-image/upload-image.component.scss
new file mode 100644
index 000000000..3c4bf3bd1
--- /dev/null
+++ b/src/app/pages/upload-image/upload-image.component.scss
@@ -0,0 +1,43 @@
+.title {
+ font-size: 1.4em;
+ margin-top: 40px;
+}
+
+.dropzone {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-direction: column;
+ font-weight: 200;
+ height: 300px;
+ border: 2px dashed #f16624;
+ border-radius: 5px;
+ background: white;
+ margin: 40px 0;
+
+ &.hovering {
+ border: 2px solid #f16624;
+ color: #dadada !important;
+ }
+
+ .file-label {
+ font-size: 1em;
+ }
+}
+
+progress::-webkit-progress-value {
+ transition: width 0.1s ease;
+}
+
+img {
+ width: 250px
+}
+
+button {
+ padding: 10px;
+ width: 150px;
+ font-size: 1em;
+ font-weight: 400;
+ background-color: #f16624;
+ color: white
+}
diff --git a/src/app/pages/upload-image/upload-image.component.ts b/src/app/pages/upload-image/upload-image.component.ts
new file mode 100644
index 000000000..c8005536c
--- /dev/null
+++ b/src/app/pages/upload-image/upload-image.component.ts
@@ -0,0 +1,72 @@
+import { Component, Output, Input, EventEmitter } from '@angular/core';
+import { AngularFireStorage, AngularFireUploadTask } from 'angularfire2/storage';
+import { AngularFirestore } from 'angularfire2/firestore';
+import { Observable } from 'rxjs';
+import { tap } from 'rxjs/operators';
+
+@Component({
+ selector: 'upload-image',
+ templateUrl: './upload-image.component.html',
+ styleUrls: ['./upload-image.component.scss']
+})
+export class UploadImageComponent {
+
+ @Input() username: string;
+ @Output() exit = new EventEmitter();
+ @Output() updateUser = new EventEmitter();
+
+ // Main task
+ task: AngularFireUploadTask;
+
+ // State for dropzone CSS toggling
+ isHovering: boolean;
+
+ // Progress monitoring
+ percentage: Observable;
+
+ snapshot: Observable;
+
+ // constructor(private db: AngularFirestore) { }
+ constructor(private storage: AngularFireStorage, private db: AngularFirestore) { }
+
+ toggleHover(event: boolean) {
+ this.isHovering = event;
+ }
+
+ startUpload(event: FileList) {
+ // The File object
+ const file = event.item(0);
+
+ // Client-side validation example
+ if (!file || file.type.split('/')[0] !== 'image') {
+ console.error('unsupported file type :( ');
+ return;
+ }
+
+ // The storage path
+ const path = `avatars/${new Date().getTime()}_${file.name}`;
+
+ // Totally optional metadata
+ const customMetadata = { app: 'My AngularFire-powered PWA!' };
+
+ // const ref = this.storage.ref(path);
+ // this.task = ref.put(file, { customMetadata });
+ // The main task
+ this.task = this.storage.upload(path, file, { customMetadata });
+
+ // Progress monitoring
+ this.percentage = this.task.percentageChanges();
+ this.snapshot = this.task.snapshotChanges().pipe(
+ tap(snap => {
+ if (snap.bytesTransferred === snap.totalBytes) {
+ this.updateUser.emit(path);
+ }
+ })
+ );
+ }
+
+ // Determines if the upload task is active
+ isActive(snapshot) {
+ return snapshot.state === 'running' && snapshot.bytesTransferred < snapshot.totalBytes;
+ }
+}
diff --git a/src/app/pipe/file-size.pipe.ts b/src/app/pipe/file-size.pipe.ts
new file mode 100644
index 000000000..3bc76a88d
--- /dev/null
+++ b/src/app/pipe/file-size.pipe.ts
@@ -0,0 +1,25 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+const FILE_SIZE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
+const FILE_SIZE_UNITS_LONG = ['Bytes', 'Kilobytes', 'Megabytes', 'Gigabytes', 'Pettabytes', 'Exabytes', 'Zettabytes', 'Yottabytes'];
+
+@Pipe({
+ name: 'fileSize'
+})
+export class FileSizePipe implements PipeTransform {
+
+ transform(sizeInBytes: number, longForm: boolean): string {
+ const units = longForm
+ ? FILE_SIZE_UNITS_LONG
+ : FILE_SIZE_UNITS;
+
+ let power = Math.round(Math.log(sizeInBytes) / Math.log(1024));
+ power = Math.min(power, units.length - 1);
+
+ const size = sizeInBytes / Math.pow(1024, power); // size in new units
+ const formattedSize = Math.round(size * 100) / 100; // keep up to 2 decimals
+ const unit = units[power];
+
+ return size ? `${formattedSize} ${unit}` : '0';
+ }
+}
diff --git a/src/app/providers/conference-data.ts b/src/app/providers/conference-data.ts
index 9891292d4..82cc9e51f 100644
--- a/src/app/providers/conference-data.ts
+++ b/src/app/providers/conference-data.ts
@@ -1,117 +1,80 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
-import { of } from 'rxjs';
+import { AngularFirestore,
+ AngularFirestoreCollection,
+ AngularFirestoreDocument } from 'angularfire2/firestore';
+import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
-
import { UserData } from './user-data';
+import { Track, Session, PartOfDay } from '../models';
@Injectable({
providedIn: 'root'
})
export class ConferenceData {
- data: any;
+ tracksCollection: AngularFirestoreCollection