Ulubione na koncie - koniec kasowania zapisow przez pusta przegladarke
Deploy Polska Lokalnie / deploy (push) Successful in 12s

Dotad front po kazdej zmianie dyktowal serwerowi cala liste ulubionych.
Zalogowanie sie na innym urzadzeniu wysylalo "mam zero ulubionych"
i kasowalo zapisy, przez co wlasciciel ogloszenia tracil licznik zapisan.

Teraz: GET /api/listings/favorites zwraca ulubione konta, dodanie
i usuniecie ida pojedynczo (POST/DELETE /{id}/favorite), a wpisy z
przegladarki dopisuje jednorazowy /favorites/import. Endpoint
/favorites/sync usuniety razem z jego jedynym wywolaniem.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-23 11:26:00 +02:00
parent b9fecdea85
commit 557c76257b
3 changed files with 114 additions and 45 deletions
+51 -21
View File
@@ -2782,33 +2782,40 @@ function App() {
}, [user]);
useEffect(() => {
setFavoriteListingsState(readUserJsonStorage<FavoriteListing[]>(user, FAVORITE_LISTINGS_STORAGE_SUFFIX, []));
setFavoritesPreferredTab(readUserJsonStorage<FavoriteTab>(user, FAVORITES_PREFERRED_TAB_STORAGE_SUFFIX, 'buy'));
setListingPriceAlertSubscriptionsState(readUserJsonStorage<ListingPriceAlertSubscription[]>(user, LISTING_PRICE_ALERTS_STORAGE_SUFFIX, []));
}, [user]);
useEffect(() => {
writeUserJsonStorage(user, FAVORITE_LISTINGS_STORAGE_SUFFIX, favoriteListingsState);
}, [favoriteListingsState, user]);
// Ulubione zyja lokalnie (szybkie UI), ale wlasciciel ogloszenia musi widziec, ile osob je zapisalo.
// Po kazdej zmianie wysylamy caly zbior - dziala niezaleznie od tego, gdzie uzytkownik kliknal serce.
// Ulubione trzyma konto (/api/listings/favorites), wiec sa te same na kazdym urzadzeniu.
// Wpisy zapisane wczesniej w przegladarce dopisujemy raz przy pierwszym wejsciu.
useEffect(() => {
if (!user) {
setFavoriteListingsState([]);
return;
}
const listingIds = favoriteListingsState
.map((favorite) => favorite.listingId)
.filter((listingId): listingId is number => typeof listingId === 'number');
let cancelled = false;
const local = readUserJsonStorage<FavoriteListing[]>(user, FAVORITE_LISTINGS_STORAGE_SUFFIX, []);
const localIds = local.map((item) => item.listingId).filter((id): id is number => typeof id === 'number');
const load = localIds.length > 0
? apiFetch<ApiListingSummary[]>('/listings/favorites/import', { method: 'POST', body: JSON.stringify({ listingIds: localIds }) })
.then((items) => {
writeUserJsonStorage<FavoriteListing[]>(user, FAVORITE_LISTINGS_STORAGE_SUFFIX, []);
return items;
})
: apiFetch<ApiListingSummary[]>('/listings/favorites');
load
.then((items) => { if (!cancelled) { setFavoriteListingsState(items.map(toFavoriteListingFromSummary)); } })
.catch(() => { if (!cancelled) { setFavoriteListingsState([]); } });
return () => { cancelled = true; };
}, [user]);
apiFetch<void>('/listings/favorites/sync', {
method: 'POST',
body: JSON.stringify({ listingIds }),
}).catch(() => {
// Brak polaczenia - licznik zapisan dogoni stan przy nastepnej zmianie ulubionych.
});
}, [favoriteListingsState, user]);
// Zapis i usuniecie ulubionego ida na serwer pojedynczo - stan przegladarki nigdy nie
// dyktuje serwerowi calej listy, wiec wejscie z nowego urzadzenia niczego nie kasuje.
const saveFavoriteOnServer = (listingId: number, add: boolean) => {
apiFetch<void>(`/listings/${listingId}/favorite`, { method: add ? 'POST' : 'DELETE' })
.catch(() => { /* brak sieci - zmiana wroci przy nastepnym wejsciu na konto */ });
};
useEffect(() => {
writeUserJsonStorage(user, FAVORITES_PREFERRED_TAB_STORAGE_SUFFIX, favoritesPreferredTab);
@@ -3241,6 +3248,7 @@ function App() {
const toggleListingFavorite = (listing: ApiListingDetail) => {
const listingKey = favoriteListingKey(listing.id);
const alreadyFavorite = favoriteListingsState.some((item) => item.id === listingKey);
saveFavoriteOnServer(listing.id, !alreadyFavorite);
if (!alreadyFavorite) {
notificationsCenter.notify({
category: 'listing',
@@ -3332,6 +3340,7 @@ function App() {
const toggleSummaryFavorite = async (listing: ApiListingSummary) => {
const listingKey = favoriteListingKey(listing.id);
const exists = favoriteListingsState.some((item) => item.id === listingKey);
saveFavoriteOnServer(listing.id, !exists);
if (exists) {
setUnreadFavoriteIds((prev) => {
if (!prev.has(listingKey)) {
@@ -6497,9 +6506,19 @@ function AccountSidebar() {
.catch(() => { if (!cancelled) { setSavedSearchCount(0); } });
return () => { cancelled = true; };
}, [user, location.pathname]);
const favoritesCount = user
? readUserJsonStorage<FavoriteListing[]>(user, FAVORITE_LISTINGS_STORAGE_SUFFIX, []).length
: 0;
// Ulubione sa na koncie - licznik bierzemy z serwera, tak jak liczbe ofert i wyszukiwan.
const [favoritesCount, setFavoritesCount] = useState(0);
useEffect(() => {
if (!user) {
setFavoritesCount(0);
return;
}
let cancelled = false;
apiFetch<ApiListingSummary[]>('/listings/favorites')
.then((items) => { if (!cancelled) { setFavoritesCount(items.length); } })
.catch(() => { if (!cancelled) { setFavoritesCount(0); } });
return () => { cancelled = true; };
}, [user, location.pathname]);
// Licznik obejmuje oba rodzaje alertow - lokalizacji i pojedynczych ogloszen -
// dokladnie tak, jak lista na /konto/alerty-cenowe.
const priceAlertsCount = user
@@ -26297,7 +26316,17 @@ function FavoritesPage({
return () => window.clearTimeout(timeoutId);
}, [favoritesFeedback]);
// Usuniecie idzie takze na serwer - ulubione sa na koncie, nie w tej jednej przegladarce.
const forgetOnServer = (listingId: number) => {
apiFetch<void>(`/listings/${listingId}/favorite`, { method: 'DELETE' })
.catch(() => { /* brak sieci - wpis wroci przy nastepnym wejsciu */ });
};
const removeFavoriteListing = (id: string) => {
const removed = favoritesState.find((listing) => listing.id === id);
if (removed) {
forgetOnServer(removed.listingId);
}
setFavoritesState((current) => current.filter((listing) => listing.id !== id));
setMenuListingId((current) => (current === id ? null : current));
setFavoritesFeedback('Usunięto ogłoszenie z ulubionych.');
@@ -26363,6 +26392,7 @@ function FavoritesPage({
return;
}
favoritesState.forEach((listing) => forgetOnServer(listing.listingId));
setFavoritesState([]);
setMenuListingId(null);
setFavoritesFeedback('Usunięto wszystkie ogłoszenia z ulubionych.');