chore: make compact mode an opt-in view setting

Memo list views hard-coded compact rendering, truncating long memos by
default. Make full content the default and add a persisted 'Compact mode'
toggle (default off) in the display-settings popover, backed by the
localStorage ViewContext. Wire Home/Explore/Archived/UserProfile to read
it; add the compact-mode label across all locales.
pull/6027/head
johnnyjoygh 3 weeks ago
parent 8fa2ff4423
commit cafa56f1a8

@ -1,5 +1,6 @@
import { Settings2Icon } from "lucide-react";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Switch } from "@/components/ui/switch";
import { useView } from "@/contexts/ViewContext";
import { cn } from "@/lib/utils";
import { useTranslate } from "@/utils/i18n";
@ -11,8 +12,8 @@ interface Props {
function MemoDisplaySettingMenu({ className }: Props) {
const t = useTranslate();
const { orderByTimeAsc, timeBasis, setTimeBasis, toggleSortOrder } = useView();
const isApplying = orderByTimeAsc !== false || timeBasis !== "create_time";
const { orderByTimeAsc, timeBasis, compactMode, setTimeBasis, toggleSortOrder, setCompactMode } = useView();
const isApplying = orderByTimeAsc !== false || timeBasis !== "create_time" || compactMode;
return (
<Popover>
@ -52,6 +53,10 @@ function MemoDisplaySettingMenu({ className }: Props) {
</SelectContent>
</Select>
</div>
<div className="w-full flex flex-row justify-between items-center">
<span className="text-sm shrink-0 mr-3 text-foreground">{t("memo.compact-mode")}</span>
<Switch checked={compactMode} onCheckedChange={setCompactMode} />
</div>
</div>
</PopoverContent>
</Popover>

@ -6,13 +6,16 @@ interface ViewState {
orderByTimeAsc: boolean;
timeBasis?: MemoTimeBasis;
sortTimeField?: MemoTimeBasis;
compactMode: boolean;
}
interface ViewContextValue {
orderByTimeAsc: boolean;
timeBasis: MemoTimeBasis;
compactMode: boolean;
toggleSortOrder: () => void;
setTimeBasis: (field: MemoTimeBasis) => void;
setCompactMode: (value: boolean) => void;
}
const ViewContext = createContext<ViewContextValue | null>(null);
@ -30,12 +33,13 @@ export function ViewProvider({ children }: { children: ReactNode }) {
return {
orderByTimeAsc: Boolean(data.orderByTimeAsc ?? false),
timeBasis,
compactMode: Boolean(data.compactMode ?? false),
};
}
} catch (error) {
console.warn("Failed to load view settings from localStorage:", error);
}
return { orderByTimeAsc: false };
return { orderByTimeAsc: false, compactMode: false };
};
const [viewState, setViewState] = useState(getInitialState);
@ -65,13 +69,23 @@ export function ViewProvider({ children }: { children: ReactNode }) {
});
};
const setCompactMode = (value: boolean) => {
setViewState((prev) => {
const newState = { ...prev, compactMode: value };
persistToStorage(newState);
return newState;
});
};
return (
<ViewContext.Provider
value={{
orderByTimeAsc: viewState.orderByTimeAsc,
timeBasis,
compactMode: viewState.compactMode,
toggleSortOrder,
setTimeBasis,
setCompactMode,
}}
>
{children}

@ -194,6 +194,7 @@
"has-task-list": "يحتوي على قائمة مهام",
"label": "المرشحات"
},
"compact-mode": "الوضع المضغوط",
"links": "روابط",
"load-more": "تحميل المزيد",
"no-archived-memos": "لا يوجد مذكرات مؤرشفة",

@ -246,6 +246,7 @@
"has-task-list": "hasTaskList",
"label": "Филтри"
},
"compact-mode": "Компактен режим",
"links": "Връзки",
"load-more": "Заредете повече",
"no-archived-memos": "Няма архивирани бележки.",

@ -208,6 +208,7 @@
"has-task-list": "téLlistaTasques",
"label": "Filtres"
},
"compact-mode": "Mode compacte",
"links": "Enllaços",
"load-more": "Carrega més",
"no-archived-memos": "No hi ha notes arxivades.",

