From a7cabb7ce66f1ea7a509b83ee24e993e763527ff Mon Sep 17 00:00:00 2001 From: memoclaw Date: Fri, 20 Mar 2026 09:01:43 +0800 Subject: [PATCH] refactor(web): remove masonry memo layout (#5746) Co-authored-by: memoclaw <265580040+memoclaw@users.noreply.github.com> --- .../components/MasonryView/MasonryColumn.tsx | 34 ----- .../components/MasonryView/MasonryItem.tsx | 32 ----- .../components/MasonryView/MasonryView.tsx | 47 ------- web/src/components/MasonryView/README.md | 116 ------------------ web/src/components/MasonryView/constants.ts | 3 - .../components/MasonryView/distributeItems.ts | 68 ---------- web/src/components/MasonryView/index.ts | 22 ---- web/src/components/MasonryView/types.ts | 41 ------- .../MasonryView/useMasonryLayout.ts | 106 ---------------- web/src/components/MemoDisplaySettingMenu.tsx | 16 +-- web/src/components/MemoView/MemoView.tsx | 2 +- .../components/MemoCommentListView.tsx | 2 +- .../PagedMemoList/PagedMemoList.tsx | 24 +--- web/src/contexts/ViewContext.tsx | 17 +-- web/src/locales/ar.json | 3 - web/src/locales/ca.json | 3 - web/src/locales/cs.json | 3 - web/src/locales/de.json | 3 - web/src/locales/en.json | 3 - web/src/locales/es.json | 3 - web/src/locales/fa.json | 3 - web/src/locales/fr.json | 3 - web/src/locales/gl.json | 3 - web/src/locales/hi.json | 3 - web/src/locales/hr.json | 3 - web/src/locales/hu.json | 3 - web/src/locales/id.json | 3 - web/src/locales/it.json | 3 - web/src/locales/ja.json | 3 - web/src/locales/ka-GE.json | 3 - web/src/locales/ko.json | 3 - web/src/locales/mr.json | 3 - web/src/locales/nb.json | 3 - web/src/locales/nl.json | 3 - web/src/locales/pl.json | 3 - web/src/locales/pt-BR.json | 3 - web/src/locales/pt-PT.json | 3 - web/src/locales/ru.json | 3 - web/src/locales/sl.json | 5 +- web/src/locales/sv.json | 3 - web/src/locales/th.json | 3 - web/src/locales/tr.json | 3 - web/src/locales/uk.json | 3 - web/src/locales/vi.json | 3 - web/src/locales/zh-Hans.json | 3 - web/src/locales/zh-Hant.json | 3 - 46 files changed, 11 insertions(+), 617 deletions(-) delete mode 100644 web/src/components/MasonryView/MasonryColumn.tsx delete mode 100644 web/src/components/MasonryView/MasonryItem.tsx delete mode 100644 web/src/components/MasonryView/MasonryView.tsx delete mode 100644 web/src/components/MasonryView/README.md delete mode 100644 web/src/components/MasonryView/constants.ts delete mode 100644 web/src/components/MasonryView/distributeItems.ts delete mode 100644 web/src/components/MasonryView/index.ts delete mode 100644 web/src/components/MasonryView/types.ts delete mode 100644 web/src/components/MasonryView/useMasonryLayout.ts diff --git a/web/src/components/MasonryView/MasonryColumn.tsx b/web/src/components/MasonryView/MasonryColumn.tsx deleted file mode 100644 index 9876435f2..000000000 --- a/web/src/components/MasonryView/MasonryColumn.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { MasonryItem } from "./MasonryItem"; -import { MasonryColumnProps } from "./types"; - -export function MasonryColumn({ - memoIndices, - memoList, - renderer, - renderContext, - onHeightChange, - isFirstColumn, - prefixElement, - prefixElementRef, -}: MasonryColumnProps) { - return ( -
- {/* Prefix element (like memo editor) goes in first column */} - {isFirstColumn && prefixElement &&
{prefixElement}
} - - {/* Render all memos assigned to this column */} - {memoIndices?.map((memoIndex) => { - const memo = memoList[memoIndex]; - return memo ? ( - - ) : null; - })} -
- ); -} diff --git a/web/src/components/MasonryView/MasonryItem.tsx b/web/src/components/MasonryView/MasonryItem.tsx deleted file mode 100644 index 7151a8626..000000000 --- a/web/src/components/MasonryView/MasonryItem.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { useEffect, useRef } from "react"; -import { MasonryItemProps } from "./types"; - -export function MasonryItem({ memo, renderer, renderContext, onHeightChange }: MasonryItemProps) { - const itemRef = useRef(null); - const resizeObserverRef = useRef(null); - - useEffect(() => { - if (!itemRef.current) return; - - const measureHeight = () => { - if (itemRef.current) { - const height = itemRef.current.offsetHeight; - onHeightChange(memo.name, height); - } - }; - - // Initial measurement - measureHeight(); - - // Set up ResizeObserver to track dynamic content changes - resizeObserverRef.current = new ResizeObserver(measureHeight); - resizeObserverRef.current.observe(itemRef.current); - - // Cleanup on unmount - return () => { - resizeObserverRef.current?.disconnect(); - }; - }, [memo.name, onHeightChange]); - - return
{renderer(memo, renderContext)}
; -} diff --git a/web/src/components/MasonryView/MasonryView.tsx b/web/src/components/MasonryView/MasonryView.tsx deleted file mode 100644 index f20067b16..000000000 --- a/web/src/components/MasonryView/MasonryView.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { useMemo, useRef } from "react"; -import { cn } from "@/lib/utils"; -import { MasonryColumn } from "./MasonryColumn"; -import { MasonryViewProps, MemoRenderContext } from "./types"; -import { useMasonryLayout } from "./useMasonryLayout"; - -const MasonryView = ({ memoList, renderer, prefixElement, listMode = false }: MasonryViewProps) => { - const containerRef = useRef(null); - const prefixElementRef = useRef(null); - - const { columns, distribution, handleHeightChange } = useMasonryLayout(memoList, listMode, containerRef, prefixElementRef); - - // Create render context: always enable compact mode for list views - const renderContext: MemoRenderContext = useMemo( - () => ({ - compact: true, - columns, - }), - [columns], - ); - - return ( -
- {Array.from({ length: columns }).map((_, columnIndex) => ( - - ))} -
- ); -}; - -export default MasonryView; diff --git a/web/src/components/MasonryView/README.md b/web/src/components/MasonryView/README.md deleted file mode 100644 index c9b4673dd..000000000 --- a/web/src/components/MasonryView/README.md +++ /dev/null @@ -1,116 +0,0 @@ -# MasonryView - Height-Based Masonry Layout - -## Overview - -This improved MasonryView component implements a true masonry layout that distributes memo cards based on their actual rendered heights, creating a balanced waterfall-style layout instead of naive sequential distribution. - -## Key Features - -### 1. Height Measurement - -- **MemoItem Wrapper**: Each memo is wrapped in a `MemoItem` component that measures its actual height -- **ResizeObserver**: Automatically detects height changes when content changes (e.g., images load, content expands) -- **Real-time Updates**: Heights are measured on mount and updated dynamically - -### 2. Smart Distribution Algorithm - -- **Shortest Column First**: Memos are assigned to the column with the smallest total height -- **Dynamic Balancing**: As new memos are added or heights change, the layout rebalances -- **Prefix Element Support**: Properly accounts for the MemoEditor height in the first column - -### 3. Performance Optimizations - -- **Memoized Callbacks**: `handleHeightChange` is memoized to prevent unnecessary re-renders -- **Efficient State Updates**: Only redistributes when necessary (memo list changes, column count changes) -- **ResizeObserver Cleanup**: Properly disconnects observers to prevent memory leaks - -## Architecture - -``` -MasonryView -├── State Management -│ ├── columns: number of columns based on viewport width -│ ├── itemHeights: Map for each memo -│ ├── columnHeights: current total height of each column -│ └── distribution: which memos belong to which column -├── MemoItem (for each memo) -│ ├── Ref for height measurement -│ ├── ResizeObserver for dynamic updates -│ └── Callback to parent on height changes -└── Distribution Algorithm - ├── Finds shortest column - ├── Assigns memo to that column - └── Updates column height tracking -``` - -## Usage - -The component maintains the same API as before, so no changes are needed in consuming components: - -```tsx - } prefixElement={} listMode={false} /> -``` - -## Benefits vs Previous Implementation - -### Before (Naive) - -- Distributed memos by index: `memo[i % columns]` -- No consideration of actual heights -- Resulted in unbalanced columns -- Static layout that didn't adapt to content - -### After (Height-Based) - -- Distributes memos by actual rendered height -- Creates balanced columns with similar total heights -- Adapts to dynamic content changes -- Smoother visual layout - -## Technical Implementation Details - -### Height Measurement - -```tsx -const measureHeight = () => { - if (itemRef.current) { - const height = itemRef.current.offsetHeight; - onHeightChange(memo.name, height); - } -}; -``` - -### Distribution Algorithm - -```tsx -const shortestColumnIndex = columnHeights.reduce( - (minIndex, currentHeight, currentIndex) => (currentHeight < columnHeights[minIndex] ? currentIndex : minIndex), - 0, -); -``` - -### Dynamic Updates - -- **Window Resize**: Recalculates column count and redistributes -- **Content Changes**: ResizeObserver triggers height remeasurement -- **Memo List Changes**: Redistributes all memos with new ordering - -## Browser Support - -- Modern browsers with ResizeObserver support -- Fallback behavior: Falls back to sequential distribution if ResizeObserver is not available -- CSS Grid support required for column layout - -## Performance Considerations - -1. **Initial Load**: Slight delay as heights are measured -2. **Memory Usage**: Stores height data for each memo -3. **Re-renders**: Optimized to only update when necessary -4. **Large Lists**: Scales well with proper virtualization (if needed in future) - -## Future Enhancements - -1. **Virtualization**: For very large memo lists -2. **Animation**: Smooth transitions when items change position -3. **Gap Optimization**: More sophisticated gap handling -4. **Estimated Heights**: Faster initial layout with height estimation diff --git a/web/src/components/MasonryView/constants.ts b/web/src/components/MasonryView/constants.ts deleted file mode 100644 index a234505d8..000000000 --- a/web/src/components/MasonryView/constants.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const MINIMUM_MEMO_VIEWPORT_WIDTH = 512; - -export const REDISTRIBUTION_DEBOUNCE_MS = 100; diff --git a/web/src/components/MasonryView/distributeItems.ts b/web/src/components/MasonryView/distributeItems.ts deleted file mode 100644 index c8a1e9f85..000000000 --- a/web/src/components/MasonryView/distributeItems.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Memo } from "@/types/proto/api/v1/memo_service_pb"; -import { DistributionResult } from "./types"; - -export function distributeItemsToColumns( - memos: Memo[], - columns: number, - itemHeights: Map, - prefixElementHeight: number = 0, -): DistributionResult { - if (columns === 1) { - const totalHeight = memos.reduce((sum, memo) => sum + (itemHeights.get(memo.name) || 0), prefixElementHeight); - return { - distribution: [Array.from({ length: memos.length }, (_, i) => i)], - columnHeights: [totalHeight], - }; - } - - const distribution: number[][] = Array.from({ length: columns }, () => []); - const columnHeights: number[] = Array(columns).fill(0); - const columnCounts: number[] = Array(columns).fill(0); - - if (prefixElementHeight > 0) { - columnHeights[0] = prefixElementHeight; - } - - let startIndex = 0; - - if (memos.length > 0) { - const firstMemoHeight = itemHeights.get(memos[0].name) || 0; - distribution[0].push(0); - columnHeights[0] += firstMemoHeight; - columnCounts[0] += 1; - startIndex = 1; - } - - for (let i = startIndex; i < memos.length; i++) { - const memo = memos[i]; - const height = itemHeights.get(memo.name) || 0; - - const shortestColumnIndex = findShortestColumnIndex(columnHeights, columnCounts); - - distribution[shortestColumnIndex].push(i); - columnHeights[shortestColumnIndex] += height; - columnCounts[shortestColumnIndex] += 1; - } - - return { distribution, columnHeights }; -} - -function findShortestColumnIndex(columnHeights: number[], columnCounts: number[]): number { - let minIndex = 0; - let minHeight = columnHeights[0]; - - for (let i = 1; i < columnHeights.length; i++) { - const currentHeight = columnHeights[i]; - if (currentHeight < minHeight) { - minHeight = currentHeight; - minIndex = i; - continue; - } - - if (currentHeight === minHeight && columnCounts[i] < columnCounts[minIndex]) { - minIndex = i; - } - } - - return minIndex; -} diff --git a/web/src/components/MasonryView/index.ts b/web/src/components/MasonryView/index.ts deleted file mode 100644 index 9c462c97b..000000000 --- a/web/src/components/MasonryView/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -// Main component - -// Constants -export { MINIMUM_MEMO_VIEWPORT_WIDTH, REDISTRIBUTION_DEBOUNCE_MS } from "./constants"; -// Utilities -export { distributeItemsToColumns } from "./distributeItems"; -// Sub-components (exported for testing or advanced usage) -export { MasonryColumn } from "./MasonryColumn"; -export { MasonryItem } from "./MasonryItem"; -export { default } from "./MasonryView"; - -// Types -export type { - DistributionResult, - MasonryColumnProps, - MasonryItemProps, - MasonryViewProps, - MemoRenderContext, - MemoWithHeight, -} from "./types"; -// Hooks -export { useMasonryLayout } from "./useMasonryLayout"; diff --git a/web/src/components/MasonryView/types.ts b/web/src/components/MasonryView/types.ts deleted file mode 100644 index 862ea4ddd..000000000 --- a/web/src/components/MasonryView/types.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Memo } from "@/types/proto/api/v1/memo_service_pb"; - -export interface MemoRenderContext { - compact: boolean; - columns: number; -} - -export interface MasonryViewProps { - memoList: Memo[]; - renderer: (memo: Memo, context?: MemoRenderContext) => JSX.Element; - prefixElement?: JSX.Element; - listMode?: boolean; -} - -export interface MasonryItemProps { - memo: Memo; - renderer: (memo: Memo, context?: MemoRenderContext) => JSX.Element; - renderContext: MemoRenderContext; - onHeightChange: (memoName: string, height: number) => void; -} - -export interface MasonryColumnProps { - memoIndices: number[]; - memoList: Memo[]; - renderer: (memo: Memo, context?: MemoRenderContext) => JSX.Element; - renderContext: MemoRenderContext; - onHeightChange: (memoName: string, height: number) => void; - isFirstColumn: boolean; - prefixElement?: JSX.Element; - prefixElementRef?: React.RefObject; -} - -export interface DistributionResult { - distribution: number[][]; - columnHeights: number[]; -} - -export interface MemoWithHeight { - index: number; - height: number; -} diff --git a/web/src/components/MasonryView/useMasonryLayout.ts b/web/src/components/MasonryView/useMasonryLayout.ts deleted file mode 100644 index b85443fcb..000000000 --- a/web/src/components/MasonryView/useMasonryLayout.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { useCallback, useEffect, useRef, useState } from "react"; -import { Memo } from "@/types/proto/api/v1/memo_service_pb"; -import { MINIMUM_MEMO_VIEWPORT_WIDTH, REDISTRIBUTION_DEBOUNCE_MS } from "./constants"; -import { distributeItemsToColumns } from "./distributeItems"; - -export function useMasonryLayout( - memoList: Memo[], - listMode: boolean, - containerRef: React.RefObject, - prefixElementRef: React.RefObject, -) { - const [columns, setColumns] = useState(1); - const [itemHeights, setItemHeights] = useState>(new Map()); - const [distribution, setDistribution] = useState([[]]); - - const redistributionTimeoutRef = useRef(null); - const itemHeightsRef = useRef>(itemHeights); - - useEffect(() => { - itemHeightsRef.current = itemHeights; - }, [itemHeights]); - - const calculateColumns = useCallback(() => { - if (!containerRef.current || listMode) return 1; - - const containerWidth = containerRef.current.offsetWidth; - const scale = containerWidth / MINIMUM_MEMO_VIEWPORT_WIDTH; - return scale >= 1.2 ? Math.ceil(scale) : 1; - }, [containerRef, listMode]); - - const redistributeMemos = useCallback(() => { - const prefixHeight = prefixElementRef.current?.offsetHeight || 0; - setDistribution(() => { - const { distribution: newDistribution } = distributeItemsToColumns(memoList, columns, itemHeightsRef.current, prefixHeight); - return newDistribution; - }); - }, [memoList, columns, prefixElementRef]); - - const debouncedRedistribute = useCallback( - (newItemHeights: Map) => { - if (redistributionTimeoutRef.current) { - clearTimeout(redistributionTimeoutRef.current); - } - - redistributionTimeoutRef.current = window.setTimeout(() => { - const prefixHeight = prefixElementRef.current?.offsetHeight || 0; - setDistribution(() => { - const { distribution: newDistribution } = distributeItemsToColumns(memoList, columns, newItemHeights, prefixHeight); - return newDistribution; - }); - }, REDISTRIBUTION_DEBOUNCE_MS); - }, - [memoList, columns, prefixElementRef], - ); - - const handleHeightChange = useCallback( - (memoName: string, height: number) => { - setItemHeights((prevHeights) => { - const newItemHeights = new Map(prevHeights); - const previousHeight = prevHeights.get(memoName); - - if (previousHeight === height) { - return prevHeights; - } - - newItemHeights.set(memoName, height); - debouncedRedistribute(newItemHeights); - return newItemHeights; - }); - }, - [debouncedRedistribute], - ); - - useEffect(() => { - const handleResize = () => { - if (!containerRef.current) return; - - const newColumns = calculateColumns(); - if (newColumns !== columns) { - setColumns(newColumns); - } - }; - - handleResize(); - window.addEventListener("resize", handleResize); - return () => window.removeEventListener("resize", handleResize); - }, [calculateColumns, columns, containerRef]); - - useEffect(() => { - redistributeMemos(); - }, [columns, memoList, redistributeMemos]); - - useEffect(() => { - return () => { - if (redistributionTimeoutRef.current) { - clearTimeout(redistributionTimeoutRef.current); - } - }; - }, []); - - return { - columns, - distribution, - handleHeightChange, - }; -} diff --git a/web/src/components/MemoDisplaySettingMenu.tsx b/web/src/components/MemoDisplaySettingMenu.tsx index d6293d513..c1ec24632 100644 --- a/web/src/components/MemoDisplaySettingMenu.tsx +++ b/web/src/components/MemoDisplaySettingMenu.tsx @@ -11,8 +11,8 @@ interface Props { function MemoDisplaySettingMenu({ className }: Props) { const t = useTranslate(); - const { orderByTimeAsc, layout, toggleSortOrder, setLayout } = useView(); - const isApplying = orderByTimeAsc !== false || layout !== "LIST"; + const { orderByTimeAsc, toggleSortOrder } = useView(); + const isApplying = orderByTimeAsc !== false; return ( @@ -40,18 +40,6 @@ function MemoDisplaySettingMenu({ className }: Props) { -
- {t("common.layout")} - -
diff --git a/web/src/components/MemoView/MemoView.tsx b/web/src/components/MemoView/MemoView.tsx index 8752fcef1..2b834c9d0 100644 --- a/web/src/components/MemoView/MemoView.tsx +++ b/web/src/components/MemoView/MemoView.tsx @@ -108,7 +108,7 @@ const MemoView: React.FC = (props: MemoViewProps) => { return ( {showCommentPreview ? ( -
+
{article}
diff --git a/web/src/components/MemoView/components/MemoCommentListView.tsx b/web/src/components/MemoView/components/MemoCommentListView.tsx index b08c67b2c..58957ce4a 100644 --- a/web/src/components/MemoView/components/MemoCommentListView.tsx +++ b/web/src/components/MemoView/components/MemoCommentListView.tsx @@ -19,7 +19,7 @@ const MemoCommentListView: React.FC = () => { const displayedComments = comments.slice(0, 3); return ( -
+
Comments{commentAmount > 1 ? ` (${commentAmount})` : ""} JSX.Element; + renderer: (memo: Memo) => JSX.Element; listSort?: (list: Memo[]) => Memo[]; state?: State; orderBy?: string; @@ -83,7 +80,6 @@ function useAutoFetchWhenNotScrollable({ const PagedMemoList = (props: Props) => { const t = useTranslate(); - const { layout } = useView(); const queryClient = useQueryClient(); // Show memo editor only on the root route @@ -149,25 +145,15 @@ const PagedMemoList = (props: Props) => { }, [hasNextPage, isFetchingNextPage, fetchNextPage]); const children = ( -
+
{/* Show skeleton loader during initial load */} {isLoading ? ( ) : ( <> - - {showMemoEditor ? ( - - ) : undefined} - - - } - listMode={layout === "LIST"} - /> + {showMemoEditor ? : null} + + {sortedMemoList.map((memo) => props.renderer(memo))} {/* Loading indicator for pagination */} {isFetchingNextPage && } diff --git a/web/src/contexts/ViewContext.tsx b/web/src/contexts/ViewContext.tsx index 908b2f824..3057d9efe 100644 --- a/web/src/contexts/ViewContext.tsx +++ b/web/src/contexts/ViewContext.tsx @@ -1,12 +1,8 @@ import { createContext, type ReactNode, useContext, useState } from "react"; -export type LayoutMode = "LIST" | "MASONRY"; - interface ViewContextValue { orderByTimeAsc: boolean; - layout: LayoutMode; toggleSortOrder: () => void; - setLayout: (layout: LayoutMode) => void; } const ViewContext = createContext(null); @@ -14,7 +10,6 @@ const ViewContext = createContext(null); const LOCAL_STORAGE_KEY = "memos-view-setting"; export function ViewProvider({ children }: { children: ReactNode }) { - // Load initial state from localStorage const getInitialState = () => { try { const cached = localStorage.getItem(LOCAL_STORAGE_KEY); @@ -22,13 +17,12 @@ export function ViewProvider({ children }: { children: ReactNode }) { const data = JSON.parse(cached); return { orderByTimeAsc: Boolean(data.orderByTimeAsc ?? false), - layout: (["LIST", "MASONRY"].includes(data.layout) ? data.layout : "LIST") as LayoutMode, }; } } catch (error) { console.warn("Failed to load view settings from localStorage:", error); } - return { orderByTimeAsc: false, layout: "LIST" as LayoutMode }; + return { orderByTimeAsc: false }; }; const [viewState, setViewState] = useState(getInitialState); @@ -49,20 +43,11 @@ export function ViewProvider({ children }: { children: ReactNode }) { }); }; - const setLayout = (layout: LayoutMode) => { - setViewState((prev) => { - const newState = { ...prev, layout }; - persistToStorage(newState); - return newState; - }); - }; - return ( {children} diff --git a/web/src/locales/ar.json b/web/src/locales/ar.json index 17f632a64..eb3b9020c 100644 --- a/web/src/locales/ar.json +++ b/web/src/locales/ar.json @@ -62,7 +62,6 @@ "input": "إدخال", "language": "اللغة", "last-updated-at": "آخر تحديث في", - "layout": "تخطيط", "learn-more": "تعلم المزيد", "link": "الرابط", "map": "خريطة", @@ -162,9 +161,7 @@ "has-task-list": "يحتوي على قائمة مهام" }, "links": "روابط", - "list": "قائمة", "load-more": "تحميل المزيد", - "masonry": "فسيفساء", "no-archived-memos": "لا يوجد مذكرات مؤرشفة", "no-memos": "لا يوجد مذكرات.", "order-by": "ترتيب حسب", diff --git a/web/src/locales/ca.json b/web/src/locales/ca.json index c13dff553..383021eef 100644 --- a/web/src/locales/ca.json +++ b/web/src/locales/ca.json @@ -62,7 +62,6 @@ "input": "Entrada", "language": "Llenguatge", "last-updated-at": "Darrera actualització el", - "layout": "Disseny", "learn-more": "Saber-ne més", "link": "Enllaç", "map": "Mapa", @@ -162,9 +161,7 @@ "has-task-list": "téLlistaTasques" }, "links": "Enllaços", - "list": "Llista", "load-more": "Carrega més", - "masonry": "Mosaic", "no-archived-memos": "No hi ha notes arxivades.", "no-memos": "No hi ha notes.", "order-by": "Ordena per", diff --git a/web/src/locales/cs.json b/web/src/locales/cs.json index 99e37dc83..8955aa14c 100644 --- a/web/src/locales/cs.json +++ b/web/src/locales/cs.json @@ -62,7 +62,6 @@ "input": "Vstup", "language": "Jazyk", "last-updated-at": "Změněno", - "layout": "Rozložení", "learn-more": "Více informací", "link": "Odkaz", "map": "Mapa", @@ -162,9 +161,7 @@ "has-task-list": "maSeznamUkolu" }, "links": "Odkazy", - "list": "Seznam", "load-more": "Načíst více", - "masonry": "Zdivo", "no-archived-memos": "Žádné archivované poznámky.", "no-memos": "Žádné poznámky.", "order-by": "Řadit dle", diff --git a/web/src/locales/de.json b/web/src/locales/de.json index fb5a2634b..1ec697be7 100644 --- a/web/src/locales/de.json +++ b/web/src/locales/de.json @@ -62,7 +62,6 @@ "input": "Eingabe", "language": "Sprache", "last-updated-at": "Zuletzt aktualisiert am", - "layout": "Layout", "learn-more": "Mehr erfahren", "link": "Link", "map": "Karte", @@ -162,9 +161,7 @@ "has-task-list": "hatAufgabenliste" }, "links": "Links", - "list": "Liste", "load-more": "Mehr laden", - "masonry": "Masonry", "no-archived-memos": "Keine archivierten Notizen.", "no-memos": "Keine Notizen.", "order-by": "Sortieren nach", diff --git a/web/src/locales/en.json b/web/src/locales/en.json index 3941a1d45..841adf60c 100644 --- a/web/src/locales/en.json +++ b/web/src/locales/en.json @@ -62,7 +62,6 @@ "input": "Input", "language": "Language", "last-updated-at": "Last updated at", - "layout": "Layout", "learn-more": "Learn more", "link": "Link", "map": "Map", @@ -168,9 +167,7 @@ "label": "Filters" }, "links": "Links", - "list": "List", "load-more": "Load more", - "masonry": "Masonry", "no-archived-memos": "No archived memos.", "no-memos": "No memos.", "order-by": "Order By", diff --git a/web/src/locales/es.json b/web/src/locales/es.json index fe5eb39c9..2e2ca4f41 100644 --- a/web/src/locales/es.json +++ b/web/src/locales/es.json @@ -62,7 +62,6 @@ "input": "Entrada", "language": "Idioma", "last-updated-at": "Última actualización en", - "layout": "Diseño", "learn-more": "Saber más", "link": "Enlace", "map": "Mapa", @@ -162,9 +161,7 @@ "has-task-list": "tieneListaTareas" }, "links": "Enlaces", - "list": "Lista", "load-more": "Cargar más", - "masonry": "Mosaico", "no-archived-memos": "No hay memos archivados.", "no-memos": "No hay memos.", "order-by": "Ordenar por", diff --git a/web/src/locales/fa.json b/web/src/locales/fa.json index 5a1e14880..4f5a7b43f 100644 --- a/web/src/locales/fa.json +++ b/web/src/locales/fa.json @@ -62,7 +62,6 @@ "input": "ورودی", "language": "زبان", "last-updated-at": "آخرین بروزرسانی در", - "layout": "چیدمان", "learn-more": "اطلاعات بیشتر", "link": "پیوست", "map": "نقشه", @@ -162,9 +161,7 @@ "has-task-list": "دارای لیست کارها" }, "links": "پیوندها", - "list": "لیست", "load-more": "بارگذاری بیشتر", - "masonry": "آجرچینی", "no-archived-memos": "یادداشت آرشیو شده‌ای وجود ندارد.", "no-memos": "یادداشتی یافت نشد.", "order-by": "مرتب‌سازی بر اساس", diff --git a/web/src/locales/fr.json b/web/src/locales/fr.json index 15610f204..4230496b4 100644 --- a/web/src/locales/fr.json +++ b/web/src/locales/fr.json @@ -62,7 +62,6 @@ "input": "Entrée", "language": "Langue", "last-updated-at": "Dernière mise à jour le", - "layout": "Disposition", "learn-more": "En savoir plus", "link": "Lien", "map": "Carte", @@ -162,9 +161,7 @@ "has-task-list": "aListeTâches" }, "links": "Liens", - "list": "Liste", "load-more": "Charger plus", - "masonry": "Mosaïque", "no-archived-memos": "Aucune note archivée.", "no-memos": "Aucune note.", "order-by": "Trier par", diff --git a/web/src/locales/gl.json b/web/src/locales/gl.json index 1b8c638e0..ebdfc5c28 100644 --- a/web/src/locales/gl.json +++ b/web/src/locales/gl.json @@ -62,7 +62,6 @@ "input": "Entrada", "language": "Idioma", "last-updated-at": "Última actualización", - "layout": "Disposición", "learn-more": "Saber máis", "link": "Ligar", "map": "Map", @@ -162,9 +161,7 @@ "has-task-list": "hasTaskList" }, "links": "Ligazóns", - "list": "Lista", "load-more": "Cargar máis", - "masonry": "Masonry", "no-archived-memos": "Sen memos arquivadas.", "no-memos": "Sen memos.", "order-by": "Orde por", diff --git a/web/src/locales/hi.json b/web/src/locales/hi.json index 4b97caff3..446b62e15 100644 --- a/web/src/locales/hi.json +++ b/web/src/locales/hi.json @@ -62,7 +62,6 @@ "input": "इनपुट", "language": "भाषा", "last-updated-at": "अंतिम अपडेट", - "layout": "लेआउट", "learn-more": "अधिक जानें", "link": "लिंक", "map": "नक्शा", @@ -162,9 +161,7 @@ "has-task-list": "टास्क लिस्ट है" }, "links": "लिंक", - "list": "सूची", "load-more": "और लोड करें", - "masonry": "मेसनरी", "no-archived-memos": "कोई संग्रहीत मेमो नहीं।", "no-memos": "कोई मेमो नहीं।", "order-by": "क्रमबद्ध करें", diff --git a/web/src/locales/hr.json b/web/src/locales/hr.json index 6c68658ca..33a45a54f 100644 --- a/web/src/locales/hr.json +++ b/web/src/locales/hr.json @@ -62,7 +62,6 @@ "input": "Unos", "language": "Jezik", "last-updated-at": "Zadnje ažurirano", - "layout": "Raspored", "learn-more": "Nauči više", "link": "Link", "map": "Karta", @@ -162,9 +161,7 @@ "has-task-list": "imaZadatke" }, "links": "Linkovi", - "list": "Popis", "load-more": "Učitaj više", - "masonry": "Zidni raspored", "no-archived-memos": "Nema arhiviranih memoa.", "no-memos": "Nema memoa.", "order-by": "Sortiraj po", diff --git a/web/src/locales/hu.json b/web/src/locales/hu.json index 92f5ee26b..46393b36f 100644 --- a/web/src/locales/hu.json +++ b/web/src/locales/hu.json @@ -62,7 +62,6 @@ "input": "Bemenet", "language": "Nyelv", "last-updated-at": "Utoljára frissítve", - "layout": "Elrendezés", "learn-more": "További információ", "link": "Hivatkozás", "map": "Térkép", @@ -162,9 +161,7 @@ "has-task-list": "vanFeladatLista" }, "links": "Hivatkozások", - "list": "Lista", "load-more": "Több betöltése", - "masonry": "Mozaik elrendezés", "no-archived-memos": "Nincsenek archivált jegyzetek.", "no-memos": "Nincsenek jegyzetek.", "order-by": "Rendezés", diff --git a/web/src/locales/id.json b/web/src/locales/id.json index d10724812..7804bbf76 100644 --- a/web/src/locales/id.json +++ b/web/src/locales/id.json @@ -62,7 +62,6 @@ "input": "Masukan", "language": "Bahasa", "last-updated-at": "Terakhir diperbarui pada", - "layout": "Tata letak", "learn-more": "Pelajari lebih lanjut", "link": "Tautan", "map": "Peta", @@ -162,9 +161,7 @@ "has-task-list": "Memiliki daftar tugas" }, "links": "Tautan", - "list": "Daftar", "load-more": "Muat lebih banyak", - "masonry": "Masonry", "no-archived-memos": "Tidak ada memo yang diarsipkan.", "no-memos": "Tidak ada memo.", "order-by": "Urutkan Berdasarkan", diff --git a/web/src/locales/it.json b/web/src/locales/it.json index 5bc515b55..aaecabac0 100644 --- a/web/src/locales/it.json +++ b/web/src/locales/it.json @@ -62,7 +62,6 @@ "input": "Input", "language": "Lingua", "last-updated-at": "Ultimo aggiornamento", - "layout": "Layout", "learn-more": "Scopri di più", "link": "Link", "map": "Mappa", @@ -162,9 +161,7 @@ "has-task-list": "haListaCompiti" }, "links": "Link", - "list": "Lista", "load-more": "Carica altro", - "masonry": "Masonry", "no-archived-memos": "Nessun memo archiviato.", "no-memos": "Nessun memo.", "order-by": "Ordina per", diff --git a/web/src/locales/ja.json b/web/src/locales/ja.json index c43b3926e..85e5ea126 100644 --- a/web/src/locales/ja.json +++ b/web/src/locales/ja.json @@ -62,7 +62,6 @@ "input": "入力", "language": "言語", "last-updated-at": "最終更新日時", - "layout": "レイアウト", "learn-more": "さらに詳しく", "link": "リンク", "map": "マップ", @@ -162,9 +161,7 @@ "has-task-list": "タスクリストあり" }, "links": "リンク", - "list": "リスト", "load-more": "さらに読み込む", - "masonry": "マソンリーレイアウト", "no-archived-memos": "アーカイブされたメモはありません。", "no-memos": "メモがありません。", "order-by": "並び替え", diff --git a/web/src/locales/ka-GE.json b/web/src/locales/ka-GE.json index c5f736013..14951b95c 100644 --- a/web/src/locales/ka-GE.json +++ b/web/src/locales/ka-GE.json @@ -62,7 +62,6 @@ "input": "შეყვანა", "language": "ენა", "last-updated-at": "ბოლო განახლება", - "layout": "განლაგება", "learn-more": "გაიგეთ მეტი", "link": "ლინკი", "map": "რუკა", @@ -162,9 +161,7 @@ "has-task-list": "სიისამოცანებია" }, "links": "ლინკები", - "list": "სია", "load-more": "მეტის ჩატვირთვა", - "masonry": "მესონრი", "no-archived-memos": "დაარქივებული მემოები არ არის.", "no-memos": "მემოები არ არის.", "order-by": "დალაგება", diff --git a/web/src/locales/ko.json b/web/src/locales/ko.json index f3c86f8c8..c9723d5be 100644 --- a/web/src/locales/ko.json +++ b/web/src/locales/ko.json @@ -62,7 +62,6 @@ "input": "입력", "language": "언어", "last-updated-at": "마지막 수정일", - "layout": "레이아웃", "learn-more": "더 보기", "link": "링크", "map": "지도", @@ -162,9 +161,7 @@ "has-task-list": "할일목록있음" }, "links": "링크", - "list": "목록", "load-more": "더보기", - "masonry": "메이슨리", "no-archived-memos": "보관처리된 메모가 없습니다.", "no-memos": "메모가 없습니다.", "order-by": "정렬 기준", diff --git a/web/src/locales/mr.json b/web/src/locales/mr.json index ecf231151..49bad3e4b 100644 --- a/web/src/locales/mr.json +++ b/web/src/locales/mr.json @@ -62,7 +62,6 @@ "input": "इनपुट", "language": "भाषा", "last-updated-at": "शेवटचे अद्यतन", - "layout": "लेआउट", "learn-more": "अधिक जाणून घ्या", "link": "लिंक", "map": "नकाशा", @@ -162,9 +161,7 @@ "has-task-list": "कार्यसूची आहे" }, "links": "लिंक्स", - "list": "यादी", "load-more": "अधिक लोड करा", - "masonry": "मेसनरी", "no-archived-memos": "कोणतेही संग्रहित मेमो नाहीत.", "no-memos": "कोणतेही मेमो नाहीत.", "order-by": "क्रमवारी", diff --git a/web/src/locales/nb.json b/web/src/locales/nb.json index b72fbeecf..c3fb0862e 100644 --- a/web/src/locales/nb.json +++ b/web/src/locales/nb.json @@ -62,7 +62,6 @@ "input": "Input", "language": "Språk", "last-updated-at": "Sist oppdatert", - "layout": "Layout", "learn-more": "Lær mer", "link": "Link", "map": "Kart", @@ -162,9 +161,7 @@ "has-task-list": "harGjøremålsListe" }, "links": "Linker", - "list": "Liste", "load-more": "Last inn mer", - "masonry": "Masonry", "no-archived-memos": "Ingen arkiverte memoer.", "no-memos": "Ingen memoer.", "order-by": "Sorter etter", diff --git a/web/src/locales/nl.json b/web/src/locales/nl.json index 13336835b..26e917479 100644 --- a/web/src/locales/nl.json +++ b/web/src/locales/nl.json @@ -62,7 +62,6 @@ "input": "Invoer", "language": "Taal", "last-updated-at": "Laatst bijgewerkt op", - "layout": "Indeling", "learn-more": "Meer informatie", "link": "Link", "map": "Kaart", @@ -162,9 +161,7 @@ "has-task-list": "heeftTakenlijst" }, "links": "Links", - "list": "Lijst", "load-more": "Meer laden", - "masonry": "Tegelruit", "no-archived-memos": "Geen gearchiveerde memos.", "no-memos": "Geen memos.", "order-by": "Sorteren op", diff --git a/web/src/locales/pl.json b/web/src/locales/pl.json index 6806cff37..ab04ac284 100644 --- a/web/src/locales/pl.json +++ b/web/src/locales/pl.json @@ -62,7 +62,6 @@ "input": "Wejście", "language": "Język", "last-updated-at": "Ostatnia aktualizacja", - "layout": "Układ", "learn-more": "Dowiedz się więcej", "link": "Link", "map": "Mapa", @@ -163,9 +162,7 @@ "has-task-list": "ma listę zadań" }, "links": "Linki", - "list": "Lista", "load-more": "Załaduj więcej", - "masonry": "Masonry", "no-archived-memos": "Brak zarchiwizowanych notatek.", "no-memos": "Brak notatek.", "order-by": "Sortuj według", diff --git a/web/src/locales/pt-BR.json b/web/src/locales/pt-BR.json index 2a40d96ac..b576f04e3 100644 --- a/web/src/locales/pt-BR.json +++ b/web/src/locales/pt-BR.json @@ -62,7 +62,6 @@ "input": "Entrada", "language": "Idioma", "last-updated-at": "Atualizado em", - "layout": "Layout", "learn-more": "Saiba mais", "link": "Link", "map": "Mapa", @@ -162,9 +161,7 @@ "has-task-list": "temListaDeTarefas" }, "links": "Links", - "list": "Lista", "load-more": "Carregar mais", - "masonry": "Mosaico", "no-archived-memos": "Nenhum memo arquivado.", "no-memos": "Nenhum memo.", "order-by": "Ordenar por", diff --git a/web/src/locales/pt-PT.json b/web/src/locales/pt-PT.json index 04c770342..bcf973838 100644 --- a/web/src/locales/pt-PT.json +++ b/web/src/locales/pt-PT.json @@ -62,7 +62,6 @@ "input": "Entrada", "language": "Idioma", "last-updated-at": "Última atualização em", - "layout": "Layout", "learn-more": "Saiba mais", "link": "Link", "map": "Mapa", @@ -162,9 +161,7 @@ "has-task-list": "temListaDeTarefas" }, "links": "Links", - "list": "Lista", "load-more": "Carregar mais", - "masonry": "Masonry", "no-archived-memos": "Não existem memos arquivados.", "no-memos": "Não existem memos.", "order-by": "Ordenar por", diff --git a/web/src/locales/ru.json b/web/src/locales/ru.json index 2a215ef2e..16f52d5cb 100644 --- a/web/src/locales/ru.json +++ b/web/src/locales/ru.json @@ -62,7 +62,6 @@ "input": "Напишите...", "language": "Язык", "last-updated-at": "Изменено", - "layout": "Макет", "learn-more": "Узнать больше", "link": "Ссылка", "map": "Карта", @@ -162,9 +161,7 @@ "has-task-list": "hasTaskList" }, "links": "Ссылки", - "list": "Список", "load-more": "Загрузить еще", - "masonry": "Плитки", "no-archived-memos": "Нет заархивированных записей.", "no-memos": "Нет заметок.", "order-by": "Сортировать по", diff --git a/web/src/locales/sl.json b/web/src/locales/sl.json index eb8285e0d..7a56b87fa 100644 --- a/web/src/locales/sl.json +++ b/web/src/locales/sl.json @@ -62,7 +62,6 @@ "input": "Vnos", "language": "Jezik", "last-updated-at": "Zadnja posodobitev", - "layout": "Postavitev", "learn-more": "Spoznaj več", "link": "Povezava", "map": "Zemljevid", @@ -177,9 +176,7 @@ "private": "Zasebno", "protected": "Za uporabnike", "public": "Javno" - }, - "list": "Seznam", - "masonry": "Mozaik" + } }, "message": { "archived-successfully": "Uspešno arhivirano", diff --git a/web/src/locales/sv.json b/web/src/locales/sv.json index 76ec35e56..b618f4594 100644 --- a/web/src/locales/sv.json +++ b/web/src/locales/sv.json @@ -62,7 +62,6 @@ "input": "Inmatning", "language": "Språk", "last-updated-at": "Senast uppdaterad", - "layout": "Layout", "learn-more": "Läs mer", "link": "Länk", "map": "Karta", @@ -162,9 +161,7 @@ "has-task-list": "harAttGöraLista" }, "links": "Länkar", - "list": "Lista", "load-more": "Ladda mer", - "masonry": "Mosaik", "no-archived-memos": "Inga arkiverade anteckningar.", "no-memos": "Inga anteckningar.", "order-by": "Sortera efter", diff --git a/web/src/locales/th.json b/web/src/locales/th.json index 5dc9dfd05..0b5bcc345 100644 --- a/web/src/locales/th.json +++ b/web/src/locales/th.json @@ -62,7 +62,6 @@ "input": "อินพุต", "language": "ภาษา", "last-updated-at": "อัปเดตล่าสุดเมื่อ", - "layout": "เลย์เอาต์", "learn-more": "เรียนรู้เพิ่มเติม", "link": "ลิงก์", "map": "แผนที่", @@ -162,9 +161,7 @@ "has-task-list": "มีรายการที่ต้องทำ" }, "links": "ลิงก์", - "list": "รายการ", "load-more": "โหลดเพิ่มเติม", - "masonry": "มอซอนิก", "no-archived-memos": "ไม่มีบันทึกช่วยจำที่ถูกเก็บถาวร", "no-memos": "ไม่มีบันทึกช่วยจำ", "order-by": "เรียงตาม", diff --git a/web/src/locales/tr.json b/web/src/locales/tr.json index 542fcd3d2..98aafcd30 100644 --- a/web/src/locales/tr.json +++ b/web/src/locales/tr.json @@ -62,7 +62,6 @@ "input": "Girdi", "language": "Dil", "last-updated-at": "Son güncelleme", - "layout": "Yerleşim", "learn-more": "Daha fazla bilgi", "link": "Bağlantı", "map": "Harita", @@ -162,9 +161,7 @@ "has-task-list": "görevListesiVar" }, "links": "Bağlantılar", - "list": "Liste", "load-more": "Daha fazla yükle", - "masonry": "Mozaik", "no-archived-memos": "Arşivlenmiş not yok.", "no-memos": "Not yok.", "order-by": "Sıralama Ölçütü", diff --git a/web/src/locales/uk.json b/web/src/locales/uk.json index d666125bb..c479c2b02 100644 --- a/web/src/locales/uk.json +++ b/web/src/locales/uk.json @@ -62,7 +62,6 @@ "input": "Ввід", "language": "Мова", "last-updated-at": "Останнє оновлення", - "layout": "Макет", "learn-more": "Дізнатися більше", "link": "Посилання", "map": "Мапа", @@ -162,9 +161,7 @@ "has-task-list": "єСписокЗавдань" }, "links": "Посилання", - "list": "Список", "load-more": "Завантажити більше", - "masonry": "Плитка", "no-archived-memos": "Немає архівованих нотаток.", "no-memos": "Немає нотаток.", "order-by": "Впорядкувати за", diff --git a/web/src/locales/vi.json b/web/src/locales/vi.json index 896b6a76b..be0cff9a9 100644 --- a/web/src/locales/vi.json +++ b/web/src/locales/vi.json @@ -62,7 +62,6 @@ "input": "Nhập liệu", "language": "Ngôn ngữ", "last-updated-at": "Cập nhật lần cuối", - "layout": "Bố cục", "learn-more": "Tìm hiểu thêm", "link": "Liên kết", "map": "Bản đồ", @@ -162,9 +161,7 @@ "has-task-list": "cóDanhSáchViệc" }, "links": "Liên kết", - "list": "Danh sách", "load-more": "Tải thêm", - "masonry": "Xếp ô", "no-archived-memos": "Không có ghi chú nào được lưu trữ.", "no-memos": "Không có ghi chú nào.", "order-by": "Sắp xếp theo", diff --git a/web/src/locales/zh-Hans.json b/web/src/locales/zh-Hans.json index 57f6cb08b..22814d205 100644 --- a/web/src/locales/zh-Hans.json +++ b/web/src/locales/zh-Hans.json @@ -62,7 +62,6 @@ "input": "输入", "language": "语言", "last-updated-at": "最后更新时间", - "layout": "布局", "learn-more": "了解更多", "link": "链接", "map": "地图", @@ -162,9 +161,7 @@ "has-task-list": "有待办" }, "links": "链接", - "list": "列表模式", "load-more": "加载更多", - "masonry": "瀑布流模式", "no-archived-memos": "没有已归档备忘录。", "no-memos": "无备忘录", "order-by": "排序", diff --git a/web/src/locales/zh-Hant.json b/web/src/locales/zh-Hant.json index 395ef9396..aada603f1 100644 --- a/web/src/locales/zh-Hant.json +++ b/web/src/locales/zh-Hant.json @@ -62,7 +62,6 @@ "input": "輸入", "language": "語言", "last-updated-at": "最後更新於", - "layout": "排版", "learn-more": "了解更多", "link": "連結", "map": "地圖", @@ -162,9 +161,7 @@ "has-task-list": "有待辦事項" }, "links": "連結", - "list": "列表", "load-more": "載入更多", - "masonry": "瀑布流", "no-archived-memos": "無已封存的備忘錄", "no-memos": "無備忘錄", "order-by": "排序",