All files / src/hooks useGroupHierarchy.ts

100% Statements 40/40
100% Branches 8/8
100% Functions 17/17
100% Lines 31/31

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91                          5x 634x 634x 634x   634x 128x 126x         125x 125x   1x 126x     634x 120x     634x   634x 222x       634x   63x       634x   634x   4x   3x 4x 3x         634x   4x 3x 3x         634x 58x       634x                            
// SPDX-License-Identifier: GPL-3.0-only
// Copyright (C) 2026 Max Boksem. See NOTICE for additional terms under GPLv3 section 7.
 
import { useState, useEffect, useCallback, useMemo } from "react";
import {
	getGroupHierarchy,
	getPinnedGroups,
	pinGroup as pinGroupApi,
	unpinGroup as unpinGroupApi,
} from "@/lib/tauri";
import { superCategoryNames } from "@/lib/group-hierarchy";
import type { GroupHierarchyEntry, PinnedGroup } from "@/lib/types";
 
export const useGroupHierarchy = (providerId: string | null, contentType: string) => {
	const [entries, setEntries] = useState<GroupHierarchyEntry[]>([]);
	const [pinnedGroups, setPinnedGroups] = useState<PinnedGroup[]>([]);
	const [loaded, setLoaded] = useState(false);
 
	const load = useCallback(async () => {
		if (!providerId) return;
		return Promise.all([
			getGroupHierarchy(providerId, contentType),
			getPinnedGroups(providerId, contentType),
		])
			.then(([h, p]) => {
				setEntries(h);
				setPinnedGroups(p);
			})
			.catch((err) => console.error("Failed to load group hierarchy:", err))
			.finally(() => setLoaded(true));
	}, [providerId, contentType]);
 
	useEffect(() => {
		load();
	}, [load]);
 
	const superCategories = useMemo(() => superCategoryNames(entries), [entries]);
 
	const topLevelGroups = useMemo(
		() => entries.filter((e) => !e.superCategory).map((e) => e.groupName),
		[entries]
	);
 
	const getGroupsForCategory = useCallback(
		(category: string) =>
			entries.filter((e) => e.superCategory === category).map((e) => e.groupName),
		[entries]
	);
 
	const hasHierarchy = superCategories.length > 0;
 
	const pinGroup = useCallback(
		async (groupName: string) => {
			if (!providerId) return;
			const nextOrder =
				pinnedGroups.length > 0 ? pinnedGroups[pinnedGroups.length - 1].sortOrder + 100 : 0;
			await pinGroupApi(providerId, contentType, groupName, nextOrder);
			await load();
		},
		[providerId, contentType, pinnedGroups, load]
	);
 
	const unpinGroup = useCallback(
		async (groupName: string) => {
			if (!providerId) return;
			await unpinGroupApi(providerId, contentType, groupName);
			await load();
		},
		[providerId, contentType, load]
	);
 
	const isPinned = useCallback(
		(groupName: string) => pinnedGroups.some((p) => p.groupName === groupName),
		[pinnedGroups]
	);
 
	return {
		entries,
		pinnedGroups,
		loaded,
		superCategories,
		topLevelGroups,
		hasHierarchy,
		getGroupsForCategory,
		pinGroup,
		unpinGroup,
		isPinned,
		reload: load,
	};
};