@ -208,6 +208,7 @@
"has-task-list": "maSeznamUkolu",
"label": "Filtry"
},
"compact-mode": "Kompaktní režim",
"links": "Odkazy",
"load-more": "Načíst více",
"no-archived-memos": "Žádné archivované poznámky.",

@ -246,6 +246,7 @@
"has-task-list": "har Opgaveliste",
"label": "Filtre"
},
"compact-mode": "Kompakt tilstand",
"links": "Links",
"load-more": "Indlæs mere",
"no-archived-memos": "Ingen arkiverede notater.",

@ -208,6 +208,7 @@
"has-task-list": "hatAufgabenliste",
"label": "Filter"
},
"compact-mode": "Kompaktmodus",
"links": "Links",
"load-more": "Mehr laden",
"no-archived-memos": "Keine archivierten Notizen.",

@ -246,6 +246,7 @@
"has-task-list": "hasTaskList",
"label": "Φίλτρα"
},
"compact-mode": "Συμπαγής λειτουργία",
"links": "Εδαφος διά παιγνίδι γκολφ",
"load-more": "Φορτώστε περισσότερα",
"no-archived-memos": "Δεν υπάρχουν αρχειοθετημένα σημειώματα.",

@ -208,6 +208,7 @@
"has-task-list": "hasTaskList",
"label": "Filters"
},
"compact-mode": "Compact mode",
"links": "Links",
"load-more": "Load more",
"no-archived-memos": "No archived memos.",

@ -264,6 +264,7 @@
"has-task-list": "hasTaskList",
"label": "Filters"
},
"compact-mode": "Compact mode",
"links": "Links",
"load-more": "Load more",
"no-archived-memos": "No archived memos.",

@ -208,6 +208,7 @@
"has-task-list": "tieneListaTareas",
"label": "Filtros"
},
"compact-mode": "Modo compacto",
"links": "Enlaces",
"load-more": "Cargar más",
"no-archived-memos": "No hay memos archivados.",

@ -246,6 +246,7 @@
"has-task-list": "hasTaskList",
"label": "Filtrid"
},
"compact-mode": "Kompaktne režiim",
"links": "Lingid",
"load-more": "Laadige rohkem",
"no-archived-memos": "Arhiveeritud memosid pole.",

@ -194,6 +194,7 @@
"has-task-list": "دارای لیست کارها",
"label": "فیلترها"
},
"compact-mode": "حالت فشرده",
"links": "پیوندها",
"load-more": "بارگذاری بیشتر",
"no-archived-memos": "یادداشت آرشیو شده‌ای وجود ندارد.",

@ -246,6 +246,7 @@
"has-task-list": "hasTaskList",
"label": "Suodattimet"
},
"compact-mode": "Tiivis tila",
"links": "Linkit",
"load-more": "Lataa lisää",
"no-archived-memos": "Ei arkistoituja muistioita.",

@ -208,6 +208,7 @@
"has-task-list": "aListeTâches",
"label": "Filtres"
},
"compact-mode": "Mode compact",
"links": "Liens",
"load-more": "Charger plus",
"no-archived-memos": "Aucune note archivée.",

@ -208,6 +208,7 @@
"has-task-list": "hasTaskList",
"label": "Filtros"
},
"compact-mode": "Modo compacto",
"links": "Ligazóns",
"load-more": "Cargar máis",
"no-archived-memos": "Sen memos arquivadas.",

@ -194,6 +194,7 @@
"has-task-list": "टास्क लिस्ट है",
"label": "फिल्टर"
},
"compact-mode": "कॉम्पैक्ट मोड",
"links": "लिंक",
"load-more": "और लोड करें",
"no-archived-memos": "कोई संग्रहीत मेमो नहीं।",

@ -208,6 +208,7 @@
"has-task-list": "imaZadatke",
"label": "Filteri"
},
"compact-mode": "Kompaktni način",
"links": "Linkovi",
"load-more": "Učitaj više",
"no-archived-memos": "Nema arhiviranih memoa.",

