openreplay/frontend/app/components/Spots/SpotsList/SpotListItem.tsx
Delirium 1326bb2eae
feat spot: init commit for extension (#2452)
* 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>
2024-08-29 13:35:58 +02:00

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;