Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

analytics: PageView component #146

Merged
merged 1 commit into from
Dec 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"
}
}
Loading