import AvatarInfo from '../../components/AvatarInfo.vue'; import Location from '../../components/Location.vue'; import { Badge } from '../../components/ui/badge'; import { Button } from '../../components/ui/button'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../components/ui/tooltip'; import { ArrowDown, ArrowRight, ArrowUpDown, ChevronDown, ChevronRight } from 'lucide-vue-next'; import { formatDateFilter, statusClass, timeToText } from '../../shared/utils'; import { i18n } from '../../plugin'; import { useUserStore, useGalleryStore } from '../../stores'; const { t } = i18n.global; const expandedRow = ({ row }) => { const original = row.original; const type = original.type; if (type === 'GPS') { return (
{original.previousLocation ? ( <> {timeToText(original.time)}
) : null} {original.location ? ( ) : null}
); } if (type === 'Offline') { return original.location ? (
{timeToText(original.time)}
) : null; } if (type === 'Online') { return original.location ? (
) : null; } if (type === 'Avatar') { const { showFullscreenImageDialog } = useGalleryStore(); return (
{original.previousCurrentAvatarThumbnailImageUrl ? ( <> showFullscreenImageDialog( original.previousCurrentAvatarImageUrl ) } />
) : null}
{original.currentAvatarThumbnailImageUrl ? ( <> showFullscreenImageDialog( original.currentAvatarImageUrl ) } />
) : null}
); } if (type === 'Status') { return (
{original.previousStatusDescription}
{original.statusDescription}
); } if (type === 'Bio') { return (

            
); } return null; }; export const columns = [ { id: 'expander', header: () => null, enableSorting: false, size: 20, minSize: 0, maxSize: 20, meta: { expandedRow }, cell: ({ row }) => { if (!row.getCanExpand()) { return null; } return ( ); } }, { accessorKey: 'created_at', size: 140, header: ({ column }) => ( ), cell: ({ row }) => { const createdAt = row.getValue('created_at'); const shortText = formatDateFilter(createdAt, 'short'); const longText = formatDateFilter(createdAt, 'long'); return ( {shortText} {longText} ); } }, { accessorKey: 'type', size: 130, header: () => t('table.feed.type'), cell: ({ row }) => { const type = row.getValue('type'); return (
{t(`view.feed.filters.${type}`)}
); } }, { accessorKey: 'displayName', size: 190, header: () => t('table.feed.user'), cell: ({ row }) => { const { showUserDialog } = useUserStore(); const original = row.original; return ( showUserDialog(original.userId)} > {original.displayName} ); } }, { id: 'detail', header: () => t('table.feed.detail'), enableSorting: false, minSize: 100, meta: { stretch: true }, cell: ({ row }) => { const original = row.original; const type = original.type; if (type === 'GPS') { return original.location ? (
) : null; } if (type === 'Offline' || type === 'Online') { return original.location ? (
) : null; } if (type === 'Status') { if ( original.statusDescription === original.previousStatusDescription ) { return (
); } return (
{original.statusDescription}
); } if (type === 'Avatar') { return (
); } if (type === 'Bio') { return (
{original.bio}
); } return null; } } ]; /** * Function that format the differences between two strings with HTML tags * markerStartTag and markerEndTag are optional, if emitted, the differences will be highlighted with yellow and underlined. * @param {*} s1 * @param {*} s2 * @param {*} markerStartTag * @param {*} markerEndTag * @returns An array that contains both the string 1 and string 2, which the differences are formatted with HTML tags */ //function getWordDifferences function formatDifference( oldString, newString, markerAddition = '{{text}}', markerDeletion = '{{text}}' ) { [oldString, newString] = [oldString, newString].map((s) => s .replaceAll(/&/g, '&') .replaceAll(//g, '>') .replaceAll(/"/g, '"') .replaceAll(/'/g, ''') .replaceAll(/\n/g, '
') ); const oldWords = oldString .split(/\s+/) .flatMap((word) => word.split(/(
)/)); const newWords = newString .split(/\s+/) .flatMap((word) => word.split(/(
)/)); function findLongestMatch(oldStart, oldEnd, newStart, newEnd) { let bestOldStart = oldStart; let bestNewStart = newStart; let bestSize = 0; const lookup = new Map(); for (let i = oldStart; i < oldEnd; i++) { const word = oldWords[i]; if (!lookup.has(word)) lookup.set(word, []); lookup.get(word).push(i); } for (let j = newStart; j < newEnd; j++) { const word = newWords[j]; if (!lookup.has(word)) continue; for (const i of lookup.get(word)) { let size = 0; while ( i + size < oldEnd && j + size < newEnd && oldWords[i + size] === newWords[j + size] ) { size++; } if (size > bestSize) { bestOldStart = i; bestNewStart = j; bestSize = size; } } } return { oldStart: bestOldStart, newStart: bestNewStart, size: bestSize }; } function buildDiff(oldStart, oldEnd, newStart, newEnd) { const result = []; const match = findLongestMatch(oldStart, oldEnd, newStart, newEnd); if (match.size > 0) { // Handle differences before the match if (oldStart < match.oldStart || newStart < match.newStart) { result.push( ...buildDiff( oldStart, match.oldStart, newStart, match.newStart ) ); } // Add the matched words result.push( oldWords .slice(match.oldStart, match.oldStart + match.size) .join(' ') ); // Handle differences after the match if ( match.oldStart + match.size < oldEnd || match.newStart + match.size < newEnd ) { result.push( ...buildDiff( match.oldStart + match.size, oldEnd, match.newStart + match.size, newEnd ) ); } } else { function build(words, start, end, pattern) { let r = []; let ts = words .slice(start, end) .filter((w) => w.length > 0) .join(' ') .split('
'); for (let i = 0; i < ts.length; i++) { if (i > 0) r.push('
'); if (ts[i].length < 1) continue; r.push(pattern.replace('{{text}}', ts[i])); } return r; } // Add deletions if (oldStart < oldEnd) result.push( ...build(oldWords, oldStart, oldEnd, markerDeletion) ); // Add insertions if (newStart < newEnd) result.push( ...build(newWords, newStart, newEnd, markerAddition) ); } return result; } return buildDiff(0, oldWords.length, 0, newWords.length) .join(' ') .replace(/
[ ]+
/g, '

') .replace(/
/g, '
'); }