@ -208,6 +208,7 @@
"has-task-list": "vanFeladatLista",
"label": "Szűrők"
},
"compact-mode": "Kompakt mód",
"links": "Hivatkozások",
"load-more": "Több betöltése",
"no-archived-memos": "Nincsenek archivált jegyzetek.",

@ -194,6 +194,7 @@
"has-task-list": "Memiliki daftar tugas",
"label": "Filter"
},
"compact-mode": "Mode ringkas",
"links": "Tautan",
"load-more": "Muat lebih banyak",
"no-archived-memos": "Tidak ada memo yang diarsipkan.",

@ -208,6 +208,7 @@
"has-task-list": "haListaCompiti",
"label": "Filtri"
},
"compact-mode": "Modalità compatta",
"links": "Link",
"load-more": "Carica altro",
"no-archived-memos": "Nessun memo archiviato.",

@ -208,6 +208,7 @@
"has-task-list": "タスクリストあり",
"label": "フィルター"
},
"compact-mode": "コンパクト表示",
"links": "リンク",
"load-more": "さらに読み込む",
"no-archived-memos": "アーカイブされたメモはありません。",

@ -208,6 +208,7 @@
"has-task-list": "სიისამოცანებია",
"label": "ფილტრები"
},
"compact-mode": "კომპაქტური რეჟიმი",
"links": "ლინკები",
"load-more": "მეტის ჩატვირთვა",
"no-archived-memos": "დაარქივებული მემოები არ არის.",

@ -194,6 +194,7 @@
"has-task-list": "할일목록있음",
"label": "필터"
},
"compact-mode": "간략 보기",
"links": "링크",
"load-more": "더보기",
"no-archived-memos": "보관처리된 메모가 없습니다.",

@ -246,6 +246,7 @@
"has-task-list": "turiTaskList",
"label": "Filtrai"
},
"compact-mode": "Kompaktiškas režimas",
"links": "Nuorodos",
"load-more": "Įkelti daugiau",
"no-archived-memos": "Nėra archyvuotų atmintinių.",

@ -246,6 +246,7 @@
"has-task-list": "hasTaskList",
"label": "Filtri"
},
"compact-mode": "Kompaktais režīms",
"links": "Saites",
"load-more": "Ielādēt vairāk",
"no-archived-memos": "Nav arhivētu piezīmju.",

@ -194,6 +194,7 @@
"has-task-list": "कार्यसूची आहे",
"label": "फिल्टर"
},
"compact-mode": "संक्षिप्त मोड",
"links": "लिंक्स",
"load-more": "अधिक लोड करा",
"no-archived-memos": "कोणतेही संग्रहित मेमो नाहीत.",

@ -208,6 +208,7 @@
"has-task-list": "harGjøremålsListe",
"label": "Filtre"
},
"compact-mode": "Kompakt modus",
"links": "Linker",
"load-more": "Last inn mer",
"no-archived-memos": "Ingen arkiverte memoer.",

@ -208,6 +208,7 @@
"has-task-list": "heeftTakenlijst",
"label": "Filters"
},
"compact-mode": "Compacte modus",
"links": "Links",
"load-more": "Meer laden",
"no-archived-memos": "Geen gearchiveerde memos.",

@ -208,6 +208,7 @@
"has-task-list": "ma listę zadań",
"label": "Filtry"
},
"compact-mode": "Tryb kompaktowy",
"links": "Linki",
"load-more": "Załaduj więcej",
"no-archived-memos": "Brak zarchiwizowanych notatek.",

@ -194,6 +194,7 @@
"has-task-list": "temListaDeTarefas",
"label": "Filtros"
},
"compact-mode": "Modo compacto",
"links": "Links",
"load-more": "Carregar mais",
"no-archived-memos": "Nenhum memo arquivado.",

@ -208,6 +208,7 @@
"has-task-list": "temListaDeTarefas",
"label": "Filtros"
},
"compact-mode": "Modo compacto",
"links": "Links",
"load-more": "Carregar mais",
"no-archived-memos": "Não existem memos arquivados.",

@ -246,6 +246,7 @@
"has-task-list": "areTaskList",
"label": "Filtre"
},
"compact-mode": "Mod compact",
"links": "Legături",
"load-more": "Încărcați mai multe",
"no-archived-memos": "Fără memorii arhivate.",

