Skip to content

Commit

Permalink
Merge pull request #146 from codediodeio/analytics
Browse files Browse the repository at this point in the history
analytics: PageView component
  • Loading branch information
codediodeio authored Dec 27, 2023
2 parents 80af67a + e01bf79 commit 57c9237
Show file tree
Hide file tree
Showing 16 changed files with 185 additions and 23 deletions.
34 changes: 22 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

A minimal, yet powerful library that puts realtime Firebase data into Svelte stores.

[Documentation](https://sveltefire.fireship.io)
[Full Documentation](https://sveltefire.fireship.io)

```svelte
<!-- 1. 🔥 Firebase App -->
Expand All @@ -22,17 +22,6 @@ A minimal, yet powerful library that puts realtime Firebase data into Svelte sto
<Collection ref={postRef.path + '/comments'} let:data={comments}>
{#each comments as comment}
{/each}
<!-- 3 (b). 📜 Get a Realtime Database node owned by a user -->
<Node path={`posts/${user.uid}`} let:data={post} let:ref={postRef}>
<h2>{post.title}</h2>
<!-- 4 (b). 💬 Get all the comments in its subnodes -->
<NodeList path={postRef.path + '/comments'} let:data={comments}>
{#each comments as comment}
{/each}
...
```
Expand All @@ -46,6 +35,7 @@ Svelte makes it possible to dramatically simplify the way developers work with F
- Better TypeScript experience for Firebase
- Handle complex relational data between Auth, Firestore, and Realtime Database
- Easily hydrate SvelteKit server data into a realtime Firebase stream
- Simple Google Analytics integration for SvelteKit

## Quick Start

Expand All @@ -60,12 +50,14 @@ import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";
import { getDatabase } from "firebase/database";
import { getAuth } from "firebase/auth";
import { getAnalytics } from "firebase/analytics";

// Initialize Firebase
const app = initializeApp(/* your firebase config */);
export const db = getFirestore(app);
export const rtdb = getDatabase(app);
export const auth = getAuth(app);
export const auth = getAnalytics(app);
```

2. Get the Current user
Expand Down Expand Up @@ -98,6 +90,24 @@ Use the `$` as much as you want - it will only result in one Firebase read reque

Or better yet, use the built in `Doc` and `Collection` components for Firestore, or `Node` and `NodeList` components for Realtime Database. See below.

4. Add Firebase/Google Analytics

Easily generate a `page_view` event on every route change with SvelteKit layouts - works for both client and server rendered pages.

```svelte
<!-- +layout.svelte -->
<script lang="ts">
import { page } from "$app/stores";
import { PageView } from "sveltefire";
</script>
<slot />
{#key $page.route.id}
<PageView />
{/key}
```

## Stores

Stores are the building blocks of SvelteFire.
Expand Down
2 changes: 1 addition & 1 deletion docs/src/components/SideNav.astro
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<li><a href="/rtdb/node-component">&ltNode&gt</a></li>
<li><a href="/rtdb/node-list-component">&ltNodeList&gt</a></li>
<li class="heading">analytics</li>
<li><a href="/guide/todo">&ltPageView&gt</a></li>
<li><a href="/analytics/page-view-component">&ltPageView&gt</a></li>
</ul>
</nav>

Expand Down
52 changes: 52 additions & 0 deletions docs/src/pages/analytics/page-view-component.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
title: PageView Component
pubDate: 2023-12-23
description: SvelteFire PageView Component API reference
layout: ../../layouts/MainLayout.astro
---

# PageView

The `PageView` component logs a Google analytics `page_view` event when it is mounted.

### Slot Props

- `eventName` - (default: 'page_view') Set the current user as the userId in Google Analytics
- `setUser` - (default: true) Set the current user as the userId in Google Analytics
- `customParams` - (optional) custom parameters to pass to the `signIn` function

### Layout Example (recommended)

The most efficient way to integrate Firebase Analytics is to log events from a layout component. This will ensure that every route change is logged, both on the client and server. Make sure to `key` the `PageView` component so that it is re-mounted on every route change.

```svelte
<!-- +layout.svelte -->
<script lang="ts">
import { page } from "$app/stores";
import { PageView } from "sveltefire";
</script>
<slot />
{#key $page.route.id}
<PageView />
{/key}
```

### Page Example

For fine-grained control, you can include `PageView` on a page-by-page basis. This is useful when sending custom parameters.


```svelte
<!-- +page.svelte -->
<script lang="ts">
const myData = {
guild: 'griffindor',
currency: 'gpb'
}
</script>
<PageView eventName="my_page_view" customParams={myData} setUser={false} />
```
4 changes: 3 additions & 1 deletion src/lib/components/FirebaseApp.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
import type { Firestore } from "firebase/firestore";
import type { Database } from "firebase/database";
import type { FirebaseStorage } from "firebase/storage";
import type { Analytics } from "firebase/analytics";
export let firestore: Firestore;
export let rtdb: Database;
export let auth: Auth;
export let storage: FirebaseStorage;
export let analytics: Analytics | null;
setFirebaseContext({ firestore, rtdb, auth, storage });
setFirebaseContext({ firestore, rtdb, auth, storage, analytics });
</script>

<slot />
27 changes: 27 additions & 0 deletions src/lib/components/PageView.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<script lang="ts">
import { logEvent, setUserId, isSupported } from "firebase/analytics";
import { getFirebaseContext } from "$lib/index.js";
import { onMount } from "svelte";
export let eventName = "page_view";
export let setUser = true;
export let customParams: Record<string, unknown> = {};
const analytics = getFirebaseContext().analytics!;
const auth = getFirebaseContext().auth!;
onMount(async () => {
if (!(await isSupported())) {
return;
}
if (setUser) {
setUserId(analytics, auth.currentUser?.uid ?? null);
}
logEvent(analytics, eventName, {
page_location: window.location.href,
page_path: window.location.pathname,
page_title: document.title,
...customParams,
});
});
</script>
12 changes: 7 additions & 5 deletions src/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import SignedOut from './components/SignedOut.svelte';
import DownloadURL from './components/DownloadURL.svelte';
import StorageList from './components/StorageList.svelte';
import UploadTask from './components/UploadTask.svelte';
import { userStore } from './stores/auth.js';
import { docStore, collectionStore } from './stores/firestore.js';
import { nodeStore, nodeListStore } from './stores/rtdb.js';
import { getFirebaseContext } from './stores/sdk.js';
import { downloadUrlStore, storageListStore, uploadTaskStore } from './stores/storage.js';
import PageView from './components/PageView.svelte';
import { userStore } from './stores/auth';
import { docStore, collectionStore } from './stores/firestore';
import { nodeStore, nodeListStore } from './stores/rtdb';
import { getFirebaseContext } from './stores/sdk';
import { downloadUrlStore, storageListStore, uploadTaskStore } from './stores/storage';

export {
Doc,
Expand All @@ -24,6 +25,7 @@ export {
UploadTask,
StorageList,
DownloadURL,
PageView,
downloadUrlStore,
storageListStore,
uploadTaskStore,
Expand Down
1 change: 0 additions & 1 deletion src/lib/stores/auth.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { writable } from "svelte/store";
import { getFirebaseContext } from "./sdk.js";
import { onAuthStateChanged, type Auth } from "firebase/auth";

/**
Expand Down
2 changes: 2 additions & 0 deletions src/lib/stores/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import type { Database } from "firebase/database";
import type { Auth } from "firebase/auth";
import { getContext, setContext } from "svelte";
import type { FirebaseStorage } from "firebase/storage";
import type { Analytics } from "firebase/analytics";

export interface FirebaseSDKContext {
auth?: Auth;
firestore?: Firestore;
rtdb?: Database;
storage?: FirebaseStorage;
analytics?: Analytics | null;
}

export const contextKey = "firebase";
Expand Down
4 changes: 2 additions & 2 deletions src/routes/+layout.svelte
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<script lang="ts">
import FirebaseApp from "$lib/components/FirebaseApp.svelte";
import { db as firestore, auth, rtdb, storage } from "./firebase.js";
import { db as firestore, auth, rtdb, storage, analytics } from "./firebase.js";
</script>

<FirebaseApp {auth} {firestore} {rtdb} {storage}>
<FirebaseApp {auth} {firestore} {rtdb} {storage} {analytics}>
<slot />
</FirebaseApp>
1 change: 1 addition & 0 deletions src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<li><a href="/rtdb-test">Realtime Database Test</a></li>
<li><a href="/ssr-test">SSR Test</a></li>
<li><a href="/storage-test">Storage Test</a></li>
<li><a href="/analytics-test">Analytics Test</a></li>
</ul>
<ul>
<li data-testid="auth">Auth Context: {!!ctx.auth}</li>
Expand Down
12 changes: 12 additions & 0 deletions src/routes/analytics-test/+layout.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<script lang="ts">
import { page } from "$app/stores";
import PageView from "$lib/components/PageView.svelte";
</script>

<slot />

<h2>Analytics Layout</h2>
{#key $page.route.id}
<p>Logged analytics for {$page.route.id}</p>
<PageView />
{/key}
7 changes: 7 additions & 0 deletions src/routes/analytics-test/+page.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { PageServerLoad } from './$types';

export const load: PageServerLoad = (async () => {
return {
title: 'Analytics Test 1 - Server Rendered',
};
}) satisfies PageServerLoad;
21 changes: 21 additions & 0 deletions src/routes/analytics-test/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script lang="ts">
import type { PageData } from './$types';
import PageView from '$lib/components/PageView.svelte';
export let data: PageData;
</script>

<svelte:head>
<title>{data.title}</title>
</svelte:head>

<h1>{data.title}</h1>

<p>
This is a test page for analytics. The title is always server rendered.
</p>

<a href="/analytics-test/client-side">Analytics CSR Test</a>


<!-- <PageView /> -->
24 changes: 24 additions & 0 deletions src/routes/analytics-test/client-side/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<script lang="ts">
import PageView from '$lib/components/PageView.svelte';
const customParams = {
mood: 'happy'
};
const title = 'Analytics Test Page 2 - Client Rendered';
</script>

<svelte:head>
<title>{title}</title>
</svelte:head>

<!-- <PageView {customParams} /> -->

<h1>{title}</h1>

<p>
This is a test page for analytics. The title is always server rendered.
</p>

<a href="/analytics-test">Analytics SSR Test</a>

3 changes: 3 additions & 0 deletions src/routes/firebase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
ref as storageRef,
uploadString,
} from "firebase/storage";
import { getAnalytics } from "firebase/analytics";

const firebaseConfig = {
apiKey: "AIzaSyAMHfJp1ec85QBo-mnke89qtiYGen9zTSE",
Expand All @@ -28,6 +29,7 @@ const firebaseConfig = {
storageBucket: "sveltefire-testing.appspot.com",
messagingSenderId: "1030648105982",
appId: "1:1030648105982:web:2afebc34841fa242ed4eaf",
measurementId: "G-RT6GM89V6K"
};

// Initialize Firebase
Expand All @@ -36,6 +38,7 @@ export const db = getFirestore(app);
export const rtdb = getDatabase(app);
export const auth = getAuth(app);
export const storage = getStorage(app);
export const analytics = typeof window !== "undefined" ? getAnalytics(app) : null;

if (dev || import.meta.env.MODE === "ci") {
connectAuthEmulator(auth, "http://localhost:9099");
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "NodeNext"
"moduleResolution": "Bundler"
}
}

0 comments on commit 57c9237

Please sign in to comment.