feat(analytics): add custom event tracking to Photos app

Add PhotosEvents helper (8 events) and integrate tracking into:
- Photos store: favorite toggle, delete, filters applied
- Albums store: create, delete, add/remove photos
- Upload page: photo uploaded

Updates ManaScore analytics from 3/5 to 4/5 (customEvents: true).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-03-25 09:59:42 +01:00
parent bade0a17db
commit 3075e515bd
5 changed files with 26 additions and 1 deletions

View file

@ -17,7 +17,7 @@ scores:
ux: 55
analytics:
pageViewTracking: true
customEvents: false
customEvents: true
authTracking: true
landingTracking: false
publicDashboard: true

View file

@ -3,6 +3,7 @@
*/
import { api } from '$lib/api/client';
import { PhotosEvents } from '@manacore/shared-utils/analytics';
import type { Album, Photo } from '@photos/shared';
// State
@ -92,6 +93,7 @@ export const albumStore = {
}
if (result.data) {
albums = [...albums, result.data];
PhotosEvents.albumCreated();
return result.data;
}
return null;
@ -138,6 +140,7 @@ export const albumStore = {
return false;
}
albums = albums.filter((a) => a.id !== id);
PhotosEvents.albumDeleted();
if (currentAlbum?.id === id) {
currentAlbum = null;
albumPhotos = [];
@ -159,6 +162,7 @@ export const albumStore = {
error = result.error.message;
return false;
}
PhotosEvents.photosAddedToAlbum(mediaIds.length);
// Reload album to get updated items
if (currentAlbum?.id === albumId) {
await this.loadAlbum(albumId);
@ -181,6 +185,7 @@ export const albumStore = {
return false;
}
albumPhotos = albumPhotos.filter((p) => p.id !== mediaId);
PhotosEvents.photoRemovedFromAlbum();
return true;
} catch (e) {
error = e instanceof Error ? e.message : 'Failed to remove photo from album';

View file

@ -3,6 +3,7 @@
*/
import { api } from '$lib/api/client';
import { PhotosEvents } from '@manacore/shared-utils/analytics';
import type { Photo, PhotoFilters, PhotoStats } from '@photos/shared';
// State
@ -104,6 +105,7 @@ export const photoStore = {
*/
async setFilters(newFilters: Partial<PhotoFilters>) {
filters = { ...filters, ...newFilters, offset: 0 };
PhotosEvents.filtersApplied();
await this.loadPhotos(true);
},
@ -135,6 +137,7 @@ export const photoStore = {
try {
const result = await api.post<{ isFavorited: boolean }>(`/favorites/${mediaId}/toggle`);
if (result.data) {
PhotosEvents.photoFavorited(result.data.isFavorited);
// Update photo in list
photos = photos.map((p) =>
p.id === mediaId ? { ...p, isFavorited: result.data!.isFavorited } : p
@ -160,6 +163,7 @@ export const photoStore = {
return false;
}
photos = photos.filter((p) => p.id !== mediaId);
PhotosEvents.photoDeleted();
if (selectedPhoto?.id === mediaId) {
selectedPhoto = null;
}

View file

@ -2,6 +2,7 @@
import { _ } from 'svelte-i18n';
import { goto } from '$app/navigation';
import { uploadWithAuth } from '$lib/api/client';
import { PhotosEvents } from '@manacore/shared-utils/analytics';
import UploadDropzone from '$lib/components/upload/UploadDropzone.svelte';
interface UploadFile {
@ -52,6 +53,7 @@
files[i].status = 'success';
files[i].progress = 100;
PhotosEvents.photoUploaded();
} catch (e) {
files[i].status = 'error';
files[i].error = e instanceof Error ? e.message : 'Upload failed';

View file

@ -275,6 +275,20 @@ export const ManaCoreEvents = {
profileUpdated: () => trackEvent('profile_updated'),
};
/**
* Photos App Events
*/
export const PhotosEvents = {
photoUploaded: () => trackEvent('photo_uploaded'),
photoFavorited: (favorited: boolean) => trackEvent('photo_favorited', { favorited }),
photoDeleted: () => trackEvent('photo_deleted'),
albumCreated: () => trackEvent('album_created'),
albumDeleted: () => trackEvent('album_deleted'),
photosAddedToAlbum: (count: number) => trackEvent('photos_added_to_album', { count }),
photoRemovedFromAlbum: () => trackEvent('photo_removed_from_album'),
filtersApplied: () => trackEvent('filters_applied'),
};
/**
* Storage App Events
*/