generated from Dicken/dickendock
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:
@@ -10,6 +10,7 @@ import {
|
|||||||
faPen,
|
faPen,
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import type { Category, Service, Bookmark, SearchResult } from "@launchpad/shared";
|
import type { Category, Service, Bookmark, SearchResult } from "@launchpad/shared";
|
||||||
|
import { rankSearchResults } from "@launchpad/shared";
|
||||||
import { ResultsList, Button } from "@launchpad/ui";
|
import { ResultsList, Button } from "@launchpad/ui";
|
||||||
import { useHomeLayout, type HomeSlot, type ItemRef } from "../hooks/useHomeLayout.js";
|
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)}`;
|
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
|
* Reine Funktion, die aus dem Ist-Zustand (Layout) + der abgeschlossenen
|
||||||
* Ziehgeste das NEUE Layout berechnet - bewusst ohne React-State-Zugriff,
|
* Ziehgeste das NEUE Layout berechnet - bewusst ohne React-State-Zugriff,
|
||||||
@@ -221,7 +230,7 @@ export interface MobileAndroidHomeProps {
|
|||||||
categoryColors: Record<string, string>;
|
categoryColors: Record<string, string>;
|
||||||
allSearchItems: SearchResult[];
|
allSearchItems: SearchResult[];
|
||||||
recentVisits: MinimalVisitItem[];
|
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;
|
onOpen: (item: OpenTarget) => void;
|
||||||
onToggleFavorite: (item: SearchResult) => void;
|
onToggleFavorite: (item: SearchResult) => void;
|
||||||
onAddReadLater: (url: string) => void;
|
onAddReadLater: (url: string) => void;
|
||||||
@@ -383,12 +392,15 @@ export function MobileAndroidHome({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const searchResults = useMemo(() => {
|
const searchResults = useMemo(() => {
|
||||||
if (query.trim().length === 0) return [];
|
// WICHTIG (Bugfix): vorher stand hier ein eigener, simpler
|
||||||
const q = query.toLowerCase();
|
// .includes()-Filter, der zusätzlich Geräte komplett ausgeschlossen hat
|
||||||
return allSearchItems
|
// - dadurch fand die Suche im Android-Design weniger als im normalen
|
||||||
.filter((i) => i.kind !== "device")
|
// Design (siehe Bugreport). Jetzt exakt dieselbe Rangier-Funktion wie im
|
||||||
.filter((i) => i.displayName.toLowerCase().includes(q) || i.alias?.some((a) => a.toLowerCase().includes(q)))
|
// klassischen Design (siehe HomePage.tsx), inklusive Geräten - ein Klick
|
||||||
.slice(0, 30);
|
// 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]);
|
}, [query, allSearchItems]);
|
||||||
|
|
||||||
const closeSearch = () => {
|
const closeSearch = () => {
|
||||||
@@ -702,20 +714,25 @@ export function MobileAndroidHome({
|
|||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
{readLaterItems.length > 0 ? (
|
{readLaterItems.length > 0 ? (
|
||||||
<div className="grid grid-cols-4 gap-x-3 gap-y-5">
|
<div className="flex flex-col divide-y divide-black/5 dark:divide-white/10">
|
||||||
{readLaterItems.map((item) => (
|
{[...readLaterItems]
|
||||||
<button
|
.sort((a, b) => new Date(b.savedAt).getTime() - new Date(a.savedAt).getTime())
|
||||||
key={item.id}
|
.map((item) => (
|
||||||
type="button"
|
<button
|
||||||
onClick={() => onOpen({ url: item.url, id: item.id, kind: "bookmark" })}
|
key={item.id}
|
||||||
className="flex flex-col items-center gap-1.5"
|
type="button"
|
||||||
>
|
onClick={() => onOpen({ url: item.url, id: item.id, kind: "bookmark" })}
|
||||||
<TileIcon favicon={item.favicon} label={item.displayName} />
|
className="flex items-center gap-3 py-2.5 text-left"
|
||||||
<span className="line-clamp-2 w-full text-center text-[11px] leading-tight text-black/70 dark:text-white/70">
|
>
|
||||||
{item.displayName}
|
<TileIcon favicon={item.favicon} label={item.displayName} size="small" />
|
||||||
</span>
|
<span className="min-w-0 flex-1">
|
||||||
</button>
|
<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>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<p className="text-sm text-black/40 dark:text-white/40">Noch nichts gemerkt.</p>
|
<p className="text-sm text-black/40 dark:text-white/40">Noch nichts gemerkt.</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user