@ -208,6 +208,7 @@
"has-task-list": "hasTaskList",
"label": "Фильтры"
},
"compact-mode": "Компактный режим",
"links": "Ссылки",
"load-more": "Загрузить еще",
"no-archived-memos": "Нет заархивированных записей.",

@ -246,6 +246,7 @@
"has-task-list": "hasTaskList",
"label": "Filtre"
},
"compact-mode": "Kompaktný režim",
"links": "Odkazy",
"load-more": "Načítať viac",
"no-archived-memos": "Žiadne archivované poznámky.",

@ -208,6 +208,7 @@
"has-task-list": "imaSeznamOpravil",
"label": "Filtri"
},
"compact-mode": "Strnjeni način",
"links": "Povezave",
"load-more": "Naloži več",
"no-archived-memos": "Ni arhiviranih beležk.",

@ -246,6 +246,7 @@
"has-task-list": "хасТаскЛист",
"label": "Филтери"
},
"compact-mode": "Компактни режим",
"links": "Линкови",
"load-more": "Учитај више",
"no-archived-memos": "Нема архивираних белешки.",

@ -208,6 +208,7 @@
"has-task-list": "harAttGöraLista",
"label": "Filter"
},
"compact-mode": "Kompakt läge",
"links": "Länkar",
"load-more": "Ladda mer",
"no-archived-memos": "Inga arkiverade anteckningar.",

@ -194,6 +194,7 @@
"has-task-list": "มีรายการที่ต้องทำ",
"label": "ตัวกรอง"
},
"compact-mode": "โหมดกะทัดรัด",
"links": "ลิงก์",
"load-more": "โหลดเพิ่มเติม",
"no-archived-memos": "ไม่มีบันทึกช่วยจำที่ถูกเก็บถาวร",

@ -208,6 +208,7 @@
"has-task-list": "Yapılacaklar listesi içerir",
"label": "Filtreler"
},
"compact-mode": "Kompakt mod",
"links": "Bağlantılar",
"load-more": "Daha fazla yükle",
"no-archived-memos": "Arşivlenmiş not yok.",

@ -208,6 +208,7 @@
"has-task-list": "єСписокЗавдань",
"label": "Фільтри"
},
"compact-mode": "Компактний режим",
"links": "Посилання",
"load-more": "Завантажити більше",
"no-archived-memos": "Немає архівованих нотаток.",

@ -194,6 +194,7 @@
"has-task-list": "cóDanhSáchViệc",
"label": "Bộ lọc"
},
"compact-mode": "Chế độ thu gọn",
"links": "Liên kết",
"load-more": "Tải thêm",
"no-archived-memos": "Không có ghi chú nào được lưu trữ.",

@ -210,6 +210,7 @@
"has-task-list": "有待办",
"label": "过滤器"
},
"compact-mode": "紧凑模式",
"links": "链接",
"load-more": "加载更多",
"no-archived-memos": "没有已归档备忘录。",

@ -246,6 +246,7 @@
"has-task-list": "有待辦事項",
"label": "過濾器"
},
"compact-mode": "精簡模式",
"links": "連結",
"load-more": "載入更多",
"no-archived-memos": "無已封存的備忘錄",

