* feat spot: init commit for extension * nvmrc * fix login flow * Spots Gridview Updates (#2422) * feat ui: login flow for spot extension * spot list, store and service created * some fixing for header * start work on single spot * spot player start * header for player, comments, icons, etc * split stuff into compoennts, create player state manager * player controls, activity panel etc etc * comments, empty page, rename and stuff * interval buttons etc * access modal * pubkey support * fix tooltip * limit 10 -> 9 * hls lib instead of videojs * some warnings * fix date display for exp * change public links * display more client data * fix cleaning, init comment * map network to replay player network ev * stream support, console panel, close panels on X * fixing streaming, destroy on leave * fix autoplay * show notification on spot login * fix spot login * backup player added, fix audio issue * show thumbnail when no video, add spot roles * add poster thumbnail * some fixes to video check * fix events jump * fix play btn * try catch over pubkey * feat ui: login flow for spot extension * spot list, store and service created * some fixing for header * start work on single spot * spot player start * header for player, comments, icons, etc * split stuff into compoennts, create player state manager * player controls, activity panel etc etc * comments, empty page, rename and stuff * interval buttons etc * access modal * pubkey support * fix tooltip * limit 10 -> 9 * hls lib instead of videojs * some warnings * fix date display for exp * change public links * display more client data * fix cleaning, init comment * map network to replay player network ev * stream support, console panel, close panels on X * fixing streaming, destroy on leave * fix autoplay * show notification on spot login * fix spot login * backup player added, fix audio issue * show thumbnail when no video, add spot roles * add poster thumbnail * some fixes to video check * fix events jump * fix play btn * try catch over pubkey * icons * Various updates * Update SVG.tsx * Update SideMenu.tsx * SpotList & Menu updates * feat ui: login flow for spot extension * spot list, store and service created * some fixing for header * start work on single spot * spot player start * header for player, comments, icons, etc * split stuff into compoennts, create player state manager * player controls, activity panel etc etc * comments, empty page, rename and stuff * interval buttons etc * access modal * pubkey support * fix tooltip * limit 10 -> 9 * hls lib instead of videojs * some warnings * fix date display for exp * change public links * display more client data * fix cleaning, init comment * map network to replay player network ev * stream support, console panel, close panels on X * fixing streaming, destroy on leave * fix autoplay * show notification on spot login * fix spot login * backup player added, fix audio issue * show thumbnail when no video, add spot roles * add poster thumbnail * some fixes to video check * fix events jump * fix play btn * try catch over pubkey * icons * spot login pinging * Spot List & Player Updates * move spot login flow to login comp, use separate spot login path for unique jwt * invalidate spot jwt on logout * add visual data on page load event * typo fix * Spot Listing improvements post review. * Update SpotListItem.tsx * Improved Spot List and Item Details * Minor improvements * More improvements * Public player header improvements * Moved formatExpirationTime to utils * fixes after merge --------- Co-authored-by: nick-delirium <nikita@openreplay.com> * set sso link to <a>? * some small perf fixes * login duck reformat... * Update frontend.yaml * add observer to spot list header * split list header * update spotjwt param in router * fix toast in router * fix async fetch, move ctx * capture space btn ev * fix header link * public sharing error msg * fix err msg for unsuccessful rec start * fix list alignment * Caching assets. Finally!!! * fix typing in comment field * add pubkey to comments, fix console jump btn * no content comp * change refresh token logic * move thumbnail ts * move thumbnail ts * fix tab change * switch up toggler * early exit if no jwt present * regenerate icons * fix location str * fix ctx * change thumnail res, return autoplay for video player * parse links in console rows, fix injected method parse? * remove ts from js * fix console parsing order? * fixes for autoplay * xray for spot player * move to spot list after login; esc to cancel; fix signup link; move ux commit * kb sc for skipping; xray for spot ext * track aborted requests * tooltip for readability * fixing empty state * New blank state + various minor improvements (#2471) * New blank state + various minor improvements * apres merge --------- Co-authored-by: nick-delirium <nikita@openreplay.com> * rm temp v * init or card * empty state debug * empty state debug * empty state debug * fix initor img * spotonly scope support * Improved Spot dead-end pages (#2475) * Improved Spot dead-end pages * Initiate OpenReplay Setup and some more * get scope changes * fix crash * scope upgrade/downgrade * scope setup flow * ping for backend * upgrade wxt deps * cancel ping int on expiration * check rec status * fix ping * check video processing state * check video processing state * fix xray close, network highlight, fcp rounding * update wxt, move open spot stuff to settings * fix some history issues * fix spot login flow * fix spot login again * fix spot login again * don't send two requests * limit messages for logged users * limit messages for logged users * fix public ignore * microphone stuff * microphone stuff * Various improvements (#2509) * Various improvements - Updated icons in mic settings - Included prefix in Spot title - Save recording notification has been updated - Other minor UI improvements * Inline declaration of spot name field, and settings UI * str f --------- Co-authored-by: nick-delirium <nikita@openreplay.com> * UI changes in player header, spot list (#2510) * Added UI elements in player page - Badge with counts for comments - Download and Delete dropdown in player - Spot selection -- UI improvement * Minor copy updates * completing changes --------- Co-authored-by: nick-delirium <nikita@openreplay.com> * rm cmt * fix cellmeasurer * thumbnail dur * fix download * Minor fixes (#2512) - Spot delete confirmation - Spot comments UI update - Minor copy updates * limit number of notif messages * add spot title to doc title, add cache groups for webpack * drop mic controls from recording popup view * fix for webpack compress * fix for auto mic pickup * change status banners * move svgs around, remove undefined check * refactor svgs * fix timetable scaling * fix error popup * self contain css * pre-select spot on spot onboarding --------- Co-authored-by: Sudheer Salavadi <connect.uxmaster@gmail.com> Co-authored-by: Rajesh Rajendran <rjshrjndrn@users.noreply.github.com>
256 lines
7.5 KiB
TypeScript
256 lines
7.5 KiB
TypeScript
import {
|
|
ClockCircleOutlined,
|
|
DeleteOutlined,
|
|
DownloadOutlined,
|
|
EditOutlined,
|
|
MoreOutlined,
|
|
PlayCircleOutlined,
|
|
SlackOutlined,
|
|
UserOutlined,
|
|
} from '@ant-design/icons';
|
|
import { Button, Checkbox, Dropdown, Tooltip } from 'antd';
|
|
import copy from 'copy-to-clipboard';
|
|
import { Link2 } from 'lucide-react';
|
|
import React, { useState } from 'react';
|
|
import { useHistory, useParams } from 'react-router-dom';
|
|
import { toast } from 'react-toastify';
|
|
|
|
import { Spot } from 'App/mstore/types/spot';
|
|
import { spot as spotUrl, withSiteId } from 'App/routes';
|
|
|
|
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
|
|
|
|
import EditItemModal from './EditItemModal';
|
|
|
|
const backgroundUrl = '/assets/img/spotThumbBg.svg';
|
|
|
|
interface ISpotListItem {
|
|
spot: Spot;
|
|
onRename: (id: string, title: string) => void;
|
|
onDelete: () => void;
|
|
onVideo: (id: string) => Promise<{ url: string }>;
|
|
onSelect: (selected: boolean) => void;
|
|
isSelected: boolean;
|
|
}
|
|
|
|
function SpotListItem({
|
|
spot,
|
|
onRename,
|
|
onDelete,
|
|
onVideo,
|
|
onSelect,
|
|
isSelected,
|
|
}: ISpotListItem) {
|
|
const [isEdit, setIsEdit] = useState(false);
|
|
const [loading, setLoading] = useState(true);
|
|
const [tooltipText, setTooltipText] = useState('Copy link to clipboard');
|
|
const history = useHistory();
|
|
const { siteId } = useParams<{ siteId: string }>();
|
|
|
|
const menuItems = [
|
|
{
|
|
key: 'rename',
|
|
icon: <EditOutlined />,
|
|
label: 'Rename',
|
|
},
|
|
{
|
|
key: 'download',
|
|
label: 'Download Video',
|
|
icon: <DownloadOutlined />,
|
|
},
|
|
{
|
|
key: 'delete',
|
|
icon: <DeleteOutlined />,
|
|
label: 'Delete',
|
|
},
|
|
];
|
|
|
|
React.useEffect(() => {
|
|
menuItems.splice(1, 0, {
|
|
key: 'slack',
|
|
icon: <SlackOutlined />,
|
|
label: 'Share via Slack',
|
|
});
|
|
}, []);
|
|
const onMenuClick = async ({ key }: any) => {
|
|
switch (key) {
|
|
case 'rename':
|
|
return setIsEdit(true);
|
|
case 'download':
|
|
const { url } = await onVideo(spot.spotId);
|
|
await downloadFile(url, `${spot.title}.webm`);
|
|
return;
|
|
case 'copy':
|
|
copy(
|
|
`${window.location.origin}${withSiteId(
|
|
spotUrl(spot.spotId.toString()),
|
|
siteId
|
|
)}`
|
|
);
|
|
return toast.success('Spot URL copied to clipboard');
|
|
case 'delete':
|
|
return onDelete();
|
|
case 'slack':
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
};
|
|
|
|
const onSpotClick = (e: any) => {
|
|
if (e.shiftKey || e.ctrlKey || e.metaKey) {
|
|
const spotLink = withSiteId(spotUrl(spot.spotId.toString()), siteId);
|
|
const fullLink = `${window.location.origin}${spotLink}`;
|
|
window.open(fullLink, '_blank');
|
|
} else {
|
|
history.push(withSiteId(spotUrl(spot.spotId.toString()), siteId));
|
|
}
|
|
};
|
|
|
|
const copyToClipboard = () => {
|
|
const spotLink = withSiteId(spotUrl(spot.spotId.toString()), siteId);
|
|
const fullLink = `${window.location.origin}${spotLink}`;
|
|
navigator.clipboard
|
|
.writeText(fullLink)
|
|
.then(() => {
|
|
setTooltipText('Link copied to clipboard!');
|
|
setTimeout(() => setTooltipText('Copy link to clipboard'), 2000); // Reset tooltip text after 2 seconds
|
|
})
|
|
.catch(() => {
|
|
setTooltipText('Failed to copy URL');
|
|
setTimeout(() => setTooltipText('Copy link to clipboard'), 2000); // Reset tooltip text after 2 seconds
|
|
});
|
|
};
|
|
|
|
const onSave = (newName: string) => {
|
|
onRename(spot.spotId, newName);
|
|
setIsEdit(false);
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className={`bg-white rounded-lg overflow-hidden shadow-sm border ${
|
|
isSelected ? 'border-teal/30' : 'border-transparent'
|
|
} transition flex flex-col items-start hover:border-teal`}
|
|
>
|
|
{isEdit ? (
|
|
<EditItemModal
|
|
onSave={onSave}
|
|
onClose={() => setIsEdit(false)}
|
|
itemName={spot.title}
|
|
/>
|
|
) : null}
|
|
<div
|
|
className="relative group overflow-hidden"
|
|
style={{
|
|
width: '100%',
|
|
height: 180,
|
|
backgroundImage: `url(${backgroundUrl})`,
|
|
backgroundSize: 'cover',
|
|
backgroundPosition: 'center',
|
|
}}
|
|
>
|
|
{loading && (
|
|
<div className="absolute inset-0 flex items-center justify-center">
|
|
<AnimatedSVG name={ICONS.LOADER} size={32} />
|
|
</div>
|
|
)}
|
|
<div
|
|
className="block w-full h-full cursor-pointer transition hover:bg-teal/70 relative"
|
|
onClick={onSpotClick}
|
|
>
|
|
<img
|
|
src={spot.thumbnail}
|
|
alt={spot.title}
|
|
className={'w-full h-full object-cover opacity-80'}
|
|
onLoad={() => setLoading(false)}
|
|
onError={() => setLoading(false)}
|
|
style={{ display: loading ? 'none' : 'block' }}
|
|
/>
|
|
<div className="absolute inset-0 flex items-center justify-center opacity-0 scale-75 transition-all hover:scale-100 hover:transition-all group-hover:opacity-100 transition-opacity ">
|
|
<PlayCircleOutlined
|
|
style={{ fontSize: '48px', color: 'white' }}
|
|
className="bg-teal/50 rounded-full"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="absolute left-0 bottom-8 flex relative gap-2 justify-end pe-2 pb-2 ">
|
|
<Tooltip title={tooltipText}>
|
|
<div
|
|
className={
|
|
'bg-black/70 text-white p-1 px-2 text-xs rounded-lg transition-transform transform translate-y-14 group-hover:translate-y-0 '
|
|
}
|
|
onClick={copyToClipboard}
|
|
style={{ cursor: 'pointer' }}
|
|
>
|
|
<Link2 size={16} strokeWidth={1} />
|
|
</div>
|
|
</Tooltip>
|
|
<div
|
|
className={
|
|
'bg-black/70 text-white p-1 px-2 text-xs rounded-lg flex items-center cursor-normal'
|
|
}
|
|
>
|
|
{spot.duration}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className={'px-4 py-4 w-full border-t'}>
|
|
<div className={'flex items-center gap-2'}>
|
|
<Checkbox
|
|
checked={isSelected}
|
|
onChange={({ target: { checked } }) => onSelect(checked)}
|
|
className={`flex cursor-pointer w-full hover:text-teal ${isSelected ? 'text-teal' : ''}`}
|
|
>
|
|
<span className="w-full text-nowrap text-ellipsis overflow-hidden max-w-80 mb-0 block">
|
|
{spot.title}
|
|
</span>
|
|
</Checkbox>
|
|
</div>
|
|
<div className={'flex items-center gap-1 leading-4 text-xs opacity-50'}>
|
|
<div>
|
|
<UserOutlined />
|
|
</div>
|
|
<div>{spot.user}</div>
|
|
<div className="ms-4">
|
|
<ClockCircleOutlined />
|
|
</div>
|
|
<div>{spot.createdAt}</div>
|
|
<div className={'ml-auto'}>
|
|
<Dropdown
|
|
menu={{ items: menuItems, onClick: onMenuClick }}
|
|
trigger={['click']}
|
|
>
|
|
<Button type="text" icon={<MoreOutlined />} size={'small'} />
|
|
</Dropdown>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
async function downloadFile(url: string, fileName: string) {
|
|
try {
|
|
const response = await fetch(url);
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Network response was not ok');
|
|
}
|
|
|
|
const blob = await response.blob();
|
|
const blobUrl = URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = blobUrl;
|
|
a.download = fileName;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
document.body.removeChild(a);
|
|
URL.revokeObjectURL(blobUrl);
|
|
} catch (error) {
|
|
console.error('Error downloading file:', error);
|
|
}
|
|
}
|
|
|
|
export default SpotListItem;
|