11import { initializeApp } from "firebase/app" ;
22import { getAuth , GoogleAuthProvider , onAuthStateChanged } from "firebase/auth" ;
3- import { get , getDatabase , ref , set } from "firebase/database" ;
4-
3+ import { get , getDatabase , ref , set , onValue } from "firebase/database" ;
4+ import { reaction , toJS } from "mobx" ;
5+ // foo
56// Your web app's Firebase configuration
67const firebaseConfig = {
7- apiKey : "AIzaSyCBckVI9nhAP62u5jZJW3F4SLulUv7znis" ,
8- authDomain : "findmynextcourse.firebaseapp.com" ,
9- databaseURL : "https://findmynextcourse-default-rtdb.europe-west1.firebasedatabase.app" ,
10- projectId : " findmynextcourse",
11- storageBucket : "findmynextcourse.firebasestorage.app " ,
12- messagingSenderId : "893484115963 " ,
13- appId : "1: 893484115963:web:59ac087d280dec919ccd5e"
14- } ;
15-
8+ apiKey : "AIzaSyCBckVI9nhAP62u5jZJW3F4SLulUv7znis" ,
9+ authDomain : "findmynextcourse.firebaseapp.com" ,
10+ databaseURL :
11+ "https:// findmynextcourse-default-rtdb.europe-west1.firebasedatabase.app ",
12+ projectId : "findmynextcourse" ,
13+ storageBucket : "findmynextcourse.firebasestorage.app " ,
14+ messagingSenderId : "893484115963" ,
15+ appId : "1:893484115963:web:59ac087d280dec919ccd5e" ,
16+ } ;
1617
1718// Initialize Firebase
1819const app = initializeApp ( firebaseConfig ) ;
@@ -21,119 +22,150 @@ export const db = getDatabase(app);
2122export const googleProvider = new GoogleAuthProvider ( ) ;
2223googleProvider . addScope ( "profile" ) ;
2324googleProvider . addScope ( "email" ) ;
25+ let noUpload = false ;
26+
27+ export function connectToFirebase ( model ) {
28+ loadCoursesFromCacheOrFirebase ( model ) ;
29+ onAuthStateChanged ( auth , ( user ) => {
30+ if ( user ) {
31+ model . setUser ( user . uid ) ; // Set the user ID once authenticated
32+ firebaseToModel ( model ) ; // Set up listeners for user-specific data
33+ syncModelToFirebase ( model ) ; // Start syncing changes to Firebase
34+ } else {
35+ model . setUser ( null ) ; // If no user, clear user-specific data
36+ }
37+ } ) ;
38+ }
2439
2540// fetches all relevant information to create the model
2641async function firebaseToModel ( model ) {
27- // Load metadata from localStorage
28- const cachedMetadata = JSON . parse ( localStorage . getItem ( "coursesMetadata" ) ) ;
29- const firebaseTimestamp = await fetchLastUpdatedTimestamp ( ) ;
30- // check if up to date
31- if ( cachedMetadata && cachedMetadata . timestamp === firebaseTimestamp ) {
32- console . log ( "Using cached courses..." ) ;
33- let mergedCourses = [ ] ;
34- for ( let i = 0 ; i < cachedMetadata . parts ; i ++ ) {
35- const part = JSON . parse ( localStorage . getItem ( `coursesPart${ i } ` ) ) ;
36- if ( part )
37- mergedCourses = mergedCourses . concat ( part ) ;
38- }
39- model . setCourses ( mergedCourses ) ;
42+ if ( ! model . user )
4043 return ;
41- }
44+ const userRef = ref ( db , `users/${ model . user } ` ) ;
45+ onValue ( userRef , ( snapshot ) => {
46+ if ( ! snapshot . exists ( ) )
47+ return ;
48+ const data = snapshot . val ( ) ;
49+ noUpload = true ;
50+ if ( data . favourites )
51+ model . setFavourite ( data . favourites ) ;
52+ if ( data . currentSearch )
53+ model . setCurrentSearch ( data . currentSearch ) ;
54+ noUpload = false ;
55+ } ) ;
56+ }
4257
43- // Fetch if outdated or missing
44- console . log ( "Fetching courses from Firebase..." ) ;
45- const courses = await fetchAllCourses ( ) ;
46- model . setCourses ( courses ) ;
47- saveCoursesInChunks ( courses , firebaseTimestamp ) ;
58+
59+ export function syncModelToFirebase ( model ) {
60+ reaction (
61+ ( ) => ( {
62+ userId : model ?. user ,
63+ favourites : toJS ( model . favourites ) ,
64+ currentSearch : toJS ( model . currentSearch ) ,
65+ // Add more per-user attributes here
66+ } ) ,
67+ ( { userId, favourites, currentSearch } ) => {
68+ if ( noUpload || ! userId )
69+ return ;
70+ const userRef = ref ( db , `users/${ userId } ` ) ;
71+ const dataToSync = {
72+ favourites,
73+ currentSearch,
74+ } ;
75+
76+ set ( userRef , dataToSync )
77+ . then ( ( ) => console . log ( "User model synced to Firebase" ) )
78+ . catch ( console . error ) ;
79+ }
80+ ) ;
4881}
4982
5083function saveCoursesInChunks ( courses , timestamp ) {
51- const parts = 3 ; // Adjust this based on course size
52- const chunkSize = Math . ceil ( courses . length / parts ) ;
53-
54- for ( let i = 0 ; i < parts ; i ++ ) {
55- const chunk = courses . slice ( i * chunkSize , ( i + 1 ) * chunkSize ) ;
56- localStorage . setItem ( `coursesPart${ i } ` , JSON . stringify ( chunk ) ) ;
57- }
58- localStorage . setItem ( "coursesMetadata" , JSON . stringify ( { parts, timestamp } ) ) ;
84+ const parts = 3 ; // Adjust this based on course size
85+ const chunkSize = Math . ceil ( courses . length / parts ) ;
86+
87+ for ( let i = 0 ; i < parts ; i ++ ) {
88+ const chunk = courses . slice ( i * chunkSize , ( i + 1 ) * chunkSize ) ;
89+ localStorage . setItem ( `coursesPart${ i } ` , JSON . stringify ( chunk ) ) ;
90+ }
91+ localStorage . setItem ( "coursesMetadata" , JSON . stringify ( { parts, timestamp } ) ) ;
5992}
6093
6194async function updateLastUpdatedTimestamp ( ) {
62- const timestampRef = ref ( db , "metadata/lastUpdated" ) ;
63- await set ( timestampRef , Date . now ( ) ) ;
95+ const timestampRef = ref ( db , "metadata/lastUpdated" ) ;
96+ await set ( timestampRef , Date . now ( ) ) ;
6497}
6598
6699async function fetchLastUpdatedTimestamp ( ) {
67- const timestampRef = ref ( db , "metadata/lastUpdated" ) ;
68- const snapshot = await get ( timestampRef ) ;
69- return snapshot . exists ( ) ? snapshot . val ( ) : 0 ;
100+ const timestampRef = ref ( db , "metadata/lastUpdated" ) ;
101+ const snapshot = await get ( timestampRef ) ;
102+ return snapshot . exists ( ) ? snapshot . val ( ) : 0 ;
70103}
71104
72- export function connectToFirebase ( model ) {
73- onAuthStateChanged ( auth , ( user ) => {
74- model . setUser ( user ) ;
75- } ) ;
76- firebaseToModel ( model ) ;
77- }
78-
79- export async function addCourse ( course ) {
80- if ( ! course ?. code )
81- return ;
82- const myRef = ref ( db , `courses/${ course . code } ` ) ;
83- await set ( myRef , course ) ;
84- updateLastUpdatedTimestamp ( ) ;
105+ export async function addCourse ( course ) {
106+ if ( ! course ?. code ) return ;
107+ const myRef = ref ( db , `courses/${ course . code } ` ) ;
108+ await set ( myRef , course ) ;
109+ updateLastUpdatedTimestamp ( ) ;
85110}
86111
87112export async function fetchAllCourses ( ) {
88- const myRef = ref ( db , `courses` ) ;
89- const snapshot = await get ( myRef ) ;
90-
91- if ( ! snapshot . exists ( ) ) return [ ] ;
113+ const myRef = ref ( db , `courses` ) ;
114+ const snapshot = await get ( myRef ) ;
115+ if ( ! snapshot . exists ( ) ) return [ ] ;
92116
93- const value = snapshot . val ( ) ; // Firebase returns an object where keys are course IDs
94- const courses = [ ] ;
117+ const value = snapshot . val ( ) ; // Firebase returns an object where keys are course IDs
118+ const courses = [ ] ;
95119
96- for ( const id of Object . keys ( value ) ) {
97- courses . push ( { id, ...value [ id ] } ) ;
98- }
99-
100- return courses ;
120+ for ( const id of Object . keys ( value ) ) {
121+ courses . push ( { id, ...value [ id ] } ) ;
122+ }
123+ return courses ;
101124}
102125
103- // Before: [ {courseCode: "CS101", name: "Intro to CS"}, {...} ]
104- // After: { "CS101": { name: "Intro to CS" }, "CS102": {...} }
105-
106-
107- export async function fetchCoursesSnapshot ( ) {
108- const myRef = ref ( db , `courses` ) ;
109- const snapshot = await get ( myRef ) ;
110- if ( ! snapshot . exists ( ) ) return { } ; // Return empty object instead of array
111-
112- return snapshot . val ( ) ; // Return the object directly
126+ async function loadCoursesFromCacheOrFirebase ( model ) {
127+ // Load metadata from localStorage
128+ const cachedMetadata = JSON . parse ( localStorage . getItem ( "coursesMetadata" ) ) ;
129+ const firebaseTimestamp = await fetchLastUpdatedTimestamp ( ) ;
130+ // check if up to date
131+ if ( cachedMetadata && cachedMetadata . timestamp === firebaseTimestamp ) {
132+ console . log ( "Using cached courses..." ) ;
133+ let mergedCourses = [ ] ;
134+ for ( let i = 0 ; i < cachedMetadata . parts ; i ++ ) {
135+ const part = JSON . parse ( localStorage . getItem ( `coursesPart${ i } ` ) ) ;
136+ if ( part ) mergedCourses = mergedCourses . concat ( part ) ;
137+ }
138+ model . setCourses ( mergedCourses ) ;
139+ return ;
140+ }
141+
142+ // Fetch if outdated or missing
143+ console . log ( "Fetching courses from Firebase..." ) ;
144+ const courses = await fetchAllCourses ( ) ;
145+ model . setCourses ( courses ) ;
146+ saveCoursesInChunks ( courses , firebaseTimestamp ) ;
113147}
114148
115- export async function saveJSONCoursesToFirebase ( model , data ) {
116- if ( ! data || ! model ) {
117- console . log ( "no model or data" )
118- return ;
119- }
120- const entries = Object . entries ( data ) ;
121- entries . forEach ( entry => {
122- const course = {
123- code : entry [ 1 ] . code ,
124- name : entry [ 1 ] ?. name ?? "" ,
125- location : entry [ 1 ] ?. location ?? "" ,
126- department : entry [ 1 ] ?. department ?? "" ,
127- language : entry [ 1 ] ?. language ?? "" ,
128- description : entry [ 1 ] ?. description ?? "" ,
129- academicLevel : entry [ 1 ] ?. academic_level ?? "" ,
130- period : entry [ 1 ] ?. period ?? "" ,
131- credits : entry [ 1 ] ?. credits ?? 0 ,
132- //lectureCount:entry[1].courseLectureCount,
133- //prerequisites:entry.coursePrerequisites
134- }
135- model . addCourse ( course ) ;
136-
137- } ) ;
149+ export async function saveJSONCoursesToFirebase ( model , data ) {
150+ if ( ! data || ! model ) {
151+ console . log ( "no model or data" ) ;
152+ return ;
153+ }
154+ const entries = Object . entries ( data ) ;
155+ entries . forEach ( ( entry ) => {
156+ const course = {
157+ code : entry [ 1 ] . code ,
158+ name : entry [ 1 ] ?. name ?? "" ,
159+ location : entry [ 1 ] ?. location ?? "" ,
160+ department : entry [ 1 ] ?. department ?? "" ,
161+ language : entry [ 1 ] ?. language ?? "" ,
162+ description : entry [ 1 ] ?. description ?? "" ,
163+ academicLevel : entry [ 1 ] ?. academic_level ?? "" ,
164+ period : entry [ 1 ] ?. period ?? "" ,
165+ credits : entry [ 1 ] ?. credits ?? 0 ,
166+ //lectureCount:entry[1].courseLectureCount,
167+ //prerequisites:entry.coursePrerequisites
168+ } ;
169+ model . addCourse ( course ) ;
170+ } ) ;
138171}
139-
0 commit comments