Add mobile memo list density

This commit is contained in:
yoma committed 2026-07-09 03:52:53 +08:00
1 parent f4683457ee
commit f49d113b7b
2 files changed
+64 -5

No files matched your search

+12
View File
@@ -0,0 +1,12 @@
import AsyncStorage from "@react-native-async-storage/async-storage";
const MEMO_LIST_DENSITY_KEY = "edgeever.mobile.memoListDensity";
export type MobileMemoListDensity = "preview" | "compact";
export const readMobileMemoListDensity = async (): Promise<MobileMemoListDensity> => {
const value = await AsyncStorage.getItem(MEMO_LIST_DENSITY_KEY);
return value === "compact" ? "compact" : "preview";
};
export const writeMobileMemoListDensity = (density: MobileMemoListDensity) => AsyncStorage.setItem(MEMO_LIST_DENSITY_KEY, density);
+52 -5
View File
@@ -62,6 +62,7 @@ import {
import { SafeAreaView } from "react-native-safe-area-context";
import type { ApiToken, MemoDetail, MemoRevision, MemoSummary, Notebook, ResourceListItem, TagSummary } from "@edgeever/shared";
import { clearMobileMemoDraft, readMobileMemoDraft, writeMobileMemoDraft } from "../lib/mobile-drafts";
import { readMobileMemoListDensity, writeMobileMemoListDensity, type MobileMemoListDensity } from "../lib/preferences";
import { useSession } from "../lib/session";
import {
emptyMobileSyncQueueSummary,
@@ -171,6 +172,7 @@ export const WorkspaceScreen = () => {
const [memoView, setMemoView] = useState<MemoView>("notebook");
const [memoFilterMode, setMemoFilterMode] = useState<MemoFilterMode>("all");
const [memoSortMode, setMemoSortMode] = useState<MemoSortMode>("updated-desc");
const [memoListDensity, setMemoListDensity] = useState<MobileMemoListDensity>("preview");
const [selectedMemoId, setSelectedMemoId] = useState<string | null>(null);
const [searchText, setSearchText] = useState("");
const [createOpen, setCreateOpen] = useState(false);
@@ -320,6 +322,25 @@ export const WorkspaceScreen = () => {
};
}, []);
useEffect(() => {
let mounted = true;
readMobileMemoListDensity().then((density) => {
if (mounted) {
setMemoListDensity(density);
}
});
return () => {
mounted = false;
};
}, []);
const handleMemoListDensityChange = (density: MobileMemoListDensity) => {
setMemoListDensity(density);
void writeMobileMemoListDensity(density);
};
const invalidateWorkspace = async () => {
await Promise.all([
queryClient.invalidateQueries({ queryKey: ["mobile", "notebooks"] }),
@@ -545,6 +566,7 @@ export const WorkspaceScreen = () => {
isRefreshing={isRefreshing}
memoCount={memosQuery.data?.totalCount ?? memos.length}
memoFilterMode={memoFilterMode}
memoListDensity={memoListDensity}
memoSortMode={memoSortMode}
memoView={memoView}
memos={memos}
@@ -553,6 +575,7 @@ export const WorkspaceScreen = () => {
onCreate={() => setCreateOpen(true)}
onEmptyTrash={handleEmptyTrash}
onFilterModeChange={setMemoFilterMode}
onMemoListDensityChange={handleMemoListDensityChange}
onOpenTemplates={() => setTemplatesOpen(true)}
onMemoPress={handleMemoPress}
onMemoLongPress={toggleSelectedMemo}
@@ -757,6 +780,7 @@ const NotesView = ({
isRefreshing,
memoCount,
memoFilterMode,
memoListDensity,
memoSortMode,
memoView,
memos,
@@ -765,6 +789,7 @@ const NotesView = ({
onCreate,
onEmptyTrash,
onFilterModeChange,
onMemoListDensityChange,
onMemoLongPress,
onMemoPress,
onOpenTemplates,
@@ -783,6 +808,7 @@ const NotesView = ({
isRefreshing: boolean;
memoCount: number;
memoFilterMode: MemoFilterMode;
memoListDensity: MobileMemoListDensity;
memoSortMode: MemoSortMode;
memoView: MemoView;
memos: MemoSummary[];
@@ -791,6 +817,7 @@ const NotesView = ({
onCreate: () => void;
onEmptyTrash: () => void;
onFilterModeChange: (filterMode: MemoFilterMode) => void;
onMemoListDensityChange: (density: MobileMemoListDensity) => void;
onMemoLongPress: (memoId: string) => void;
onMemoPress: (memoId: string) => void;
onOpenTemplates: () => void;
@@ -863,6 +890,10 @@ const NotesView = ({
<OptionPill active={memoSortMode === "created-desc"} label="创建时间" onPress={() => onSortModeChange("created-desc")} />
<OptionPill active={memoSortMode === "title-asc"} label="标题 A-Z" onPress={() => onSortModeChange("title-asc")} />
</ScrollView>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
<OptionPill active={memoListDensity === "preview"} label="预览" onPress={() => onMemoListDensityChange("preview")} />
<OptionPill active={memoListDensity === "compact"} label="紧凑" onPress={() => onMemoListDensityChange("compact")} />
</ScrollView>
</View>
) : null}
@@ -873,6 +904,7 @@ const NotesView = ({
isError={isError}
isLoading={isLoading}
isRefreshing={isRefreshing}
listDensity={memoListDensity}
memos={memos}
onMemoLongPress={onMemoLongPress}
onMemoPress={onMemoPress}
@@ -931,6 +963,7 @@ const SearchView = ({
isError={false}
isLoading={isLoading}
isRefreshing={isRefreshing}
listDensity="preview"
memos={results}
onMemoPress={onMemoPress}
onRefresh={onRefresh}
@@ -2805,6 +2838,7 @@ const MemoList = ({
isError,
isLoading,
isRefreshing,
listDensity,
memos,
onMemoLongPress,
onMemoPress,
@@ -2817,6 +2851,7 @@ const MemoList = ({
isError: boolean;
isLoading: boolean;
isRefreshing: boolean;
listDensity: MobileMemoListDensity;
memos: MemoSummary[];
onMemoLongPress?: (memoId: string) => void;
onMemoPress: (memoId: string) => void;
@@ -2849,6 +2884,7 @@ const MemoList = ({
renderItem={({ item }) => (
<MemoCard
memo={item}
listDensity={listDensity}
onLongPress={onMemoLongPress ? () => onMemoLongPress(item.id) : undefined}
onPress={() => onMemoPress(item.id)}
selected={selectedMemoIds.has(item.id)}
@@ -3034,19 +3070,21 @@ const OptionPill = ({ active, label, onPress }: { active: boolean; label: string
);
const MemoCard = ({
listDensity,
memo,
onLongPress,
onPress,
selected = false,
selectionMode = false,
}: {
listDensity: MobileMemoListDensity;
memo: MemoSummary;
onLongPress?: () => void;
onPress: () => void;
selected?: boolean;
selectionMode?: boolean;
}) => (
<Pressable onLongPress={onLongPress} onPress={onPress} style={[styles.memoCard, selected && styles.memoCardSelected]}>
<Pressable onLongPress={onLongPress} onPress={onPress} style={[styles.memoCard, listDensity === "compact" && styles.memoCardCompact, selected && styles.memoCardSelected]}>
<View style={styles.memoCardTop}>
{selectionMode ? (
<View style={[styles.selectionIndicator, selected && styles.selectionIndicatorActive]}>
@@ -3060,10 +3098,12 @@ const MemoCard = ({
</Text>
{memo.isPinned ? <Text style={styles.pinText}>置顶</Text> : null}
</View>
<Text numberOfLines={2} style={styles.memoExcerpt}>
{memo.excerpt || "没有正文预览"}
</Text>
<View style={styles.memoMeta}>
{listDensity === "preview" ? (
<Text numberOfLines={2} style={styles.memoExcerpt}>
{memo.excerpt || "没有正文预览"}
</Text>
) : null}
<View style={[styles.memoMeta, listDensity === "compact" && styles.memoMetaCompact]}>
<Text style={styles.memoDate}>{formatDate(memo.updatedAt)}</Text>
{memo.tags.slice(0, 2).map((tag) => (
<Text key={tag} style={styles.tag}>
@@ -3733,6 +3773,10 @@ const styles = StyleSheet.create({
marginBottom: 10,
padding: 14,
},
memoCardCompact: {
marginBottom: 8,
padding: 11,
},
memoCardSelected: {
backgroundColor: "#f8fafc",
borderColor: "#0f172a",
@@ -3779,6 +3823,9 @@ const styles = StyleSheet.create({
gap: 8,
marginTop: 12,
},
memoMetaCompact: {
marginTop: 8,
},
memoDate: {
color: "#94a3b8",
fontSize: 12,