import { ReactElement, useState, useMemo } from 'react'; import { useAppDispatch, useAppSelector } from '../../store/hooks'; import { clearSelectionAndSelectMulti, selectSimilarUrls, selectSelectedUrls } from '../../store/uiSlice'; import { selectWindowsSortedExcept } from '../../store/selectors'; import { selectTabs, selectDuplicateUrls, dedupAll } from '../../store/windowsSlice'; import WindowTabList from './WindowTabList'; import { useLiveQuery } from 'dexie-react-hooks'; import { getTabvanaWindowId } from '../../db/prefs'; import FlexViewHeader from '../flexView/FlexViewHeader'; import Inspector from '../inspector/Inspector'; import { RootState } from '../../store/rootState'; import SplitPane from 'react-split-pane'; import { Page, PageFilter, PageSortOrder, fetchPagesByNormalizedUrls, fetchPages } from '../../db/pages'; import { sortBy } from 'lodash'; import { pageSortOrderMap, filterPage } from '../sort'; import PageRow from '../flexView/pagelists/pagerow/PageRow'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faClone } from '@fortawesome/free-solid-svg-icons'; import IconButton from '@mui/joy/IconButton'; import { Tooltip } from '@mui/joy'; const Windows = (): ReactElement => { // const inspectorSize = useAppSelector(selectInspectorSize); const [filter, setFilter] = useState({ flagged: false }); const [groupByWindow, setGroupByWindow] = useState(true); const [sortOrder, setSortOrder] = useState(PageSortOrder.recent); const tabvanaWindowId = useLiveQuery(() => { return getTabvanaWindowId(); }, []); const windowsSorted = useAppSelector((state: RootState) => selectWindowsSortedExcept(state, tabvanaWindowId) ); const tabs = useAppSelector(selectTabs); const dispatch = useAppDispatch(); const duplicateUrls = useAppSelector(selectDuplicateUrls); const similarUrls = useAppSelector(selectSimilarUrls); const selectedUrls = useAppSelector(selectSelectedUrls); // Data for Flat View const allTabIds = useMemo(() => { if (!windowsSorted) return []; return windowsSorted.flatMap(([_, w]) => w.tabIds); }, [windowsSorted]); const allTabObjects = useMemo(() => allTabIds.map(id => tabs[id]).filter(t => t && t.url), [allTabIds, tabs]); const allUrls = useMemo(() => allTabObjects.map(t => t.url!.normalized), [allTabObjects]); const pages = useLiveQuery(() => fetchPages(allUrls), [allUrls]); let content; if (!windowsSorted) { content = <>; } else if (groupByWindow) { content = windowsSorted.map(([windowId, window]) => ( )); } else { if (!pages) { content = <>; // Loading? } else { const pageMap = new Map(pages.map(p => [p.nurl.normalized, p])); // Enhance tabs const enrichedTabs = allTabObjects.map(tab => ({ tab, page: pageMap.get(tab.url!.normalized) })).filter(item => item.page); // Filter const filteredItems = enrichedTabs.filter(item => filterPage(item.page!, filter)); let sortedItems = filteredItems; if (sortOrder === PageSortOrder.similarity) { sortedItems = sortBy(filteredItems, [ (item) => !selectedUrls.includes(item.page!.nurl.normalized), (item) => -(similarUrls[item.page!.nurl.normalized] || 0) ]); } else if (sortOrder !== PageSortOrder.recent) { sortedItems = sortBy(filteredItems, (item) => pageSortOrderMap[sortOrder](item.page!)); } content = (
{sortedItems.map(item => ( ))}
); } } const dedupButton = duplicateUrls.length > 0 && ( dispatch(dedupAll())} title="Deduplicate" > {duplicateUrls.length} ); return ( /* this lib is incompatible with react18. To fix children: React.ReactNode; needs to be added to SplitPaneProps. @ts-ignore TS2322 */ dispatch(setInspectorSize(size))} >
{content}
{ // Select all visible if (!windowsSorted) return; // Reuse logic from content generation? expensive. // Just gather all available tabs in windowsSorted, pass to selector, relying on reducer to handle. // Usually selecting all involves selecting the items that are visible. // If filtered/sorted, we should select only those? // Existing logic selected ALL in windows irrespective of filter? // Existing logic: // const allTabsInWindows = windowsSorted.flatMap(...) // dispatch(clearSelectionAndSelectMulti(...)) // We probably want to respect the Filter at least. // But implementing exact "visible items" might be duplicated logic. // For now, let's keep selecting all open tabs as a safe default or improve it. // Given the user request is about sorting/grouping, let's stick to "select all open tabs" for now or // try to approximate if filter is on. const allTabsInWindows = windowsSorted .flatMap(([, window]) => window.tabIds) .map((tabId) => tabs[tabId]) .filter((tab) => tab != null && tab.url != null); let finalTabs = allTabsInWindows; // If we have access to loaded pages, we can filter. if (pages && filter.flagged) { const pageMap = new Map(pages.map(p => [p.nurl.normalized, p])); finalTabs = allTabsInWindows.filter(t => { const p = pageMap.get(t.url!.normalized); return p && filterPage(p, filter); }); } dispatch( clearSelectionAndSelectMulti({ urls: finalTabs.map((t) => ({ normalized: t.url!.normalized } as any)), }) ); }} > {dedupButton}
); }; export default Windows;