generated from Dicken/dickendock
round70: Icons ruckeln nicht mehr nach - Raster wartet auf alle Datenquellen bevor es rendert
This commit is contained in:
@@ -235,6 +235,14 @@ export interface MobileAndroidHomeProps {
|
|||||||
onToggleFavorite: (item: SearchResult) => void;
|
onToggleFavorite: (item: SearchResult) => void;
|
||||||
onAddReadLater: (url: string) => void;
|
onAddReadLater: (url: string) => void;
|
||||||
isOnline: boolean;
|
isOnline: boolean;
|
||||||
|
/**
|
||||||
|
* Wird erst true, wenn ALLE für das Raster nötigen Daten (Dienste,
|
||||||
|
* Lesezeichen, Kategorien, Zuletzt-besucht, Einstellungen/Layout)
|
||||||
|
* mindestens einmal geladen sind - siehe Kommentar in HomePage.tsx. Bis
|
||||||
|
* dahin wird ein einfacher Platzhalter statt des Rasters gezeigt, damit
|
||||||
|
* nicht einzelne Kacheln nacheinander "nachrücken".
|
||||||
|
*/
|
||||||
|
isReady: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MobileAndroidHome({
|
export function MobileAndroidHome({
|
||||||
@@ -249,6 +257,7 @@ export function MobileAndroidHome({
|
|||||||
onToggleFavorite,
|
onToggleFavorite,
|
||||||
onAddReadLater,
|
onAddReadLater,
|
||||||
isOnline,
|
isOnline,
|
||||||
|
isReady,
|
||||||
}: MobileAndroidHomeProps) {
|
}: MobileAndroidHomeProps) {
|
||||||
const [searchOpen, setSearchOpen] = useState(false);
|
const [searchOpen, setSearchOpen] = useState(false);
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
@@ -537,46 +546,63 @@ export function MobileAndroidHome({
|
|||||||
|
|
||||||
{/* Haupt-Raster */}
|
{/* Haupt-Raster */}
|
||||||
<div data-drop-area="grid" className="min-h-[40vh] px-4 pb-32 pt-2">
|
<div data-drop-area="grid" className="min-h-[40vh] px-4 pb-32 pt-2">
|
||||||
<div className="grid grid-cols-4 gap-x-3 gap-y-5">
|
{isReady ? (
|
||||||
{recentVisits.length > 0 ? (
|
<>
|
||||||
<button
|
<div className="grid grid-cols-4 gap-x-3 gap-y-5">
|
||||||
type="button"
|
{recentVisits.length > 0 ? (
|
||||||
onClick={() => setOpenFolderId("__recent__")}
|
<button
|
||||||
className="flex flex-col items-center gap-1.5"
|
type="button"
|
||||||
>
|
onClick={() => setOpenFolderId("__recent__")}
|
||||||
<span
|
className="flex flex-col items-center gap-1.5"
|
||||||
className="flex h-14 w-14 items-center justify-center rounded-[20px] bg-white/60 text-black/50
|
>
|
||||||
shadow-[0_2px_10px_rgba(0,0,0,0.18)] ring-1 ring-white/40 backdrop-blur-sm
|
<span
|
||||||
dark:bg-white/10 dark:text-white/50 dark:ring-white/10"
|
className="flex h-14 w-14 items-center justify-center rounded-[20px] bg-white/60 text-black/50
|
||||||
>
|
shadow-[0_2px_10px_rgba(0,0,0,0.18)] ring-1 ring-white/40 backdrop-blur-sm
|
||||||
<FontAwesomeIcon icon={faClockRotateLeft} />
|
dark:bg-white/10 dark:text-white/50 dark:ring-white/10"
|
||||||
</span>
|
>
|
||||||
<span className="line-clamp-2 w-full text-center text-[11px] leading-tight text-black/70 dark:text-white/70">
|
<FontAwesomeIcon icon={faClockRotateLeft} />
|
||||||
Zuletzt besucht
|
</span>
|
||||||
</span>
|
<span className="line-clamp-2 w-full text-center text-[11px] leading-tight text-black/70 dark:text-white/70">
|
||||||
</button>
|
Zuletzt besucht
|
||||||
) : null}
|
</span>
|
||||||
<button type="button" onClick={() => setOpenFolderId("__readlater__")} className="flex flex-col items-center gap-1.5">
|
</button>
|
||||||
<span
|
) : null}
|
||||||
className="flex h-14 w-14 items-center justify-center rounded-[20px] bg-white/60 text-black/50
|
<button type="button" onClick={() => setOpenFolderId("__readlater__")} className="flex flex-col items-center gap-1.5">
|
||||||
shadow-[0_2px_10px_rgba(0,0,0,0.18)] ring-1 ring-white/40 backdrop-blur-sm
|
<span
|
||||||
dark:bg-white/10 dark:text-white/50 dark:ring-white/10"
|
className="flex h-14 w-14 items-center justify-center rounded-[20px] bg-white/60 text-black/50
|
||||||
>
|
shadow-[0_2px_10px_rgba(0,0,0,0.18)] ring-1 ring-white/40 backdrop-blur-sm
|
||||||
<FontAwesomeIcon icon={faBookmark} />
|
dark:bg-white/10 dark:text-white/50 dark:ring-white/10"
|
||||||
</span>
|
>
|
||||||
<span className="line-clamp-2 w-full text-center text-[11px] leading-tight text-black/70 dark:text-white/70">
|
<FontAwesomeIcon icon={faBookmark} />
|
||||||
Später lesen
|
</span>
|
||||||
</span>
|
<span className="line-clamp-2 w-full text-center text-[11px] leading-tight text-black/70 dark:text-white/70">
|
||||||
</button>
|
Später lesen
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
{layout.grid.map((slot) => renderTile(slot, "grid"))}
|
{layout.grid.map((slot) => renderTile(slot, "grid"))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{layout.grid.length === 0 ? (
|
{layout.grid.length === 0 ? (
|
||||||
<p className="mt-8 text-center text-sm text-black/40 dark:text-white/40">
|
<p className="mt-8 text-center text-sm text-black/40 dark:text-white/40">
|
||||||
Markiere Dienste/Lesezeichen im Adminbereich als Favorit – sie erscheinen dann hier.
|
Markiere Dienste/Lesezeichen im Adminbereich als Favorit – sie erscheinen dann hier.
|
||||||
</p>
|
</p>
|
||||||
) : null}
|
) : null}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
// Platzhalter statt des Rasters, solange noch nicht ALLE nötigen
|
||||||
|
// Daten da sind (siehe isReady-Kommentar an der Prop weiter oben) -
|
||||||
|
// verhindert, dass z. B. "Zuletzt besucht" erst nachträglich
|
||||||
|
// auftaucht und alle Icons danach verschiebt (siehe Bugreport).
|
||||||
|
<div className="grid grid-cols-4 gap-x-3 gap-y-5" aria-hidden="true">
|
||||||
|
{Array.from({ length: 8 }).map((_, i) => (
|
||||||
|
<div key={i} className="flex flex-col items-center gap-1.5">
|
||||||
|
<div className="h-14 w-14 animate-pulse rounded-[20px] bg-black/10 dark:bg-white/10" />
|
||||||
|
<div className="h-2.5 w-10 animate-pulse rounded bg-black/10 dark:bg-white/10" />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Dock */}
|
{/* Dock */}
|
||||||
@@ -588,7 +614,9 @@ export function MobileAndroidHome({
|
|||||||
className="flex min-h-[4.5rem] min-w-[5rem] items-center gap-3 rounded-[28px] border border-white/40
|
className="flex min-h-[4.5rem] min-w-[5rem] items-center gap-3 rounded-[28px] border border-white/40
|
||||||
bg-white/70 px-4 py-3 shadow-xl backdrop-blur-xl dark:border-white/10 dark:bg-neutral-900/70"
|
bg-white/70 px-4 py-3 shadow-xl backdrop-blur-xl dark:border-white/10 dark:bg-neutral-900/70"
|
||||||
>
|
>
|
||||||
{layout.dock.length === 0 ? (
|
{!isReady ? (
|
||||||
|
<div className="h-9 w-9 animate-pulse rounded-xl bg-black/10 dark:bg-white/10" aria-hidden="true" />
|
||||||
|
) : layout.dock.length === 0 ? (
|
||||||
<span className="px-2 text-xs text-black/30 dark:text-white/30">Icon hierher ziehen zum Anheften</span>
|
<span className="px-2 text-xs text-black/30 dark:text-white/30">Icon hierher ziehen zum Anheften</span>
|
||||||
) : (
|
) : (
|
||||||
layout.dock.map((slot) => renderTile(slot, "dock", "small"))
|
layout.dock.map((slot) => renderTile(slot, "dock", "small"))
|
||||||
|
|||||||
@@ -128,9 +128,9 @@ export function HomePage() {
|
|||||||
const { data: services, isLoading: servicesLoading, isError: servicesError } = useServices();
|
const { data: services, isLoading: servicesLoading, isError: servicesError } = useServices();
|
||||||
const { data: bookmarks, isLoading: bookmarksLoading } = useBookmarks();
|
const { data: bookmarks, isLoading: bookmarksLoading } = useBookmarks();
|
||||||
const { data: devices } = useDevices();
|
const { data: devices } = useDevices();
|
||||||
const { data: settings } = useSettings();
|
const { data: settings, isLoading: settingsLoading } = useSettings();
|
||||||
const { data: categories } = useCategories();
|
const { data: categories, isLoading: categoriesLoading } = useCategories();
|
||||||
const { data: recentVisits } = useRecentVisits();
|
const { data: recentVisits, isLoading: recentVisitsLoading } = useRecentVisits();
|
||||||
const { data: readLaterItems } = useReadLater();
|
const { data: readLaterItems } = useReadLater();
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
const searchContainerRef = useRef<HTMLDivElement>(null);
|
const searchContainerRef = useRef<HTMLDivElement>(null);
|
||||||
@@ -334,6 +334,17 @@ export function HomePage() {
|
|||||||
}, [settings?.mobileHomeStyle]);
|
}, [settings?.mobileHomeStyle]);
|
||||||
const effectiveMobileHomeStyle = settings?.mobileHomeStyle ?? cachedMobileHomeStyle ?? "classic";
|
const effectiveMobileHomeStyle = settings?.mobileHomeStyle ?? cachedMobileHomeStyle ?? "classic";
|
||||||
const useAndroidHome = isMobile && effectiveMobileHomeStyle === "android";
|
const useAndroidHome = isMobile && effectiveMobileHomeStyle === "android";
|
||||||
|
// WICHTIG: Dienste/Lesezeichen/Kategorien/"Zuletzt besucht"/Einstellungen
|
||||||
|
// laden über getrennte, unabhängig voneinander abgeschlossene
|
||||||
|
// Netzwerk-Anfragen - kämen sie einzeln nacheinander im Raster an, würde
|
||||||
|
// z. B. die "Zuletzt besucht"-Kachel nachträglich auftauchen und alle
|
||||||
|
// Icons danach verschieben (genau das im Bugreport beschriebene
|
||||||
|
// "Verschieben, weil manche Icons schneller da sind als andere"). Daher
|
||||||
|
// wird das komplette Raster erst gerendert, wenn ALLE beteiligten
|
||||||
|
// Abfragen mindestens einmal durch sind - dann in einem Rutsch, ohne
|
||||||
|
// Nachrücken einzelner Kacheln.
|
||||||
|
const androidHomeDataReady =
|
||||||
|
!servicesLoading && !bookmarksLoading && !categoriesLoading && !recentVisitsLoading && !settingsLoading;
|
||||||
|
|
||||||
const addReadLaterMutation = useMutation({
|
const addReadLaterMutation = useMutation({
|
||||||
mutationFn: (url: string) => saveReadLaterRequest(url),
|
mutationFn: (url: string) => saveReadLaterRequest(url),
|
||||||
@@ -354,6 +365,7 @@ export function HomePage() {
|
|||||||
onToggleFavorite={(item) => toggleFavorite.mutate(item)}
|
onToggleFavorite={(item) => toggleFavorite.mutate(item)}
|
||||||
onAddReadLater={(url) => addReadLaterMutation.mutate(url)}
|
onAddReadLater={(url) => addReadLaterMutation.mutate(url)}
|
||||||
isOnline={isOnline}
|
isOnline={isOnline}
|
||||||
|
isReady={androidHomeDataReady}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user