diff --git a/apps/codingcatdev/package.json b/apps/codingcatdev/package.json
index 46e5e57a0..116ac07d9 100644
--- a/apps/codingcatdev/package.json
+++ b/apps/codingcatdev/package.json
@@ -61,6 +61,7 @@
"firebase": "^10.4.0",
"gsap": "^3.12.2",
"prism-svelte": "^0.5.0",
- "prism-themes": "^1.9.0"
+ "prism-themes": "^1.9.0",
+ "sveltefire": "^0.4.2"
}
}
\ No newline at end of file
diff --git a/apps/codingcatdev/src/lib/client/firebase.ts b/apps/codingcatdev/src/lib/client/firebase.ts
index 99739a31f..6a143137d 100644
--- a/apps/codingcatdev/src/lib/client/firebase.ts
+++ b/apps/codingcatdev/src/lib/client/firebase.ts
@@ -1,17 +1,25 @@
import { browser } from '$app/environment';
-import { initializeApp, getApps, FirebaseError } from 'firebase/app';
+import { initializeApp, getApps } from 'firebase/app';
import {
getAuth,
- setPersistence,
- browserSessionPersistence,
signInWithEmailAndPassword,
signInWithPopup,
type AuthProvider,
type Auth,
createUserWithEmailAndPassword
} from 'firebase/auth';
-import { getFirestore, collection, doc, addDoc, onSnapshot, Firestore } from 'firebase/firestore';
+import {
+ getFirestore,
+ collection,
+ doc,
+ addDoc,
+ onSnapshot,
+ Firestore,
+ setDoc,
+ type DocumentData,
+ initializeFirestore
+} from 'firebase/firestore';
import { httpsCallable, getFunctions, type Functions } from 'firebase/functions';
import {
getAnalytics,
@@ -32,11 +40,11 @@ export const firebaseConfig = {
measurementId: env.PUBLIC_FB_MEASUREMENT_ID
};
-let app = getApps().at(0);
-let auth: Auth;
-let db: Firestore;
-let functions: Functions;
-let analytics: Analytics;
+export let app = getApps().at(0);
+export let auth: Auth;
+export let firestore: Firestore;
+export let functions: Functions;
+export let analytics: Analytics;
if (
!app &&
@@ -50,15 +58,25 @@ if (
firebaseConfig.measurementId
) {
app = initializeApp(firebaseConfig);
-
auth = getAuth(app);
+
// As httpOnly cookies are to be used, do not persist any state client side.
- setPersistence(auth, browserSessionPersistence);
- db = getFirestore(app);
+ // setPersistence(auth, browserSessionPersistence);
+ firestore = initializeFirestore(app, { ignoreUndefinedProperties: true });
functions = getFunctions(app);
analytics = getAnalytics(app);
} else {
- console.debug('Skipping Firebase Initialization, check firebaseconfig.');
+ if (
+ browser &&
+ (!firebaseConfig.apiKey ||
+ !firebaseConfig.authDomain ||
+ !firebaseConfig.projectId ||
+ !firebaseConfig.storageBucket ||
+ !firebaseConfig.messagingSenderId ||
+ !firebaseConfig.appId ||
+ !firebaseConfig.measurementId)
+ )
+ console.debug('Skipping Firebase Initialization, check firebaseconfig.');
}
/* AUTH */
@@ -100,10 +118,13 @@ export const ccdSignInWithPopUp = async (provider: AuthProvider) => {
};
/* DB */
+export const updateUser = async (docRef: string, data: DocumentData) => {
+ return setDoc(doc(firestore, docRef), data, { merge: true });
+};
/* STRIPE */
export const addSubscription = async (price: string, uid: string) => {
- const userDoc = doc(collection(db, 'stripe-customers'), uid);
+ const userDoc = doc(collection(firestore, 'stripe-customers'), uid);
return await addDoc(collection(userDoc, 'checkout_sessions'), {
price,
success_url: window.location.href,
diff --git a/apps/codingcatdev/src/lib/server/firebase.ts b/apps/codingcatdev/src/lib/server/firebase.ts
index 21e7e9168..4a7e807ed 100644
--- a/apps/codingcatdev/src/lib/server/firebase.ts
+++ b/apps/codingcatdev/src/lib/server/firebase.ts
@@ -5,6 +5,7 @@ import { getFirestore } from 'firebase-admin/firestore';
import { env as publicEnv } from '$env/dynamic/public';
import { env as privateEnv } from '$env/dynamic/private';
+import type { UserDoc } from '$lib/types';
export let app = getApps().at(0);
@@ -101,33 +102,3 @@ export const getStripeProducts = async () => {
}
return products;
};
-
-export interface UserSettings {
- settings: {
- showDrafts?: boolean;
- };
-}
-
-export const getUser = async (uid?: string) => {
- if (!uid) return undefined;
-
- // Check if user is Pro and wants drafts
- const auth = getAuth(app);
- const user = await auth.getUser(uid);
-
- const db = getFirestore();
- const doc = await db.collection('users').doc(user.uid).get();
- return doc.data() as UserSettings;
-};
-
-export const updateUser = async (uid?: string, userSettings?: UserSettings) => {
- if (!uid) return undefined;
- if (!userSettings) return;
-
- // Check if user is Pro and wants drafts
- const auth = getAuth(app);
- const user = await auth.getUser(uid);
-
- const db = getFirestore();
- await db.collection('users').doc(user.uid).set(userSettings, { merge: true });
-};
diff --git a/apps/codingcatdev/src/lib/types/index.ts b/apps/codingcatdev/src/lib/types/index.ts
index 024c7dcfd..f6e5fbc80 100644
--- a/apps/codingcatdev/src/lib/types/index.ts
+++ b/apps/codingcatdev/src/lib/types/index.ts
@@ -75,11 +75,11 @@ export enum ContentType {
page = 'page',
podcast = 'podcast',
post = 'post',
- sponsor = 'sponsor',
+ sponsor = 'sponsor'
}
export enum PodcastType {
- codingcatdev = 'codingcatdev',
+ codingcatdev = 'codingcatdev'
}
export interface Socials {
@@ -151,3 +151,25 @@ export interface DirectoryStub {
}
export type Stub = FileStub | DirectoryStub;
+export interface UserDoc {
+ pro?: Pro;
+}
+export interface Pro {
+ settings?: {
+ showDrafts?: boolean;
+ };
+ completed?: PathDate[];
+ bookmarked?: PathDate[];
+}
+
+export interface PathDate {
+ path: string;
+ date: number;
+}
+
+export interface Saved extends Content, Lesson {
+ savedId: string;
+ savedUpdated: Date;
+ savedComplete: boolean;
+ lesson?: Saved[];
+}
diff --git a/apps/codingcatdev/src/routes/(content-list)/ContentCards.svelte b/apps/codingcatdev/src/routes/(content-list)/ContentCards.svelte
index 98bfd42fe..3bcdd68c3 100644
--- a/apps/codingcatdev/src/routes/(content-list)/ContentCards.svelte
+++ b/apps/codingcatdev/src/routes/(content-list)/ContentCards.svelte
@@ -2,7 +2,14 @@
import Image from '$lib/components/content/Image.svelte';
import { ContentPublished, type Content, type Course } from '$lib/types';
import { ContentType } from '$lib/types';
- export let data: { contentType: ContentType; content: Content[] & Course[]; next?: any };
+ import type { LayoutData } from '../$types';
+ import ProCourseMark from '../(content-single)/course/ProCourseMark.svelte';
+ export let data: {
+ contentType: ContentType;
+ content: Content[] & Course[];
+ next?: any;
+ user: LayoutData['user'];
+ };
let next = data.next;
const contentType = data.contentType;
@@ -18,7 +25,8 @@
data = {
contentType,
content: [...data.content, ...d.content],
- next
+ next,
+ user: data?.user
};
next = d.next;
};
@@ -58,13 +66,30 @@
{content.title}
@@ -88,9 +113,3 @@
{data.course.title}
{#if data?.sponsors?.length}
@@ -90,12 +89,7 @@