mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-08-07 01:03:34 +02:00
Allow favorites to be sorted by type. Closes #314.
This commit is contained in:
@@ -23,6 +23,7 @@ import {
|
||||
} from '../types';
|
||||
import {
|
||||
buildSidebarSectionSortOrders,
|
||||
FAVORITES_SORT_CYCLE,
|
||||
getStateKey,
|
||||
loadLegacyLocalStorageSortOrder,
|
||||
loadLocalStorageSidebarSectionSortOrders,
|
||||
@@ -42,6 +43,60 @@ import { cn } from '@/lib/utils';
|
||||
|
||||
type FavoriteItem = { type: 'channel'; channel: Channel } | { type: 'contact'; contact: Contact };
|
||||
|
||||
// Grouping order for the Favorites "by type" sorts. Mirrors the standalone sidebar
|
||||
// section order: Channels, Contacts (clients/sensors/unknown), Rooms, Repeaters.
|
||||
function favoriteTypeRank(item: FavoriteItem): number {
|
||||
if (item.type === 'channel') return 0;
|
||||
switch (item.contact.type) {
|
||||
case CONTACT_TYPE_ROOM:
|
||||
return 2;
|
||||
case CONTACT_TYPE_REPEATER:
|
||||
return 3;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// The next order when the section's sort toggle is clicked. Favorites cycles
|
||||
// through all four orders; every other (single-type) section flips recent<->alpha.
|
||||
function nextSortOrder(section: SidebarSortableSection, current: SortOrder): SortOrder {
|
||||
if (section === 'favorites') {
|
||||
const idx = FAVORITES_SORT_CYCLE.indexOf(current);
|
||||
return FAVORITES_SORT_CYCLE[(idx + 1) % FAVORITES_SORT_CYCLE.length];
|
||||
}
|
||||
return current === 'alpha' ? 'recent' : 'alpha';
|
||||
}
|
||||
|
||||
// Compact glyph/text shown on the toggle for the current order.
|
||||
function sortOrderLabel(order: SortOrder): string {
|
||||
switch (order) {
|
||||
case 'alpha':
|
||||
return 'A-Z';
|
||||
case 'type-recent':
|
||||
return 'Type ⏱';
|
||||
case 'type-alpha':
|
||||
return 'Type A-Z';
|
||||
case 'recent':
|
||||
default:
|
||||
return '⏱';
|
||||
}
|
||||
}
|
||||
|
||||
// Human phrase for aria/title, describing what the order sorts by.
|
||||
function sortOrderDescription(order: SortOrder): string {
|
||||
switch (order) {
|
||||
case 'alpha':
|
||||
return 'alphabetically';
|
||||
case 'type-recent':
|
||||
return 'by type, then recent';
|
||||
case 'type-alpha':
|
||||
return 'by type, then alphabetically';
|
||||
case 'recent':
|
||||
default:
|
||||
return 'by recent';
|
||||
}
|
||||
}
|
||||
|
||||
type ConversationRow = {
|
||||
key: string;
|
||||
type: 'channel' | 'contact';
|
||||
@@ -159,7 +214,7 @@ export function Sidebar({
|
||||
|
||||
const handleSortToggle = (section: SidebarSortableSection) => {
|
||||
setSectionSortOrders((prev) => {
|
||||
const nextOrder = prev[section] === 'alpha' ? 'recent' : 'alpha';
|
||||
const nextOrder = nextSortOrder(section, prev[section]);
|
||||
const updated = { ...prev, [section]: nextOrder };
|
||||
saveLocalStorageSidebarSectionSortOrders(updated);
|
||||
return updated;
|
||||
@@ -323,9 +378,16 @@ export function Sidebar({
|
||||
);
|
||||
|
||||
const sortFavoriteItemsByOrder = useCallback(
|
||||
(items: FavoriteItem[], order: SortOrder) =>
|
||||
[...items].sort((a, b) => {
|
||||
if (order === 'recent') {
|
||||
(items: FavoriteItem[], order: SortOrder) => {
|
||||
const typeGrouped = order === 'type-recent' || order === 'type-alpha';
|
||||
const byRecent = order === 'recent' || order === 'type-recent';
|
||||
return [...items].sort((a, b) => {
|
||||
if (typeGrouped) {
|
||||
const rankDiff = favoriteTypeRank(a) - favoriteTypeRank(b);
|
||||
if (rankDiff !== 0) return rankDiff;
|
||||
}
|
||||
|
||||
if (byRecent) {
|
||||
const timeA =
|
||||
a.type === 'channel'
|
||||
? getLastMessageTime('channel', a.channel.key)
|
||||
@@ -340,7 +402,8 @@ export function Sidebar({
|
||||
}
|
||||
|
||||
return getFavoriteItemName(a).localeCompare(getFavoriteItemName(b));
|
||||
}),
|
||||
});
|
||||
},
|
||||
[getContactRecentTime, getFavoriteItemName, getLastMessageTime]
|
||||
);
|
||||
|
||||
@@ -802,20 +865,18 @@ export function Sidebar({
|
||||
<div className="ml-auto flex items-center gap-1.5">
|
||||
{sortSection && sectionSortOrder && (
|
||||
<button
|
||||
className="bg-transparent text-muted-foreground/60 px-1 py-0.5 text-[0.625rem] rounded hover:text-foreground transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
className="bg-transparent text-muted-foreground/60 px-1 py-0.5 text-[0.625rem] rounded hover:text-foreground transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring whitespace-nowrap"
|
||||
onClick={() => handleSortToggle(sortSection)}
|
||||
aria-label={
|
||||
sectionSortOrder === 'alpha'
|
||||
? `Sort ${title} by recent`
|
||||
: `Sort ${title} alphabetically`
|
||||
}
|
||||
title={
|
||||
sectionSortOrder === 'alpha'
|
||||
? `Sort ${title} by recent`
|
||||
: `Sort ${title} alphabetically`
|
||||
}
|
||||
aria-label={`Sort ${title} ${sortOrderDescription(
|
||||
nextSortOrder(sortSection, sectionSortOrder)
|
||||
)}`}
|
||||
title={`Currently sorted ${sortOrderDescription(
|
||||
sectionSortOrder
|
||||
)}. Click to sort ${sortOrderDescription(
|
||||
nextSortOrder(sortSection, sectionSortOrder)
|
||||
)}.`}
|
||||
>
|
||||
{sectionSortOrder === 'alpha' ? 'A-Z' : '⏱'}
|
||||
{sortOrderLabel(sectionSortOrder)}
|
||||
</button>
|
||||
)}
|
||||
{unreadCount > 0 && (
|
||||
|
||||
Reference in New Issue
Block a user