'use client';
import { useState, useEffect } from 'react';
import { PageControls } from '../shared/page-controls';
import { PageWrapper } from '../shared/page-wrapper';
import { SettingsCard } from '../shared/settings-card';
import { Select } from '../ui/select';
import { useUserData } from '@/context/userData';
import { IconButton } from '../ui/button';
import { Combobox } from '../ui/combobox';
import {
SORT_CRITERIA,
SORT_CRITERIA_DETAILS,
SORT_DIRECTIONS,
} from '../../../../core/src/utils/constants';
import {
DndContext,
useSensors,
useSensor,
PointerSensor,
TouchSensor,
} from '@dnd-kit/core';
import {
SortableContext,
useSortable,
arrayMove,
verticalListSortingStrategy,
} from '@dnd-kit/sortable';
import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
import { CSS } from '@dnd-kit/utilities';
import { ArrowDownAZ, ArrowUpAZ } from 'lucide-react';
import {
Accordion,
AccordionTrigger,
AccordionContent,
AccordionItem,
} from '../ui/accordion';
export function SortingMenu() {
return (
<>
>
);
}
function SortableItem({
id,
name,
description,
direction,
onDirectionChange,
}: {
id: string;
name: string;
description: string;
direction: (typeof SORT_DIRECTIONS)[number];
onDirectionChange: () => void;
}) {
const {
attributes,
listeners,
setNodeRef,
transform,
transition,
isDragging,
} = useSortable({ id });
const style = {
transform: CSS.Transform.toString(transform),
transition,
opacity: isDragging ? 0.5 : 1,
};
return (
{name}
{description}
:
}
intent="primary-subtle"
onClick={onDirectionChange}
/>
);
}
function Content() {
const [currentSortType, setCurrentSortType] = useState('global');
const { userData, setUserData } = useUserData();
const [isDragging, setIsDragging] = useState(false);
type SortCriteriaItem = {
key: (typeof SORT_CRITERIA)[number];
direction: (typeof SORT_DIRECTIONS)[number];
};
type SortCriteriaType = {
global: SortCriteriaItem[];
series: SortCriteriaItem[];
movies: SortCriteriaItem[];
cached: SortCriteriaItem[];
uncached: SortCriteriaItem[];
};
// Initialize sortCriteria if it doesn't exist
useEffect(() => {
if (!userData.sortCriteria) {
setUserData((prev) => ({
...prev,
sortCriteria: {
global: [],
series: [],
movies: [],
cached: [],
uncached: [],
cachedMovies: [],
uncachedMovies: [],
cachedSeries: [],
uncachedSeries: [],
},
}));
}
}, []);
const currentSortCriteria = (userData.sortCriteria?.[
currentSortType as keyof SortCriteriaType
] || []) as SortCriteriaItem[];
const getSortCriteriaDetails = (key: (typeof SORT_CRITERIA)[number]) => {
return SORT_CRITERIA_DETAILS[key];
};
const sensors = useSensors(
useSensor(PointerSensor),
useSensor(TouchSensor, {
activationConstraint: {
delay: 150,
tolerance: 8,
},
})
);
function handleDragEnd(event: any) {
const { active, over } = event;
if (!over) return;
if (active.id !== over.id) {
const oldIndex = currentSortCriteria.findIndex(
(item) => `${item.key}-${item.direction}` === active.id
);
const newIndex = currentSortCriteria.findIndex(
(item) => `${item.key}-${item.direction}` === over.id
);
const newSortCriteria = arrayMove(
currentSortCriteria,
oldIndex,
newIndex
);
setUserData((prev) => ({
...prev,
sortCriteria: {
...(prev.sortCriteria || {}),
[currentSortType]: newSortCriteria,
},
}));
}
setIsDragging(false);
}
function handleDragStart() {
setIsDragging(true);
}
useEffect(() => {
function preventTouchMove(e: TouchEvent) {
if (isDragging) {
e.preventDefault();
}
}
if (isDragging) {
document.body.addEventListener('touchmove', preventTouchMove, {
passive: false,
});
} else {
document.body.removeEventListener('touchmove', preventTouchMove);
}
return () => {
document.body.removeEventListener('touchmove', preventTouchMove);
};
}, [isDragging]);
return (
<>
Sorting
Configure how your content is sorted and organized.
How Sorting Works
The sorting system uses a hierarchical approach to determine
how content is sorted:
-
Primary Sorts: Define
the main sorting order for different content types:
-
Global: Used as a fallback when no specific type sort is
defined (You most likely just want to define this and
ignore everything else.)
- Movies: Specific sorting for movies
- Series: Specific sorting for TV series
- Anime: Specific sorting for anime content
-
Cached/Uncached Sorting:
{' '}
If your primary sort has "cached" at the top:
- Content is split into cached and uncached groups
-
Each group is sorted separately using its specific sort
criteria
-
Groups are then combined based on the cached sort
direction
-
Type-specific cached/uncached sorts (e.g., "Cached
Movies") take priority over global cached/uncached sorts
Note: The system will use the most specific sort criteria
available for the content type and cache status.
item.key)}
emptyMessage="No sort criteria available"
onValueChange={(value) => {
const typedValue = value as (typeof SORT_CRITERIA)[number][];
// Preserve existing directions for items that already exist
const newCriteria: SortCriteriaItem[] = typedValue.map((key) => {
const existingItem = currentSortCriteria.find(
(item) => item.key === key
);
return (
existingItem || {
key: key as (typeof SORT_CRITERIA)[number],
direction: getSortCriteriaDetails(key).defaultDirection,
}
);
});
setUserData((prev) => ({
...prev,
sortCriteria: {
...(prev.sortCriteria || {}),
[currentSortType]: newCriteria,
},
}));
}}
options={SORT_CRITERIA.map((criteria) => ({
label: getSortCriteriaDetails(criteria).name,
textValue: getSortCriteriaDetails(criteria).name,
value: criteria,
}))}
/>
{currentSortCriteria.length > 0 && (
`${item.key}-${item.direction}`
)}
strategy={verticalListSortingStrategy}
>
{currentSortCriteria.map((item) => (
{
const newDirection =
item.direction === 'asc'
? ('desc' as const)
: ('asc' as const);
setUserData((prev) => {
const prevCriteria =
prev.sortCriteria?.[
currentSortType as keyof SortCriteriaType
] || [];
return {
...prev,
sortCriteria: {
...(prev.sortCriteria || {}),
[currentSortType]: prevCriteria.map(
(criteria: SortCriteriaItem) =>
criteria.key === item.key
? { ...criteria, direction: newDirection }
: criteria
),
},
};
});
}}
/>
))}
)}
>
);
}