migrate firebase db

This commit is contained in:
2026-03-09 21:50:49 +01:00
parent a569c070c5
commit b6c2fe1645
18 changed files with 143 additions and 62 deletions

View File

@@ -1,9 +1,15 @@
import {TestBed} from '@angular/core/testing';
import {Firestore} from '@angular/fire/firestore';
import {DbService} from './db.service';
describe('DbService', () => {
beforeEach(() => void TestBed.configureTestingModule({}));
beforeEach(
() =>
void TestBed.configureTestingModule({
providers: [{provide: Firestore, useValue: {}}],
})
);
it('should be created', () => {
const service: DbService = TestBed.inject(DbService);

View File

@@ -1,24 +1,89 @@
import {Injectable} from '@angular/core';
import {AngularFirestore, AngularFirestoreCollection, AngularFirestoreDocument} from '@angular/fire/compat/firestore';
import {
addDoc,
collection,
collectionData,
CollectionReference,
deleteDoc,
doc,
docData,
DocumentData,
DocumentReference,
Firestore,
query,
QueryConstraint,
setDoc,
updateDoc,
WithFieldValue,
} from '@angular/fire/firestore';
import {Observable} from 'rxjs';
import {QueryFn} from '@angular/fire/compat/firestore/interfaces';
import {map} from 'rxjs/operators';
type CollectionPredicate<T> = string | AngularFirestoreCollection<T>;
type DocumentPredicate<T> = string | AngularFirestoreDocument<T>;
type CollectionPredicate<T> = string | DbCollection<T>;
type DocumentPredicate<T> = string | DbDocument<T>;
export class DbCollection<T> {
public constructor(
private readonly fs: Firestore,
private readonly path: string
) {}
public add(data: Partial<T>): Promise<DocumentReference<T>> {
return addDoc(this.ref as CollectionReference<T>, data as WithFieldValue<T>);
}
public valueChanges(options?: {idField?: string}): Observable<T[]> {
return collectionData(this.ref, options as {idField?: never}) as Observable<T[]>;
}
private get ref(): CollectionReference<DocumentData> {
return collection(this.fs, this.path);
}
}
export class DbDocument<T> {
public constructor(
private readonly fs: Firestore,
private readonly path: string
) {}
public set(data: Partial<T>): Promise<void> {
return setDoc(this.ref as DocumentReference<T>, data as WithFieldValue<T>);
}
public update(data: Partial<T>): Promise<void> {
return updateDoc(this.ref, data as Partial<DocumentData>);
}
public delete(): Promise<void> {
return deleteDoc(this.ref);
}
public collection<U>(subPath: string): DbCollection<U> {
return new DbCollection<U>(this.fs, `${this.path}/${subPath}`);
}
public valueChanges(options?: {idField?: string}): Observable<(NonNullable<T> & {id?: string}) | undefined> {
return docData(this.ref as DocumentReference<T>, options as {idField?: never}) as Observable<(NonNullable<T> & {id?: string}) | undefined>;
}
private get ref(): DocumentReference<DocumentData> {
return doc(this.fs, this.path);
}
}
@Injectable({
providedIn: 'root',
})
export class DbService {
public constructor(private afs: AngularFirestore) {}
public constructor(private fs: Firestore) {}
public col<T>(ref: CollectionPredicate<T>, queryFn?: QueryFn): AngularFirestoreCollection<T> {
return typeof ref === 'string' ? this.afs.collection<T>(ref, queryFn) : ref;
public col<T>(ref: CollectionPredicate<T>): DbCollection<T> {
return typeof ref === 'string' ? new DbCollection<T>(this.fs, ref) : ref;
}
public doc<T>(ref: DocumentPredicate<T>): AngularFirestoreDocument<T> {
return typeof ref === 'string' ? this.afs.doc<T>(ref) : ref;
public doc<T>(ref: DocumentPredicate<T>): DbDocument<T> {
return typeof ref === 'string' ? new DbDocument<T>(this.fs, ref) : ref;
}
public doc$<T>(ref: DocumentPredicate<T>): Observable<(NonNullable<T> & {id?: string}) | null> {
@@ -27,7 +92,12 @@ export class DbService {
.pipe(map(_ => (_ ? _ : null)));
}
public col$<T>(ref: CollectionPredicate<T>, queryFn?: QueryFn): Observable<T[]> {
return this.col(ref, queryFn).valueChanges({idField: 'id'});
public col$<T>(ref: CollectionPredicate<T>, queryConstraints: QueryConstraint[] = []): Observable<T[]> {
if (typeof ref !== 'string' || queryConstraints.length === 0) {
return this.col(ref).valueChanges({idField: 'id'});
}
const q = query(collection(this.fs, ref), ...queryConstraints);
return collectionData(q, {idField: 'id'}) as Observable<T[]>;
}
}

View File

@@ -1,13 +1,29 @@
const elem = document.documentElement;
export const openFullscreen = () => {
if (elem.requestFullscreen) {
void elem.requestFullscreen();
if (!elem.requestFullscreen || !document.fullscreenEnabled) {
return;
}
try {
const promise = elem.requestFullscreen();
if (promise && typeof promise.catch === 'function') {
void promise.catch(() => {
// Browser may reject when no user gesture is present. Keep app usable.
});
}
} catch {
// Some browsers may throw synchronously if fullscreen is not allowed.
}
};
export const closeFullscreen = () => {
if (document.exitFullscreen) {
void document.exitFullscreen();
const promise = document.exitFullscreen();
if (promise && typeof promise.catch === 'function') {
void promise.catch(() => {
// Ignore; leaving fullscreen is a best-effort action.
});
}
}
};

View File

@@ -8,7 +8,7 @@ import {environment} from '../../../environments/environment';
import {Router} from '@angular/router';
import {ShowDataService} from '../../modules/shows/services/show-data.service';
import {ShowSongDataService} from '../../modules/shows/services/show-song-data.service';
import firebase from 'firebase/compat/app';
import {increment} from '@angular/fire/firestore';
export interface SongUsageMigrationResult {
usersProcessed: number;
@@ -159,7 +159,7 @@ export class UserService {
if (!user) return null;
await this.db.doc<User>('users/' + user.id).update({
[`songUsage.${songId}`]: firebase.firestore.FieldValue.increment(direction),
[`songUsage.${songId}`]: increment(direction),
});
}