import { ArrowUpDown, UserMinus } from 'lucide-vue-next';
import { Button } from '../../components/ui/button';
import { Checkbox } from '../../components/ui/checkbox';
import { TooltipWrapper } from '../../components/ui/tooltip';
import { i18n } from '../../plugin';
import {
formatDateFilter,
getFaviconUrl,
languageClass,
openExternalLink,
sortStatus,
statusClass,
timeToText,
userImage
} from '../../shared/utils';
const { t } = i18n.global;
const sortButton = ({ column, label, descFirst = false }) => (
);
const compareNumbers = (a, b) => (a ?? 0) - (b ?? 0);
const sortAlphabetically = (a, b) => {
if (!a || !b) {
if (!a && !b) return 0;
return !a ? -1 : 1;
}
return String(a).toLowerCase().localeCompare(String(b).toLowerCase());
};
const sortLanguages = (a, b) => {
const as = Array.isArray(a) ? a.map((i) => i.value).sort() : [];
const bs = Array.isArray(b) ? b.map((i) => i.value).sort() : [];
return JSON.stringify(as).localeCompare(JSON.stringify(bs));
};
const withFriendNumber = (rowA, rowB, result) => {
if (result !== 0) {
return result;
}
return compareNumbers(
rowA.original?.$friendNumber,
rowB.original?.$friendNumber
);
};
const sortByNumber = (selector) => (rowA, rowB) =>
withFriendNumber(
rowA,
rowB,
compareNumbers(selector(rowA.original), selector(rowB.original))
);
const sortByString = (selector) => (rowA, rowB) =>
withFriendNumber(
rowA,
rowB,
sortAlphabetically(selector(rowA.original), selector(rowB.original))
);
const sortByStatus = (rowA, rowB) => {
const a = rowA.original?.status ?? 'offline';
const b = rowB.original?.status ?? 'offline';
return withFriendNumber(rowA, rowB, sortStatus(a, b));
};
const sortByLanguages = (rowA, rowB) =>
withFriendNumber(
rowA,
rowB,
sortLanguages(rowA.original?.$languages, rowB.original?.$languages)
);
export const createColumns = ({
randomUserColours,
selectedFriends,
onToggleFriendSelection,
onConfirmDeleteFriend
}) => {
const cols = [];
cols.push(
{
id: 'leftSpacer',
header: () => null,
size: 20,
enableSorting: false,
enableResizing: false,
meta: {
thClass: 'p-0',
tdClass: 'p-0'
},
cell: () => null
},
{
id: 'bulkSelect',
header: () => null,
size: 55,
enableSorting: false,
meta: {
thClass: 'p-0',
tdClass: 'p-0'
},
cell: ({ row }) => {
const id = row.original?.id;
const checked = selectedFriends?.value?.has?.(id);
return (
event.stopPropagation()}
>
onToggleFriendSelection?.(id)
}
/>
);
}
},
{
id: 'friendNumber',
accessorFn: (row) => row?.$friendNumber,
header: ({ column }) =>
sortButton({
column,
label: t('table.friendList.no'),
descFirst: true
}),
size: 100,
sortingFn: sortByNumber((row) => row?.$friendNumber ?? 0),
cell: ({ row }) => {row.original?.$friendNumber || ''}
},
{
id: 'avatar',
accessorFn: (row) => row?.photo,
header: () => t('table.friendList.avatar'),
size: 90,
enableSorting: false,
cell: ({ row }) => {
const src = userImage(row.original, true);
return src ? (
) : null;
}
},
{
id: 'displayName',
accessorFn: (row) => row?.displayName,
header: ({ column }) =>
sortButton({
column,
label: t('table.friendList.displayName')
}),
size: 200,
sortingFn: sortByString((row) => row?.displayName ?? ''),
cell: ({ row }) => {
const style = randomUserColours?.value
? { color: row.original?.$userColour }
: null;
return (
{row.original?.displayName ?? ''}
);
}
},
{
id: 'rank',
accessorFn: (row) => row?.$trustSortNum,
header: ({ column }) =>
sortButton({
column,
label: t('table.friendList.rank')
}),
size: 140,
sortingFn: sortByNumber((row) => row?.$trustSortNum ?? 0),
cell: ({ row }) => {
if (randomUserColours?.value) {
return (
{row.original?.$trustLevel ?? ''}
);
}
return (
{row.original?.$trustLevel ?? ''}
);
}
},
{
id: 'status',
accessorFn: (row) => row?.status,
header: ({ column }) =>
sortButton({ column, label: t('table.friendList.status') }),
minSize: 200,
sortingFn: sortByStatus,
meta: {
stretch: true
},
cell: ({ row }) => {
const status = row.original?.status;
return (
{status && status !== 'offline' ? (
) : null}
{row.original?.statusDescription ?? ''}
);
}
},
{
id: 'language',
accessorFn: (row) => row?.$languages,
header: ({ column }) =>
sortButton({ column, label: t('table.friendList.language') }),
size: 130,
sortingFn: sortByLanguages,
cell: ({ row }) => (
{(row.original?.$languages ?? []).map((item) => (
))}
)
},
{
id: 'bioLink',
header: () => t('table.friendList.bioLink'),
size: 130,
enableSorting: false,
cell: ({ row }) => (
{(row.original?.bioLinks ?? [])
.filter(Boolean)
.map((link, index) => (
{
event.stopPropagation();
openExternalLink(link);
}}
loading="lazy"
/>
))}
)
},
{
id: 'joinCount',
accessorFn: (row) => row?.$joinCount,
header: ({ column }) =>
sortButton({
column,
label: t('table.friendList.joinCount')
}),
size: 120,
sortingFn: sortByNumber((row) => row?.$joinCount ?? 0),
meta: {
class: 'text-right'
}
},
{
id: 'timeTogether',
accessorFn: (row) => row?.$timeSpent,
header: ({ column }) =>
sortButton({
column,
label: t('table.friendList.timeTogether')
}),
size: 140,
sortingFn: sortByNumber((row) => row?.$timeSpent ?? 0),
meta: {
class: 'text-right'
},
cell: ({ row }) => {
const time = row.original?.$timeSpent;
return time ? {timeToText(time)} : null;
}
},
{
id: 'lastSeen',
accessorFn: (row) => row?.$lastSeen,
header: ({ column }) =>
sortButton({
column,
label: t('table.friendList.lastSeen')
}),
size: 170,
sortingFn: sortByString((row) => row?.$lastSeen ?? ''),
cell: ({ row }) => {
const text = formatDateFilter(row.original?.$lastSeen, 'long');
return {text === '-' ? '' : text};
}
},
{
id: 'mutualFriends',
accessorFn: (row) => row?.$mutualCount,
header: ({ column }) =>
sortButton({
column,
label: t('table.friendList.mutualFriends')
}),
size: 120,
sortingFn: sortByNumber((row) => row?.$mutualCount ?? 0),
meta: {
class: 'text-right'
},
cell: ({ row }) => {
const count = row.original?.$mutualCount;
return count ? {count} : null;
}
},
{
id: 'lastActivity',
accessorFn: (row) => row?.last_activity,
header: ({ column }) =>
sortButton({
column,
label: t('table.friendList.lastActivity')
}),
size: 200,
sortingFn: sortByString((row) => row?.last_activity ?? ''),
cell: ({ row }) => (
{formatDateFilter(row.original?.last_activity, 'long')}
)
},
{
id: 'lastLogin',
accessorFn: (row) => row?.last_login,
header: ({ column }) =>
sortButton({ column, label: t('table.friendList.lastLogin') }),
size: 200,
sortingFn: sortByString((row) => row?.last_login ?? ''),
cell: ({ row }) => (
{formatDateFilter(row.original?.last_login, 'long')}
)
},
{
id: 'dateJoined',
accessorFn: (row) => row?.date_joined,
header: ({ column }) =>
sortButton({
column,
label: t('table.friendList.dateJoined')
}),
size: 120,
sortingFn: sortByString((row) => row?.date_joined ?? ''),
cell: ({ row }) => {row.original?.date_joined ?? ''}
},
{
id: 'unfriend',
header: () => t('table.friendList.unfriend'),
size: 100,
enableSorting: false,
meta: {
class: 'text-center'
},
cell: ({ row }) => (
// TODO(icon): verify unfollow icon replacement
{
event.stopPropagation();
onConfirmDeleteFriend?.(row.original?.id);
}}
/>
)
}
);
return cols;
};