pwa
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable, inject } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { of, tap, throwError } from 'rxjs';
|
||||
import {
|
||||
AddListItemRequest,
|
||||
CreateListRequest,
|
||||
@@ -10,45 +11,144 @@ import {
|
||||
UserList,
|
||||
} from './lists.models';
|
||||
import { ListTemplate } from '../templates/templates.models';
|
||||
import { OfflineSyncService } from '../offline/offline-sync.service';
|
||||
import { OnlineStatusService } from '../offline/online-status.service';
|
||||
|
||||
const LIST_CACHE_KEY = 'listify.lists.cache.v1';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class ListsService {
|
||||
private readonly http = inject(HttpClient);
|
||||
private readonly offlineSync = inject(OfflineSyncService);
|
||||
private readonly onlineStatus = inject(OnlineStatusService);
|
||||
private readonly apiUrl = '/api/lists';
|
||||
|
||||
listLists(): Observable<UserList[]> {
|
||||
return this.http.get<UserList[]>(this.apiUrl);
|
||||
if (!this.onlineStatus.online()) {
|
||||
return of(this.readCachedLists());
|
||||
}
|
||||
|
||||
return this.http
|
||||
.get<UserList[]>(this.apiUrl)
|
||||
.pipe(tap((lists) => this.writeCachedLists(lists)));
|
||||
}
|
||||
|
||||
getList(listId: string): Observable<UserList> {
|
||||
return this.http.get<UserList>(`${this.apiUrl}/${listId}`);
|
||||
if (!this.onlineStatus.online()) {
|
||||
const cachedList = this.readCachedLists().find((list) => list.id === listId);
|
||||
|
||||
if (cachedList) {
|
||||
return of(cachedList);
|
||||
}
|
||||
|
||||
return throwError(() => new Error('Liste ist offline nicht im Cache.'));
|
||||
}
|
||||
|
||||
return this.http
|
||||
.get<UserList>(`${this.apiUrl}/${listId}`)
|
||||
.pipe(tap((list) => this.upsertCachedList(list)));
|
||||
}
|
||||
|
||||
createList(data: CreateListRequest): Observable<UserList> {
|
||||
if (!this.onlineStatus.online()) {
|
||||
return throwError(
|
||||
() => new Error('Neue Listen koennen offline noch nicht erstellt werden.'),
|
||||
);
|
||||
}
|
||||
|
||||
return this.http.post<UserList>(this.apiUrl, data);
|
||||
}
|
||||
|
||||
updateList(listId: string, data: UpdateListRequest): Observable<UserList> {
|
||||
return this.http.patch<UserList>(`${this.apiUrl}/${listId}`, data);
|
||||
if (!this.onlineStatus.online()) {
|
||||
const updatedList = this.updateCachedList(listId, (list) => ({
|
||||
...list,
|
||||
...data,
|
||||
updatedAt: new Date().toISOString(),
|
||||
}));
|
||||
|
||||
this.offlineSync.enqueueUpdateList(listId, data);
|
||||
return updatedList
|
||||
? of(updatedList)
|
||||
: throwError(() => new Error('Liste ist offline nicht im Cache.'));
|
||||
}
|
||||
|
||||
return this.http
|
||||
.patch<UserList>(`${this.apiUrl}/${listId}`, data)
|
||||
.pipe(tap((list) => this.upsertCachedList(list)));
|
||||
}
|
||||
|
||||
deleteList(listId: string): Observable<{ message: string }> {
|
||||
return this.http.delete<{ message: string }>(`${this.apiUrl}/${listId}`);
|
||||
if (!this.onlineStatus.online()) {
|
||||
this.removeCachedList(listId);
|
||||
this.offlineSync.enqueueDeleteList(listId);
|
||||
return of({ message: 'List queued for deletion.' });
|
||||
}
|
||||
|
||||
return this.http
|
||||
.delete<{ message: string }>(`${this.apiUrl}/${listId}`)
|
||||
.pipe(tap(() => this.removeCachedList(listId)));
|
||||
}
|
||||
|
||||
shareList(listId: string, userId: string): Observable<UserList> {
|
||||
if (!this.onlineStatus.online()) {
|
||||
return throwError(
|
||||
() => new Error('Freigaben koennen offline nicht geaendert werden.'),
|
||||
);
|
||||
}
|
||||
|
||||
return this.http.post<UserList>(`${this.apiUrl}/${listId}/shares`, { userId });
|
||||
}
|
||||
|
||||
removeShare(listId: string, userId: string): Observable<UserList> {
|
||||
if (!this.onlineStatus.online()) {
|
||||
return throwError(
|
||||
() => new Error('Freigaben koennen offline nicht geaendert werden.'),
|
||||
);
|
||||
}
|
||||
|
||||
return this.http.delete<UserList>(`${this.apiUrl}/${listId}/shares/${userId}`);
|
||||
}
|
||||
|
||||
addItem(listId: string, data: AddListItemRequest): Observable<UserList> {
|
||||
return this.http.post<UserList>(`${this.apiUrl}/${listId}/items`, data);
|
||||
if (!this.onlineStatus.online()) {
|
||||
const updatedList = this.updateCachedList(listId, (list) => ({
|
||||
...list,
|
||||
items: [
|
||||
...list.items,
|
||||
{
|
||||
id: `offline-${Date.now()}-${Math.random().toString(16).slice(2)}`,
|
||||
title: data.title,
|
||||
notes: data.notes,
|
||||
quantity: data.quantity,
|
||||
required: data.required ?? true,
|
||||
checked: false,
|
||||
position: list.items.length,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
],
|
||||
updatedAt: new Date().toISOString(),
|
||||
}));
|
||||
|
||||
this.offlineSync.enqueueAddItem(listId, data);
|
||||
return updatedList
|
||||
? of(updatedList)
|
||||
: throwError(() => new Error('Liste ist offline nicht im Cache.'));
|
||||
}
|
||||
|
||||
return this.http
|
||||
.post<UserList>(`${this.apiUrl}/${listId}/items`, data)
|
||||
.pipe(tap((list) => this.upsertCachedList(list)));
|
||||
}
|
||||
|
||||
suggestItems(listId: string): Observable<ListItemSuggestionsResponse> {
|
||||
if (!this.onlineStatus.online()) {
|
||||
return throwError(
|
||||
() => new Error('Smart Suggestions sind nur online verfuegbar.'),
|
||||
);
|
||||
}
|
||||
|
||||
return this.http.post<ListItemSuggestionsResponse>(
|
||||
`${this.apiUrl}/${listId}/item-suggestions`,
|
||||
{},
|
||||
@@ -56,6 +156,12 @@ export class ListsService {
|
||||
}
|
||||
|
||||
createTemplateFromList(listId: string): Observable<ListTemplate> {
|
||||
if (!this.onlineStatus.online()) {
|
||||
return throwError(
|
||||
() => new Error('Templates koennen nur online aus Listen erstellt werden.'),
|
||||
);
|
||||
}
|
||||
|
||||
return this.http.post<ListTemplate>(`${this.apiUrl}/${listId}/template`, {});
|
||||
}
|
||||
|
||||
@@ -64,9 +170,100 @@ export class ListsService {
|
||||
itemId: string,
|
||||
data: UpdateListItemRequest,
|
||||
): Observable<UserList> {
|
||||
return this.http.patch<UserList>(
|
||||
`${this.apiUrl}/${listId}/items/${itemId}`,
|
||||
data,
|
||||
if (!this.onlineStatus.online()) {
|
||||
const updatedList = this.updateCachedList(listId, (list) => ({
|
||||
...list,
|
||||
items: list.items.map((item) =>
|
||||
item.id === itemId
|
||||
? {
|
||||
...item,
|
||||
...data,
|
||||
checkedAt:
|
||||
data.checked === true
|
||||
? new Date().toISOString()
|
||||
: data.checked === false
|
||||
? undefined
|
||||
: item.checkedAt,
|
||||
updatedAt: new Date().toISOString(),
|
||||
}
|
||||
: item,
|
||||
),
|
||||
updatedAt: new Date().toISOString(),
|
||||
}));
|
||||
|
||||
if (!itemId.startsWith('offline-')) {
|
||||
this.offlineSync.enqueueUpdateItem(listId, itemId, data);
|
||||
}
|
||||
|
||||
return updatedList
|
||||
? of(updatedList)
|
||||
: throwError(() => new Error('Liste ist offline nicht im Cache.'));
|
||||
}
|
||||
|
||||
return this.http
|
||||
.patch<UserList>(`${this.apiUrl}/${listId}/items/${itemId}`, data)
|
||||
.pipe(tap((list) => this.upsertCachedList(list)));
|
||||
}
|
||||
|
||||
private readCachedLists(): UserList[] {
|
||||
if (typeof localStorage === 'undefined') {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
const value = localStorage.getItem(LIST_CACHE_KEY);
|
||||
return value ? (JSON.parse(value) as UserList[]) : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
private writeCachedLists(lists: UserList[]): void {
|
||||
if (typeof localStorage === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
localStorage.setItem(LIST_CACHE_KEY, JSON.stringify(lists));
|
||||
}
|
||||
|
||||
private upsertCachedList(list: UserList): void {
|
||||
const lists = this.readCachedLists();
|
||||
const existingIndex = lists.findIndex((existingList) => existingList.id === list.id);
|
||||
|
||||
if (existingIndex === -1) {
|
||||
this.writeCachedLists([...lists, list]);
|
||||
return;
|
||||
}
|
||||
|
||||
this.writeCachedLists(
|
||||
lists.map((existingList, index) => (index === existingIndex ? list : existingList)),
|
||||
);
|
||||
}
|
||||
|
||||
private updateCachedList(
|
||||
listId: string,
|
||||
updater: (list: UserList) => UserList,
|
||||
): UserList | null {
|
||||
const lists = this.readCachedLists();
|
||||
let updatedList: UserList | null = null;
|
||||
|
||||
this.writeCachedLists(
|
||||
lists.map((list) => {
|
||||
if (list.id !== listId) {
|
||||
return list;
|
||||
}
|
||||
|
||||
updatedList = updater(list);
|
||||
return updatedList;
|
||||
}),
|
||||
);
|
||||
|
||||
return updatedList;
|
||||
}
|
||||
|
||||
private removeCachedList(listId: string): void {
|
||||
this.writeCachedLists(
|
||||
this.readCachedLists().filter((list) => list.id !== listId),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user