// import {map, startWith, filter, scan} from 'rxjs/operators'; import { Component, OnInit, OnDestroy, Pipe, PipeTransform, Input, HostListener, Injectable } from '@angular/core'; import { FormControl } from '@angular/forms'; import { Observable, of } from 'rxjs'; import * as firebase from 'firebase'; import { MatDialog, MatSnackBar } from '@angular/material'; import { DialogAddSchoolComponent } from '../dialog-add-school/dialog-add-school.component'; import { PersistingDataService } from '../persisting-data/persisting-data.service'; import { Router } from '@angular/router'; import { AngularFirestoreCollection, AngularFirestore, AngularFirestoreDocument } from '@angular/fire/firestore'; import { AuthService } from '../core/auth.service'; import { CommonService } from '../SvcCommon/common.service'; import { PaymentService } from '../SvcPayment/payment.service'; import { DialogNewUserComponent } from '../dialog-new-user/dialog-new-user.component'; import { User, Action, Assignment, SvcInterfacesService } from '../SvcInterfaces/svc-interfaces.service'; import { DialogAdminPanelComponent } from '../dialog-admin-panel/dialog-admin-panel.component'; import { DialogEditSchoolComponent } from '../dialog-edit-school/dialog-edit-school.component'; import { debounceTime, map, tap, take } from 'rxjs/operators'; import { SlicePipe } from '@angular/common'; import { HttpClient } from '@angular/common/http'; import { DialogOpenSubmissionsComponent } from '../dialog-open-submissions/dialog-open-submissions.component'; import { DialogTutorialComponent } from '../dialog-tutorial/dialog-tutorial.component'; // import 'rxjs/add/operator/toPromise'; // declare const google: any; // export class School { // constructor(public name: string, public city: string, public state: string, public type: string) { } // } interface School { name: string, schoolNameDisplay: string, city: string, state: string, type: string } interface Class { className: string; classSubmittedDate: number; classUID: string; submittersName: string; submittersUID: string; teacher: string; school: string; } @Pipe({name: 'orderBy', pure: false}) export class OrderBy implements PipeTransform { static _orderByComparator(a:any, b:any):number{ if((isNaN(parseFloat(a)) || !isFinite(a)) || (isNaN(parseFloat(b)) || !isFinite(b))){ //Isn't a number so lowercase the string to properly compare // if(a.toLowerCase() < b.toLowerCase()) return -1; // if(a.toLowerCase() > b.toLowerCase()) return 1; } else{ //Parse strings as numbers to compare properly if(parseFloat(a) < parseFloat(b)) return -1; if(parseFloat(a) > parseFloat(b)) return 1; } return 0; //equal each other } transform(input:any, [config = '+']): any{ if(!Array.isArray(input)) return input; if(!Array.isArray(config) || (Array.isArray(config) && config.length == 1)){ var propertyToCheck:string = !Array.isArray(config) ? config : config[0]; var desc = propertyToCheck.substr(0, 1) == '-'; //Basic array if(!propertyToCheck || propertyToCheck == '-' || propertyToCheck == '+'){ return !desc ? input.sort() : input.sort().reverse(); } else { var property:string = propertyToCheck.substr(0, 1) == '+' || propertyToCheck.substr(0, 1) == '-' ? propertyToCheck.substr(1) : propertyToCheck; return input.sort(function(a:any,b:any){ return !desc ? OrderBy._orderByComparator(a[property], b[property]) : -OrderBy._orderByComparator(a[property], b[property]); }); } } else { //Loop over property of the array in order and sort return input.sort(function(a:any,b:any){ for(var i:number = 0; i < config.length; i++){ var desc = config[i].substr(0, 1) == '-'; var property = config[i].substr(0, 1) == '+' || config[i].substr(0, 1) == '-' ? config[i].substr(1) : config[i]; var comparison = !desc ? OrderBy._orderByComparator(a[property], b[property]) : -OrderBy._orderByComparator(a[property], b[property]); //Don't return 0 yet in case of needing to sort by next property if(comparison != 0) return comparison; } return 0; //equal each other }); } } } @Injectable({ providedIn: 'root' }) export class MyService { constructor(private afs: AngularFirestore) { } private schools = []; fetchSchools() { console.log('---------------'); if(this.schools && this.schools.length) { console.log('Returning first data'); return of(this.schools); } else { console.log('Returning else data'); return this.afs.collection('Schools', ref => { return ref.orderBy('schoolNameDisplay') }).valueChanges().pipe( tap((schools) => this.schools = schools) ).pipe( take(1) ) } } } @Component({ selector: 'app-page-home', templateUrl: './page-home.component.html', styleUrls: ['./page-home.component.css'] }) export class PageHomeComponent implements OnInit { pinnedClassesCollection: AngularFirestoreCollection; pinnedClasses: Observable; moderatingClassesCollection: AngularFirestoreCollection; moderatingClasses: Observable; schoolsCollection: AngularFirestoreCollection; schools: Observable; // schools; schoolsArr: School[]; assignmentsCollection: AngularFirestoreCollection; assignments: Observable; createdSchoolsCollection: AngularFirestoreCollection; createdSchools: Observable; schoolsDocs: AngularFirestoreDocument; schoolCtrl: FormControl; filteredSchools: Observable searchedSchool; user: User; userExists; currentlySelectedPinnedClassUID; currentlySelectedPinnedClass; currentlySelectedPinnedTeacher; currentlySelectedPinnedSchool; pinnedSize; moderatingSize; pinsFetched; //'fetched' or 'stopped' modsFetched; //'fetched' or 'stopped' windowWidth; windowHeight; toolbarElevated; usersPosition; usersPositionLoopCounter = 0; locationFetched = false; usersMetaHot; @HostListener('window:resize', ['$event']) onresize(event) { this.windowWidth = window.innerWidth; this.windowHeight = window.innerHeight; console.log(`Window resolution: ${this.windowWidth} x ${this.windowHeight}`); } constructor(private dialog: MatDialog, private persistingData: PersistingDataService, private router: Router, private afs: AngularFirestore, private auth: AuthService, private common: CommonService, private payment: PaymentService, private myService: MyService, private snackbar: MatSnackBar, private httpClient: HttpClient) { } schoolsObs = of([]); searchModel = ''; ngOnInit() { this.persistingData.changeViewWithContainer(true); this.persistingData.toolbarElevated.subscribe(message => this.toolbarElevated = message); // this.router.events.subscribe((val) => { // console.log('ngOnInit router changed'); // this.persistingData.changeToggleDisplayGradient(false) // }) this.windowWidth = window.innerWidth; this.windowHeight = window.innerHeight; // this.fetchSchools(); this.filterSchools(); this.persistingData.loggedInUser.subscribe(user => { if(user){ this.user = user; this.userExists = true; this.fetchPinnedClasses(); this.fetchModeratingClasses(); this.fetchCreatedSchools(); this.fetchLocation(); } else { this.userExists = false; this.pinsFetched = 'stopped'; this.modsFetched = 'stopped'; } console.log('persistingData loggedinUser subscription:'); if(user){ console.log('user: '); console.log(user); } this.user = user; }); } filterSchools(){ this.schoolsObs = this.myService.fetchSchools().pipe( debounceTime(300), map((data) => this.performFilter(data)) ); } performFilter(schoolsObs){ console.log('schoolsObs:'); console.log(schoolsObs); console.log('this.schools:'); console.log(this.schools); return schoolsObs.filter((x) => { return x.name.toLowerCase().startsWith(this.searchModel.trim().toLowerCase()); }).slice(0,4); } ngOnDestroy(){ console.log('Running onDestroy()'); this.persistingData.changeToggleDisplayGradient(false); this.persistingData.changeToolbarElevated(true); } updateUsersMeta(ipResponse){ console.log('Running updateUsersMeta()'); //Update user's position property in firestore if current city different than stored city if(this.user.usersMeta){ console.log('User has existing usersMeta obj'); if(this.user.usersMeta.city != ipResponse.city){ console.log('Updating users usersMeta prop'); this.afs.collection('Users').doc(this.user.uid).ref.update({ usersMeta: ipResponse }).then(() => { console.log('Finished updating user with usersMeta prop'); }); } else { console.log('Users city is the same as before. No need to update.'); } } else { console.log('User does not yet have usersMeta obj'); this.afs.collection('Users').doc(this.user.uid).ref.update({ usersMeta: ipResponse }).then(() => { console.log('Finished updating user with usersMeta prop'); }); } } // fetchSchools(){ // console.log('Running fetchSchools'); // this.schoolsCollection = this.afs.collection('Schools', ref => { // console.log('In afs.collection'); // // return ref.orderBy('name').limit(4); // return ref.orderBy('name'); // }); // this.schools = this.schoolsCollection.valueChanges(); // console.log('schoolsCollection:'); // console.log(this.schoolsCollection); // console.log('Retreived schools:'); // console.log(this.schools); // } fetchPinnedClasses() { console.log('Loading Pinned classes now...'); //Get reference to current user's Users/user.uid/Pinned collection let pathPinned = 'Users/' + this.user.uid + '/Pinned'; this.pinnedClassesCollection = this.afs.collection(pathPinned, ref => { ref.get().then(coll => { this.pinnedSize = coll.size; this.pinsFetched = 'fetched'; console.log(`pinnedSize: ${this.pinnedSize}`); }); return ref.orderBy('className'); }); this.pinnedClasses = this.pinnedClassesCollection.valueChanges(); } moderatingClassesSnap: any; fetchModeratingClasses() { console.log('Loading Moderating classes now...'); //Get reference to current user's Users/user.uid/Pinned collection let pathModerating = 'Users/' + this.user.uid + '/Moderating'; this.moderatingClassesCollection = this.afs.collection(pathModerating, ref => { ref.get().then(coll => { this.moderatingSize = coll.size; this.modsFetched = 'fetched'; console.log(`moderatingSize: ${this.moderatingSize}`); }); return ref.orderBy('class'); }); this.moderatingClasses = this.moderatingClassesCollection.valueChanges(); // this.moderatingClassesSnap = this.moderatingClassesCollection.snapshotChanges().pipe(map(changes => { // return changes.map(a => { // const data = a.payload.doc.data(); // console.log('moderatingClassesSnap data:'); // console.log(data); // return data; // }); // })).subscribe(); } fetchAssignments(pinnedClass, i){ console.log('Fetching assignment of pinnedClass instance:'); console.log(pinnedClass); let path = `Schools/${pinnedClass.school}/Teachers/${pinnedClass.teacher}/Classes/${pinnedClass.className}/Assignments`; console.log(path); console.log('index: ' + i); this.assignmentsCollection = this.afs.collection(path, ref => { return ref.orderBy('weekDue'); }); this.assignments = this.assignmentsCollection.valueChanges(); } fetchCreatedSchools(){ console.log('Loading Created classes now...'); //Get reference to current user's Users/user.uid/Pinned collection let pathCreated = 'Users/' + this.user.uid + '/Created Schools'; this.createdSchoolsCollection = this.afs.collection(pathCreated); this.createdSchools = this.createdSchoolsCollection.valueChanges(); } fetchLocation(){ console.log('Running fetchLocation()'); //If user is signed in and their city/metadata is unknown, check their location if(this.locationFetched == false){ console.log('LocationFetch has not yet been attempted for page'); this.locationFetched = true; //Run only if users city is unknown if(!this.user.usersMeta){ console.log('Users usersMeta has no value. Fetching ip...'); this.common.getLocation().subscribe(locationData => { console.log('Returning getLocation() locationData'); console.log(locationData); this.updateUsersMeta(locationData); }) } } } searchButton(input){ if(input.length < 4){ console.log('Input length is too short: ' + input.length); this.snackbar.open('Please enter more characters', 'Ok', {duration: 2000}); return null; } //Save away search this.searchedSchool = input; console.log('Searching for school: ' + this.searchedSchool); //Persist school search this.persistingData.changeSchool(this.searchedSchool); //Navigate to Browse Teachers page let navPath = this.searchedSchool; this.router.navigateByUrl(navPath); } openDialogAddSchool() { console.log('Opening dialog Add School'); this.dialog.open(DialogAddSchoolComponent, {width: '500px'}); } navigateToClass(school, teacher, className) { console.group('navigateToClass()') console.log('Navigating to class: '); console.log('School: ' + school); console.log('Teacher: ' + teacher); console.log('className: ' + className); console.groupEnd() //Persist data this.persistingData.changeSchool(school); this.persistingData.changeTeacher(teacher); this.persistingData.changeClass(className); let pathToNav = school + '/' + teacher; this.router.navigateByUrl(pathToNav); //Move scroll back up to the top of the page (smoothly) window.scrollTo({ top: 0, behavior: 'smooth' }); } unpinClass(schoolName, teacherName, className, classUID){ console.log('Unpinning class...'); let classSnap = { name: className } this.common.removeClassFromUsersPinnedCollSimple(this.user, schoolName, teacherName, classUID, className); this.common.removeUserFromClassPinners(this.user.uid, schoolName, teacherName, className); this.common.bumpTeachersPinnedCount(schoolName, teacherName, classSnap, -1); } asyncTest(){ var p1 = new Promise((resolve, reject) => { setTimeout(() => { console.log('In p1'); resolve('p1 resolved'); }, 600); }); var p2 = new Promise((resolve, reject) => { setTimeout(() => { console.log('In p2'); resolve('p2 resolved'); }, 200); }); var p3 = new Promise((resolve, reject) => { setTimeout(() => { console.log('In p3'); resolve('p3 resolved'); }, 300); }); Promise.all([p1, p2, p3]).then(() => { console.log('Running PromiseAll then'); }) } updateCounts(request, response, counts) { let schoolsSize; let usersSize; console.log('Running updateCounts'); //Get count of schools var p1 = new Promise((resolve, reject) => { console.log('in p1'); this.afs.collection('Schools').ref.get().then(snap => { console.log('in p1 then'); schoolsSize = snap.size; console.log('schoolsSize: ' + schoolsSize); resolve(schoolsSize); // return counts.schoolsCount = schoolsSize; }).catch(error => { console.log(error); response.status(500).send(error); }); }); //Get count of users var p2 = new Promise((resolve, reject) => { console.log('in p2'); this.afs.collection('Users').ref.get().then(snap => { console.log('in p2 then'); usersSize = snap.size; console.log('usersSize: ' + usersSize); resolve(usersSize); // counts.usersCount = usersSize; }).catch(error => { console.log(error); response.status(500).send(error); }); }); Promise.all([p1, p2]).then(values => { console.log('in Promise.all then'); console.log('Promise.all values: ' + values); // console.log('Promise.all count: ' + counts); console.log('schoolsSize: ' + schoolsSize); console.log('usersSize: ' + usersSize); // response.send(values); }); } showNewUserDialog(){ console.log('Running showNewUserDialog'); this.dialog.open(DialogNewUserComponent, { width: '400px' }); } splashPicClicked(){ console.log('Running splashPicClicked()'); document.getElementById('searchInput').blur(); } setCurrentPinnedVariables(classVar){ console.log('classVar.school:'); console.log(classVar.school); this.currentlySelectedPinnedClassUID = classVar.classUID; this.currentlySelectedPinnedClass = classVar.className; this.currentlySelectedPinnedTeacher = classVar.teacher; this.currentlySelectedPinnedSchool = classVar.school; // this.currentlySelectedPinnedClass = classVar; } fetchCusID(usr){ console.log('Running fetchCusID() for usr: '); console.log(usr); this.afs.collection('Customers').ref.where('userUID', '==', usr.uid).get().then(customers => { console.log('Customers: '); if(!customers.empty){ customers.forEach(customer => { console.log(`${customer.data().displayName}'s customerID: ${customer.data().customerID}`); this.addCustomerIDToUser(customer.data().userUID, customer.data().customerID); }); } else { console.log('No Customer UID found for user'); } }); } addCustomerIDToUser(userUID, customerID){ console.log('Running addCustomerIDToUser()'); this.afs.doc(`Users/${userUID}`).ref.update({ customerID: customerID }); } openAdminPanel(){ console.log('Running openAdminPanel()'); this.dialog.open(DialogAdminPanelComponent); } toggleDisplayGradient(val){ this.persistingData.changeToggleDisplayGradient(val); } toggleElevateToolbar(val){ this.persistingData.changeToolbarElevated(val); } editSchool(school){ console.log('Opening editSchoolDialog with school obj:'); console.log(school); console.log('school.schoolName'); console.log(school.schoolName); //Persist school search (since Edit School dialog works off of currently persisted school) this.persistingData.changeSchool(school.schoolName); this.dialog.open(DialogEditSchoolComponent); } addSchoolDisabledClick(){ console.log('Running addSchoolDisabledClick()'); this.snackbar.open('You must be signed in to add a school', 'Ok', {duration: 2000}); } sendAlertTest(){ console.log('Running sendAlertTest'); this.persistingData.changeMassDelete(true); this.common.alertUser(this.user.uid, "Test alert 10 - SettingMD"); } sendAlertTestWithoutSettingMassDelete(){ console.log('Running sendAlertTestWithout'); this.common.alertUser(this.user.uid, "Test alert 10 - NoMD"); } doAsync(){ console.log('Starting doAsync'); this.delay(2000).then(() => { console.log('Done doing async. Aaaah...'); }) } delay(time){ console.log('Reached delay function'); return new Promise((resolve, reject) => { setTimeout(resolve, time); }) } testButton(){ console.log('running testButton()'); this.dialog.open(DialogTutorialComponent, { width: '600px'}); // this.afs.doc(`Users/${this.user.uid}`).ref.update({ // "misc.item3": firebase.firestore.FieldValue.increment(1) // }); } onLoadFunc(){ console.log('Thing loaded!'); } navigateToAboutPage(){ console.log('Running navigateToAboutPage()'); this.persistingData.changeViewWithContainer(false); this.router.navigateByUrl('about'); //Move scroll back up to the top of the page (smoothly) window.scrollTo({ top: 0, behavior: 'smooth' }); } openPinnedClassAssignment(selectedClass, assignment){ console.log('Running openPinnedClassAssignment()'); console.log(assignment); console.log(selectedClass); this.persistingData.changeSchool(selectedClass.school); this.persistingData.changeTeacher(selectedClass.teacher); this.persistingData.changeClass(selectedClass.className); //Set persisting data this.persistingData.changeLoadedSubmissionsType('assignments'); this.persistingData.changeAssignmentObj(assignment); //Phase out old way for this new way //Old way, but active this.persistingData.changeAssignmentUID(assignment.uid); this.persistingData.changeAssignmentName(assignment.title); // this.persistingData.changeAssignmentDueDate(assignment.dueDate); this.persistingData.changeAssignmentWeekDue(assignment.weekDue); this.persistingData.changeAssignmentType(assignment.type) // this.dialog.open(DialogOpenSubmissionsComponent, { minWidth: '900px'}); this.dialog.open(DialogOpenSubmissionsComponent, { minWidth: '800px', width: '50%'}); } } const data = [ {'city': 'Sacramento', 'name': 'Sacramento State' }, {'city': 'Rocklin', 'name': 'Sierra College' }, ]