round68: Spaeter-lesen als Liste mit Datum/Uhrzeit statt Icon-Raster, Suche im Android-Design nutzt jetzt dieselbe Rangierfunktion wie das klassische Design

This commit is contained in:
2026-07-26 22:20:04 +02:00
parent 3e27c54199
commit 2ed39d6865

View File

@@ -10,6 +10,7 @@ import {
faPen,
} from "@fortawesome/free-solid-svg-icons";
import type { Category, Service, Bookmark, SearchResult } from "@launchpad/shared";
import { rankSearchResults } from "@launchpad/shared";
import { ResultsList, Button } from "@launchpad/ui";
import { useHomeLayout, type HomeSlot, type ItemRef } from "../hooks/useHomeLayout.js";
@@ -83,6 +84,14 @@ function slotId(): string {
return `slot-${Math.random().toString(36).slice(2, 10)}-${Date.now().toString(36)}`;
}
function formatSavedAt(iso: string): string {
const date = new Date(iso);
if (Number.isNaN(date.getTime())) return "";
const datePart = date.toLocaleDateString("de-DE", { day: "2-digit", month: "2-digit", year: "2-digit" });
const timePart = date.toLocaleTimeString("de-DE", { hour: "2-digit", minute: "2-digit" });
return `${datePart}, ${timePart}`;
}
/**
* Reine Funktion, die aus dem Ist-Zustand (Layout) + der abgeschlossenen
* Ziehgeste das NEUE Layout berechnet - bewusst ohne React-State-Zugriff,
@@ -221,7 +230,7 @@ export interface MobileAndroidHomeProps {
categoryColors: Record<string, string>;
allSearchItems: SearchResult[];
recentVisits: MinimalVisitItem[];
readLaterItems: Array<{ id: string; url: string; displayName: string; favicon: string | null }>;
readLaterItems: Array<{ id: string; url: string; displayName: string; favicon: string | null; savedAt: string }>;
onOpen: (item: OpenTarget) => void;
onToggleFavorite: (item: SearchResult) => void;
onAddReadLater: (url: string) => void;
@@ -383,12 +392,15 @@ export function MobileAndroidHome({
}
const searchResults = useMemo(() => {
if (query.trim().length === 0) return [];
const q = query.toLowerCase();
return allSearchItems
.filter((i) => i.kind !== "device")
.filter((i) => i.displayName.toLowerCase().includes(q) || i.alias?.some((a) => a.toLowerCase().includes(q)))
.slice(0, 30);
// WICHTIG (Bugfix): vorher stand hier ein eigener, simpler
// .includes()-Filter, der zusätzlich Geräte komplett ausgeschlossen hat
// - dadurch fand die Suche im Android-Design weniger als im normalen
// Design (siehe Bugreport). Jetzt exakt dieselbe Rangier-Funktion wie im
// klassischen Design (siehe HomePage.tsx), inklusive Geräten - ein Klick
// auf ein Gerät bleibt trotzdem ohne Wirkung (kein eigenes Ziel, siehe
// onOpen/openItem in HomePage.tsx), aber es taucht wie gewohnt als
// Treffer auf.
return rankSearchResults(allSearchItems, query);
}, [query, allSearchItems]);
const closeSearch = () => {
@@ -702,20 +714,25 @@ export function MobileAndroidHome({
</Button>
</form>
{readLaterItems.length > 0 ? (
<div className="grid grid-cols-4 gap-x-3 gap-y-5">
{readLaterItems.map((item) => (
<button
key={item.id}
type="button"
onClick={() => onOpen({ url: item.url, id: item.id, kind: "bookmark" })}
className="flex flex-col items-center gap-1.5"
>
<TileIcon favicon={item.favicon} label={item.displayName} />
<span className="line-clamp-2 w-full text-center text-[11px] leading-tight text-black/70 dark:text-white/70">
{item.displayName}
</span>
</button>
))}
<div className="flex flex-col divide-y divide-black/5 dark:divide-white/10">
{[...readLaterItems]
.sort((a, b) => new Date(b.savedAt).getTime() - new Date(a.savedAt).getTime())
.map((item) => (
<button
key={item.id}
type="button"
onClick={() => onOpen({ url: item.url, id: item.id, kind: "bookmark" })}
className="flex items-center gap-3 py-2.5 text-left"
>
<TileIcon favicon={item.favicon} label={item.displayName} size="small" />
<span className="min-w-0 flex-1">
<span className="block truncate text-sm text-black dark:text-white">{item.displayName}</span>
<span className="block text-[11px] text-black/40 dark:text-white/40">
{formatSavedAt(item.savedAt)}
</span>
</span>
</button>
))}
</div>
) : (
<p className="text-sm text-black/40 dark:text-white/40">Noch nichts gemerkt.</p>