SPB Git forge

spb/house-ka

Public
18commits 1branches 0releases
1.9 MBsize
maindefault branch
19 days agolast push
Python 67% TypeScript 18.2% CSS 14.4%
3.0 KB · 83 lines tsx
Raw Blame History
1// -----------------------------------------------------------------------------2// House-Ka — Homes-for-sale aggregator (Canada outside Québec)3// Author: Simon-Pierre Boucher — contact@spboucher.ai4// account.tsx : shared account context — signed-in profile + ♥ favourites.5//   One fetch when the app mounts; the listing page, header and account page6//   all read the same state. Favourites live at the Groupe KA HUB (central7//   "My Ka universe" store): each ♥ is optimistic on screen and pushed to the8//   server, which relays to the hub. Signed out → ♥ redirects to KA ID login.9// -----------------------------------------------------------------------------10import {11  ReactNode, createContext, useCallback, useContext, useEffect, useState,12} from "react";13import {14  Listing, User, favItemFromListing, fetchFavorites, fetchMe, toggleFavorite,15} from "./api";1617interface AccountState {18  user: User | null;19  enabled: boolean;                   // SSO configured server-side20  loaded: boolean;                    // first fetch done21  favs: Set<string>;                  // favourite uids (hub mirror)22  toggleFav: (l: Listing) => void;23  refresh: () => void;                // re-fetch profile + favourites (after login)24}2526const Ctx = createContext<AccountState>({27  user: null, enabled: false, loaded: false, favs: new Set(),28  toggleFav: () => {}, refresh: () => {},29});3031export const useAccount = () => useContext(Ctx);3233/** Redirects to the KA ID login, returning to the current page. */34export function kaLogin() {35  const next = window.location.pathname + window.location.search;36  window.location.href = `/api/auth/ka/login?next=${encodeURIComponent(next)}`;37}3839export function AccountProvider({ children }: { children: ReactNode }) {40  const [user, setUser] = useState<User | null>(null);41  const [enabled, setEnabled] = useState(false);42  const [loaded, setLoaded] = useState(false);43  const [favs, setFavs] = useState<Set<string>>(new Set());4445  const refresh = useCallback(() => {46    fetchMe()47      .then((r) => {48        setUser(r.user);49        setEnabled(r.enabled !== false);50        setLoaded(true);51        if (r.user && r.user.ka_id) {52          fetchFavorites()53            .then((f) => setFavs(new Set(f.ids)))54            .catch(() => setFavs(new Set()));55        } else {56          setFavs(new Set());57        }58      })59      .catch(() => { setUser(null); setLoaded(true); });60  }, []);6162  useEffect(() => { refresh(); }, [refresh]);6364  const toggleFav = useCallback((l: Listing) => {65    if (!user || !user.ka_id) { kaLogin(); return; }   // ♥ = KA account required66    setFavs((prev) => {67      const next = new Set(prev);68      const on = !next.has(l.uid);69      if (on) next.add(l.uid); else next.delete(l.uid);70      toggleFavorite(on, favItemFromListing(l)).catch((e) => {71        if (String(e).includes("401")) kaLogin();      // session expired72      });73      return next;74    });75  }, [user]);7677  return (78    <Ctx.Provider value={{ user, enabled, loaded, favs, toggleFav, refresh }}>79      {children}80    </Ctx.Provider>81  );82}83