@ -1,5 +1,6 @@
import MemoView from "@/components/MemoView";
import PagedMemoList from "@/components/PagedMemoList";
import { useView } from "@/contexts/ViewContext";
import { useMemoFilters, useMemoSorting } from "@/hooks";
import useCurrentUser from "@/hooks/useCurrentUser";
import { State } from "@/types/proto/api/v1/common_pb";
@ -7,6 +8,7 @@ import { Memo } from "@/types/proto/api/v1/memo_service_pb";
const Archived = () => {
const user = useCurrentUser();
const { compactMode } = useView();
// Build filter using unified hook (no shortcuts or pinned filter)
const memoFilter = useMemoFilters({
@ -23,7 +25,7 @@ const Archived = () => {
return (
<PagedMemoList
renderer={(memo: Memo) => <MemoView key={`${memo.name}-${memo.updateTime}`} memo={memo} showVisibility compact />}
renderer={(memo: Memo) => <MemoView key={`${memo.name}-${memo.updateTime}`} memo={memo} showVisibility compact={compactMode} />}
listSort={listSort}
state={State.ARCHIVED}
orderBy={orderBy}

@ -1,5 +1,6 @@
import MemoView from "@/components/MemoView";
import PagedMemoList from "@/components/PagedMemoList";
import { useView } from "@/contexts/ViewContext";
import { useMemoFilters, useMemoSorting } from "@/hooks";
import useCurrentUser from "@/hooks/useCurrentUser";
import { State } from "@/types/proto/api/v1/common_pb";
@ -7,6 +8,7 @@ import { Memo, Visibility } from "@/types/proto/api/v1/memo_service_pb";
const Explore = () => {
const currentUser = useCurrentUser();
const { compactMode } = useView();
// Determine visibility filter based on authentication status
// - Logged-in users: Can see PUBLIC and PROTECTED memos
@ -29,7 +31,9 @@ const Explore = () => {
return (
<PagedMemoList
renderer={(memo: Memo) => <MemoView key={`${memo.name}-${memo.updateTime}`} memo={memo} showCreator showVisibility compact />}
renderer={(memo: Memo) => (
<MemoView key={`${memo.name}-${memo.updateTime}`} memo={memo} showCreator showVisibility compact={compactMode} />
)}
listSort={listSort}
orderBy={orderBy}
filter={memoFilter}

@ -2,6 +2,7 @@ import MemoView from "@/components/MemoView";
import PagedMemoList from "@/components/PagedMemoList";
import { useInstance } from "@/contexts/InstanceContext";
import { NewMemoProvider } from "@/contexts/NewMemoContext";
import { useView } from "@/contexts/ViewContext";
import { useMemoFilters, useMemoSorting } from "@/hooks";
import useCurrentUser from "@/hooks/useCurrentUser";
import { State } from "@/types/proto/api/v1/common_pb";
@ -10,6 +11,7 @@ import { Memo } from "@/types/proto/api/v1/memo_service_pb";
const Home = () => {
const user = useCurrentUser();
const { isInitialized } = useInstance();
const { compactMode } = useView();
const memoFilter = useMemoFilters({
creatorName: user?.name,
@ -26,7 +28,9 @@ const Home = () => {
<div className="w-full min-h-full bg-background text-foreground">
<NewMemoProvider>
<PagedMemoList
renderer={(memo: Memo) => <MemoView key={`${memo.name}-${memo.updateTime}`} memo={memo} showVisibility showPinned compact />}
renderer={(memo: Memo) => (
<MemoView key={`${memo.name}-${memo.updateTime}`} memo={memo} showVisibility showPinned compact={compactMode} />
)}
listSort={listSort}
orderBy={orderBy}
filter={memoFilter}

@ -8,6 +8,7 @@ import PagedMemoList from "@/components/PagedMemoList";
import UserAvatar from "@/components/UserAvatar";
import { Button } from "@/components/ui/button";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { useView } from "@/contexts/ViewContext";
import { useMemoFilters, useMemoSorting } from "@/hooks";
import { useUser } from "@/hooks/useUserQueries";
import { State } from "@/types/proto/api/v1/common_pb";
@ -50,6 +51,7 @@ const UserProfile = () => {
const username = useParams().username;
const [searchParams, setSearchParams] = useSearchParams();
const activeTab = (searchParams.get("view") === "map" ? "map" : "memos") as TabView;
const { compactMode } = useView();
const { data: user, isLoading, error } = useUser(`users/${username}`, { enabled: !!username });
@ -111,7 +113,7 @@ const UserProfile = () => {
{activeTab === "memos" ? (
<PagedMemoList
renderer={(memo: Memo) => (
<MemoView key={`${memo.name}-${memo.updateTime}`} memo={memo} showVisibility showPinned compact />
<MemoView key={`${memo.name}-${memo.updateTime}`} memo={memo} showVisibility showPinned compact={compactMode} />
)}
listSort={listSort}
orderBy={orderBy}

Loading…
Cancel
Save