import Location from '../../components/Location.vue'; import { Avatar, AvatarFallback, AvatarImage } from '../../components/ui/avatar'; import { Badge } from '../../components/ui/badge'; import { Button } from '../../components/ui/button'; import { Tooltip, TooltipContent, TooltipTrigger, TooltipWrapper } from '../../components/ui/tooltip'; import { ArrowUpDown, Ban, BellOff, Check, ImageOff, Link, MessageCircle, Reply, Tag, Trash2, X } from 'lucide-vue-next'; import { storeToRefs } from 'pinia'; import { formatDateFilter } from '../../shared/utils'; import { checkCanInvite } from '../../shared/utils/invite'; import { i18n } from '../../plugins'; import { useGameStore, useInstanceStore, useLocationStore, useUiStore, useUserStore, useNotificationStore } from '../../stores'; import { showUserDialog } from '../../coordinators/userCoordinator'; import { showWorldDialog } from '../../coordinators/worldCoordinator'; import { showGroupDialog } from '../../coordinators/groupCoordinator'; import Emoji from '../../components/Emoji.vue'; const { t, te } = i18n.global; const isGroupId = (id) => typeof id === 'string' && id.startsWith('grp_'); export const createColumns = ({ getNotificationCreatedAt, getNotificationCreatedAtTs, openNotificationLink, showFullscreenImageDialog, getSmallThumbnailUrl, acceptFriendRequestNotification, showSendInviteResponseDialog, showSendInviteRequestResponseDialog, acceptRequestInvite, sendNotificationResponse, hideNotification, hideNotificationPrompt, deleteNotificationLog, deleteNotificationLogPrompt }) => { const { showSendBoopDialog } = useUserStore(); const { shiftHeld } = storeToRefs(useUiStore()); const { currentUser } = storeToRefs(useUserStore()); const { lastLocation } = storeToRefs(useLocationStore()); const { isGameRunning } = storeToRefs(useGameStore()); const { isNotificationExpired } = useNotificationStore(); const { cachedInstances } = storeToRefs(useInstanceStore()); const canInvite = () => { const location = lastLocation.value?.location; return ( Boolean(location) && isGameRunning.value && checkCanInvite(location, { currentUserId: currentUser.value?.id, lastLocationStr: lastLocation.value?.location, cachedInstances: cachedInstances.value }) ); }; const getResponseIcon = (response, notificationType) => { if (response?.type === 'link') { return Link; } switch (response?.icon) { case 'check': return Check; case 'cancel': return X; case 'ban': return Ban; case 'bell-slash': return BellOff; case 'reply': return notificationType === 'boop' ? MessageCircle : Reply; default: return Tag; } }; return [ { accessorFn: (row) => getNotificationCreatedAtTs(row), id: 'created_at', size: 120, meta: { label: () => t('table.notification.date') }, header: ({ column }) => ( ), sortingFn: (rowA, rowB, columnId) => { const a = rowA.getValue(columnId) ?? 0; const b = rowB.getValue(columnId) ?? 0; if (a !== b) { return a - b; } const aId = typeof rowA.original?.id === 'string' ? rowA.original.id : ''; const bId = typeof rowB.original?.id === 'string' ? rowB.original.id : ''; return aId.localeCompare(bId); }, cell: ({ row }) => { const createdAt = getNotificationCreatedAt(row.original); const shortText = formatDateFilter(createdAt, 'short'); const longText = formatDateFilter(createdAt, 'long'); return ( {shortText} {longText} ); } }, { accessorKey: 'type', size: 180, header: () => t('table.notification.type'), meta: { label: () => t('table.notification.type') }, cell: ({ row }) => { const original = row.original; const typeKey = `view.notification.filters.${original.type}`; const label = te(typeKey) ? t(typeKey) : original.type; if (original.type === 'invite') { return ( {label} ); } if ( original.type === 'group.queueReady' || original.type === 'instance.closed' ) { return ( showWorldDialog(original.location) } > {label} {original.location ? ( ) : null} ); } if (original.link) { return ( openNotificationLink(original.link) } > {label} {original.linkText} ); } return ( {label} ); } }, { accessorKey: 'senderUsername', meta: { class: 'overflow-hidden', label: () => t('table.notification.user') }, size: 150, header: () => t('table.notification.user'), cell: ({ row }) => { const original = row.original; if ( original.senderUserId && !isGroupId(original.senderUserId) ) { return ( showUserDialog(original.senderUserId) } > {original.senderUsername} ); } if (original.link?.startsWith('user:')) { return ( openNotificationLink(original.link) } > {original.linkText || original.senderUsername} ); } if ( original.senderUsername && !isGroupId(original.senderUserId) ) { return ( {original.senderUsername} ); } return null; } }, { accessorKey: 'groupName', meta: { class: 'overflow-hidden', label: () => t('table.notification.group') }, size: 150, header: () => t('table.notification.group'), cell: ({ row }) => { const original = row.original; const label = original.senderUsername || original.groupName || original.data?.groupName || original.details?.groupName || original.linkText; if ( original.senderUserId && (original.type === 'groupChange' || isGroupId(original.senderUserId)) ) { return ( showGroupDialog(original.senderUserId) } > {label} ); } if ( original.type === 'groupChange' && original.senderUsername ) { return ( {original.senderUsername} ); } if (original.link?.startsWith('group:')) { return ( openNotificationLink(original.link) } > {original.data?.groupName || label} ); } if (original.link?.startsWith('event:')) { return ( openNotificationLink(original.link) } > {original.data?.groupName || original.groupName || label} ); } if (original.data?.groupName) { return ( {original.data.groupName} ); } if (original.details?.groupName) { return ( {original.details.groupName} ); } if (original.groupName) { return ( {original.groupName} ); } return null; } }, { accessorKey: 'photo', size: 80, header: () => t('table.notification.photo'), meta: { label: () => t('table.notification.photo') }, cell: ({ row }) => { const original = row.original; if (original.type === 'boop') { const imageUrl = original.details?.imageUrl || original.imageUrl; if (!imageUrl || imageUrl.startsWith('default_')) { return null; } return ( showFullscreenImageDialog(imageUrl)} imageUrl={imageUrl} size={30} /> ); } if (original.details?.imageUrl) { const detailsUrl = getSmallThumbnailUrl( original.details.imageUrl ); return ( showFullscreenImageDialog( original.details.imageUrl ) } > ); } if (original.imageUrl) { const imgUrl = getSmallThumbnailUrl(original.imageUrl); return ( showFullscreenImageDialog(original.imageUrl) } > ); } return null; } }, { id: 'message', header: () => t('table.notification.message'), enableSorting: false, meta: { class: 'min-w-0 overflow-hidden', stretch: true, label: () => t('table.notification.message') }, minSize: 100, cell: ({ row }) => { const original = row.original; return (
{original.type === 'invite' && original.details ? (
) : null} {original.message && original.title ? ( {`${original.title}, ${original.message}`} ) : null} {!original.message && original.title ? ( {original.title} ) : null} {original.message && !original.title && original.message !== `This is a generated invite to ${original.details?.worldName}` ? ( {original.message} ) : null} {!original.message && original.details?.inviteMessage ? ( {original.details.inviteMessage} ) : null} {!original.message && original.details?.requestMessage ? ( {original.details.requestMessage} ) : null} {!original.message && original.details?.responseMessage ? ( {original.details.responseMessage} ) : null}
); } }, { id: 'action', meta: { class: 'text-right', label: () => t('table.notification.action') }, size: 120, minSize: 120, maxSize: 120, header: () => t('table.notification.action'), enableSorting: false, cell: ({ row }) => { const original = row.original; const hasResponses = Array.isArray(original.responses); const showDecline = original.type !== 'requestInviteResponse' && original.type !== 'inviteResponse' && original.type !== 'message' && original.type !== 'boop' && original.type !== 'groupChange' && !original.type?.includes('group.') && !original.type?.includes('moderation.') && !original.type?.includes('instance.') && !original.link?.startsWith('economy.'); const showDeleteLog = original.type !== 'friendRequest' && original.type !== 'ignoredFriendRequest'; return (
{original.senderUserId !== currentUser.value?.id && !isNotificationExpired(original) ? ( {original.type === 'friendRequest' ? ( {t( 'view.notification.actions.accept' )} ) : null} {original.type === 'invite' ? ( {t( 'view.notification.actions.decline_with_message' )} ) : null} {original.type === 'requestInvite' ? ( {canInvite() ? ( {t( 'view.notification.actions.invite' )} ) : null} {t( 'view.notification.actions.decline_with_message' )} ) : null} {hasResponses ? original.responses.map((response) => { const onClick = () => { if (response.type === 'link') { openNotificationLink( response.data ); return; } if ( response.icon === 'reply' && original.type === 'boop' ) { showSendBoopDialog( original.senderUserId ); return; } sendNotificationResponse( original.id, original.responses, response.type ); }; const ResponseIcon = getResponseIcon( response, original.type ); return ( {response.text} ); }) : null} {showDecline ? ( {t( 'view.notification.actions.decline' )} ) : null} {original.type === 'group.queueReady' ? ( {t( 'view.notification.actions.delete_log' )} ) : null} ) : null} {showDeleteLog ? ( {t( 'view.notification.actions.delete_log' )} ) : null}
); } }, { id: 'trailing', header: () => null, enableSorting: false, enableResizing: false, size: 5, cell: () => null } ]; };