Sprawdzone lokalizacje w Historii cen zapisane na koncie
Deploy Polska Lokalnie / deploy (push) Successful in 12s
Deploy Polska Lokalnie / deploy (push) Successful in 12s
Lista ostatnio sprawdzonych lokalizacji zyla w localStorage, wiec ginela przy zmianie urzadzenia. Teraz jest polem ustawien konta (user_settings.price_history_markets), a wpisy z przegladarki przenosimy raz przy pierwszym wejsciu. To historia przegladania, nie alerty - nic z niej nie wysyla powiadomien. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -114,6 +114,14 @@ public class UserSettings {
|
|||||||
@ColumnDefault("false")
|
@ColumnDefault("false")
|
||||||
private boolean notifyProductNews = false;
|
private boolean notifyProductNews = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lokalizacje ostatnio sprawdzone w Historii cen (JSON, maks. kilka pozycji). Lista jest
|
||||||
|
* na koncie, a nie w przegladarce, wiec porownanie cen wyglada tak samo na kazdym urzadzeniu.
|
||||||
|
* To zwykla historia przegladania - nie ma z nia zwiazanych zadnych powiadomien.
|
||||||
|
*/
|
||||||
|
@Column(name = "price_history_markets", columnDefinition = "text")
|
||||||
|
private String priceHistoryMarkets;
|
||||||
|
|
||||||
// --- Preferencje wyszukiwania (podpowiadane przy filtrowaniu ofert) ---
|
// --- Preferencje wyszukiwania (podpowiadane przy filtrowaniu ofert) ---
|
||||||
|
|
||||||
@Column(length = 255)
|
@Column(length = 255)
|
||||||
@@ -314,6 +322,14 @@ public class UserSettings {
|
|||||||
return searchLocations;
|
return searchLocations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getPriceHistoryMarkets() {
|
||||||
|
return priceHistoryMarkets;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPriceHistoryMarkets(String priceHistoryMarkets) {
|
||||||
|
this.priceHistoryMarkets = priceHistoryMarkets;
|
||||||
|
}
|
||||||
|
|
||||||
public void setSearchLocations(String searchLocations) {
|
public void setSearchLocations(String searchLocations) {
|
||||||
this.searchLocations = searchLocations;
|
this.searchLocations = searchLocations;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ public record UserSettingsRequest(
|
|||||||
Boolean notifyPriceAlerts,
|
Boolean notifyPriceAlerts,
|
||||||
Boolean notifyMessages,
|
Boolean notifyMessages,
|
||||||
Boolean notifyProductNews,
|
Boolean notifyProductNews,
|
||||||
|
String priceHistoryMarkets,
|
||||||
String searchLocations,
|
String searchLocations,
|
||||||
String searchPropertyType,
|
String searchPropertyType,
|
||||||
Integer searchBudgetMax,
|
Integer searchBudgetMax,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ public record UserSettingsResponse(
|
|||||||
boolean notifyPriceAlerts,
|
boolean notifyPriceAlerts,
|
||||||
boolean notifyMessages,
|
boolean notifyMessages,
|
||||||
boolean notifyProductNews,
|
boolean notifyProductNews,
|
||||||
|
String priceHistoryMarkets,
|
||||||
String searchLocations,
|
String searchLocations,
|
||||||
String searchPropertyType,
|
String searchPropertyType,
|
||||||
Integer searchBudgetMax,
|
Integer searchBudgetMax,
|
||||||
@@ -52,6 +53,7 @@ public record UserSettingsResponse(
|
|||||||
settings.isNotifyPriceAlerts(),
|
settings.isNotifyPriceAlerts(),
|
||||||
settings.isNotifyMessages(),
|
settings.isNotifyMessages(),
|
||||||
settings.isNotifyProductNews(),
|
settings.isNotifyProductNews(),
|
||||||
|
settings.getPriceHistoryMarkets(),
|
||||||
settings.getSearchLocations(),
|
settings.getSearchLocations(),
|
||||||
settings.getSearchPropertyType(),
|
settings.getSearchPropertyType(),
|
||||||
settings.getSearchBudgetMax(),
|
settings.getSearchBudgetMax(),
|
||||||
|
|||||||
@@ -135,6 +135,10 @@ public class UserSettingsService {
|
|||||||
if (request.notifyProductNews() != null) {
|
if (request.notifyProductNews() != null) {
|
||||||
settings.setNotifyProductNews(request.notifyProductNews());
|
settings.setNotifyProductNews(request.notifyProductNews());
|
||||||
}
|
}
|
||||||
|
if (request.priceHistoryMarkets() != null) {
|
||||||
|
String markets = request.priceHistoryMarkets().trim();
|
||||||
|
settings.setPriceHistoryMarkets(markets.isEmpty() ? null : markets);
|
||||||
|
}
|
||||||
if (request.searchLocations() != null) {
|
if (request.searchLocations() != null) {
|
||||||
String locations = request.searchLocations().trim();
|
String locations = request.searchLocations().trim();
|
||||||
textModerationService.validateOrThrow(locations);
|
textModerationService.validateOrThrow(locations);
|
||||||
|
|||||||
+51
-7
@@ -444,7 +444,27 @@ function toSavedSearchPayload(search: SavedSearchRecord | SavedSearchDraft) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
// Lokalizacje, ktorych cene za m2 sprawdzil uzytkownik - najnowsza pierwsza, przechowywane per konto.
|
// Lokalizacje, ktorych cene za m2 sprawdzil uzytkownik - najnowsza pierwsza, przechowywane per konto.
|
||||||
|
// Klucz zostaje tylko po to, zeby raz przeniesc na konto lokalizacje zapisane w przegladarce
|
||||||
|
// przed przeniesieniem tej listy na serwer.
|
||||||
const PRICE_HISTORY_MARKETS_SUFFIX = 'price-history-markets';
|
const PRICE_HISTORY_MARKETS_SUFFIX = 'price-history-markets';
|
||||||
|
|
||||||
|
function parsePriceHistoryMarkets(raw: string | null): PriceHistoryMarket[] {
|
||||||
|
if (!raw) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(raw);
|
||||||
|
return Array.isArray(parsed) ? parsed.filter((item) => item && typeof item.key === 'string') : [];
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Lista lokalizacji z Historii cen jest ustawieniem konta - zapisujemy ja przy kazdej zmianie. */
|
||||||
|
function savePriceHistoryMarkets(markets: PriceHistoryMarket[]): void {
|
||||||
|
apiFetch<unknown>('/me/settings', { method: 'PUT', body: JSON.stringify({ priceHistoryMarkets: JSON.stringify(markets) }) })
|
||||||
|
.catch(() => { /* brak sieci - lista wroci do stanu z konta przy nastepnym wejsciu */ });
|
||||||
|
}
|
||||||
const PRICE_HISTORY_MARKETS_LIMIT = 5;
|
const PRICE_HISTORY_MARKETS_LIMIT = 5;
|
||||||
const PRICE_ALERT_EVENTS_LIMIT = 40;
|
const PRICE_ALERT_EVENTS_LIMIT = 40;
|
||||||
const SAVED_SEARCHES_LIMIT = 60;
|
const SAVED_SEARCHES_LIMIT = 60;
|
||||||
@@ -21484,9 +21504,33 @@ function PriceHistoryPage({ user }: { user: AuthUser | null }) {
|
|||||||
const [suggestLoading, setSuggestLoading] = useState(false);
|
const [suggestLoading, setSuggestLoading] = useState(false);
|
||||||
const [hoveredHistoryIndex, setHoveredHistoryIndex] = useState<number | null>(null);
|
const [hoveredHistoryIndex, setHoveredHistoryIndex] = useState<number | null>(null);
|
||||||
// Historia sprawdzonych lokalizacji tego konta - najnowsza na poczatku, maksymalnie 5 pozycji.
|
// Historia sprawdzonych lokalizacji tego konta - najnowsza na poczatku, maksymalnie 5 pozycji.
|
||||||
const [recentMarkets, setRecentMarkets] = useState<PriceHistoryMarket[]>(
|
// Lista siedzi w ustawieniach konta (/api/me/settings), wiec porownanie wyglada tak samo
|
||||||
() => readUserJsonStorage<PriceHistoryMarket[]>(user, PRICE_HISTORY_MARKETS_SUFFIX, []),
|
// na kazdym urzadzeniu. Wpisy z przegladarki przenosimy raz, przy pierwszym wejsciu.
|
||||||
);
|
const [recentMarkets, setRecentMarkets] = useState<PriceHistoryMarket[]>([]);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!user) {
|
||||||
|
setRecentMarkets([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let cancelled = false;
|
||||||
|
const local = readUserJsonStorage<PriceHistoryMarket[]>(user, PRICE_HISTORY_MARKETS_SUFFIX, []);
|
||||||
|
apiFetch<{ priceHistoryMarkets: string | null }>('/me/settings')
|
||||||
|
.then((settings) => {
|
||||||
|
if (cancelled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const fromAccount = parsePriceHistoryMarkets(settings.priceHistoryMarkets);
|
||||||
|
if (fromAccount.length === 0 && local.length > 0) {
|
||||||
|
setRecentMarkets(local);
|
||||||
|
savePriceHistoryMarkets(local);
|
||||||
|
} else {
|
||||||
|
setRecentMarkets(fromAccount);
|
||||||
|
}
|
||||||
|
writeUserJsonStorage<PriceHistoryMarket[]>(user, PRICE_HISTORY_MARKETS_SUFFIX, []);
|
||||||
|
})
|
||||||
|
.catch(() => { if (!cancelled) { setRecentMarkets(local); } });
|
||||||
|
return () => { cancelled = true; };
|
||||||
|
}, [user]);
|
||||||
// Ostatnio otwarte oferte (sprzedaz i wynajem) - wspolny rejestr zasilany przy wejsciu na oferte.
|
// Ostatnio otwarte oferte (sprzedaz i wynajem) - wspolny rejestr zasilany przy wejsciu na oferte.
|
||||||
const recentListings = useMemo(() => readRecentlyViewed(user).slice(0, RECENTLY_VIEWED_PANEL_LIMIT), [user]);
|
const recentListings = useMemo(() => readRecentlyViewed(user).slice(0, RECENTLY_VIEWED_PANEL_LIMIT), [user]);
|
||||||
const skipNextFetch = useRef(false);
|
const skipNextFetch = useRef(false);
|
||||||
@@ -21719,7 +21763,7 @@ function PriceHistoryPage({ user }: { user: AuthUser | null }) {
|
|||||||
// Nowa lokalizacja wchodzi na poczatek listy, najstarsza wypada po przekroczeniu limitu.
|
// Nowa lokalizacja wchodzi na poczatek listy, najstarsza wypada po przekroczeniu limitu.
|
||||||
setRecentMarkets((current) => {
|
setRecentMarkets((current) => {
|
||||||
const next = [market, ...current.filter((item) => item.key !== market.key)].slice(0, PRICE_HISTORY_MARKETS_LIMIT);
|
const next = [market, ...current.filter((item) => item.key !== market.key)].slice(0, PRICE_HISTORY_MARKETS_LIMIT);
|
||||||
writeUserJsonStorage(user, PRICE_HISTORY_MARKETS_SUFFIX, next);
|
savePriceHistoryMarkets(next);
|
||||||
return next;
|
return next;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -21729,18 +21773,18 @@ function PriceHistoryPage({ user }: { user: AuthUser | null }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Usuwanie pojedynczej lokalizacji z porownania i czyszczenie calej listy - zapis leci od razu
|
// Usuwanie pojedynczej lokalizacji z porownania i czyszczenie calej listy - zapis leci od razu
|
||||||
// do pamieci konta, zeby stan zgadzal sie po odswiezeniu strony.
|
// na konto, zeby stan zgadzal sie po odswiezeniu i na innym urzadzeniu.
|
||||||
const removeComparison = (marketKey: string) => {
|
const removeComparison = (marketKey: string) => {
|
||||||
setRecentMarkets((current) => {
|
setRecentMarkets((current) => {
|
||||||
const next = current.filter((item) => item.key !== marketKey);
|
const next = current.filter((item) => item.key !== marketKey);
|
||||||
writeUserJsonStorage(user, PRICE_HISTORY_MARKETS_SUFFIX, next);
|
savePriceHistoryMarkets(next);
|
||||||
return next;
|
return next;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearComparisons = () => {
|
const clearComparisons = () => {
|
||||||
setRecentMarkets([]);
|
setRecentMarkets([]);
|
||||||
writeUserJsonStorage(user, PRICE_HISTORY_MARKETS_SUFFIX, []);
|
savePriceHistoryMarkets([]);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Wejscie z adresu z parametrami (np. "Zobacz historie cen" przy alercie lokalizacji)
|
// Wejscie z adresu z parametrami (np. "Zobacz historie cen" przy alercie lokalizacji)
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ export type UserSettings = {
|
|||||||
notifyPriceAlerts: boolean;
|
notifyPriceAlerts: boolean;
|
||||||
notifyMessages: boolean;
|
notifyMessages: boolean;
|
||||||
notifyProductNews: boolean;
|
notifyProductNews: boolean;
|
||||||
|
// Lokalizacje sprawdzone w Historii cen (JSON) - historia przegladania, bez powiadomien.
|
||||||
|
priceHistoryMarkets: string | null;
|
||||||
searchLocations: string | null;
|
searchLocations: string | null;
|
||||||
searchPropertyType: string | null;
|
searchPropertyType: string | null;
|
||||||
searchBudgetMax: number | null;
|
searchBudgetMax: number | null;
|
||||||
|
|||||||
@@ -597,3 +597,25 @@ test.describe('Ulubione', () => {
|
|||||||
await expect(page.locator('main')).not.toContainText(title.trim().slice(0, 20));
|
await expect(page.locator('main')).not.toContainText(title.trim().slice(0, 20));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Lista sprawdzonych lokalizacji w Historii cen jest ustawieniem konta, nie pamiecia przegladarki.
|
||||||
|
test.describe('Historia cen', () => {
|
||||||
|
test('sprawdzone lokalizacje przezywaja wyczyszczenie pamieci przegladarki', async ({ page }) => {
|
||||||
|
await login(page);
|
||||||
|
await goto(page, ROUTES.priceHistory);
|
||||||
|
await page.locator('.history-location-display').click();
|
||||||
|
await page.locator('.history-location-pop input').fill('Kraków');
|
||||||
|
await page.locator('.history-location-pop button', { hasText: 'Kraków' }).first().click();
|
||||||
|
await expect(page.locator('main')).toContainText('Kraków');
|
||||||
|
|
||||||
|
await page.evaluate(() => {
|
||||||
|
const token = window.localStorage.getItem('polskalokalnie-auth-token');
|
||||||
|
window.localStorage.clear();
|
||||||
|
if (token) {
|
||||||
|
window.localStorage.setItem('polskalokalnie-auth-token', token);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await goto(page, ROUTES.priceHistory);
|
||||||
|
await expect(page.locator('main')).toContainText('Kraków');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user