updating foss with saas changes

This commit is contained in:
nick-delirium 2025-06-04 17:30:48 +02:00
parent 0f48518b51
commit 084da23442
No known key found for this signature in database
GPG key ID: 93ABD695DF5FDBA0
14 changed files with 92 additions and 181 deletions

View file

@ -22,6 +22,7 @@ function Player() {
<div className={cn('relative flex-1', 'overflow-visible')}>
<Overlay isClickmap />
<div
data-openreplay-obscured
className={cn(stl.screenWrapper, stl.checkers, '!overflow-y-scroll')}
style={{ maxHeight: 800 }}
ref={screenWrapper}

View file

@ -11,6 +11,7 @@ import styles from 'Components/Session_/playerBlock.module.css';
import ClipPlayerOverlay from 'Components/Session/Player/ClipPlayer/ClipPlayerOverlay';
import { observer } from 'mobx-react-lite';
import { Icon } from 'UI';
import { Empty } from 'antd';
interface Props {
session: Session;
@ -25,8 +26,8 @@ interface Props {
function ClipPlayerContent(props: Props) {
const playerContext = React.useContext<IPlayerContext>(PlayerContext);
const screenWrapper = React.useRef<HTMLDivElement>(null);
const { time } = playerContext.store.get();
const { range, isFull } = props;
const { time, error } = playerContext.store.get();
const { range, isFull, isHighlight } = props;
React.useEffect(() => {
if (!playerContext.player) return;
@ -57,7 +58,16 @@ function ClipPlayerContent(props: Props) {
const outerHeight = props.isHighlight ? 556 + 39 : 556;
const innerHeight = props.isHighlight ? 504 + 39 : 504;
return (
return error ? (
<div
className="inset-0 flex items-center justify-center absolute"
style={{ height: 'auto' }}
>
<div className="flex flex-col items-center">
<Empty description="Session not found." />
</div>
</div>
) : (
<div
className={cn(
styles.playerBlock,
@ -73,7 +83,10 @@ function ClipPlayerContent(props: Props) {
>
<div className={cn(stl.playerBody, 'flex flex-1 flex-col relative')}>
<div className="relative flex-1 overflow-hidden group">
<ClipPlayerOverlay autoplay={props.autoplay} />
<ClipPlayerOverlay
isHighlight={isHighlight}
autoplay={props.autoplay}
/>
<div
className={cn(stl.screenWrapper, stl.checkers)}
ref={screenWrapper}
@ -91,7 +104,11 @@ function ClipPlayerContent(props: Props) {
<div className="leading-none font-medium">{props.message}</div>
</div>
) : null}
<ClipPlayerControls isFull={isFull} session={props.session} range={props.range} />
<ClipPlayerControls
isFull={isFull}
session={props.session}
range={props.range}
/>
</div>
</div>
);

View file

@ -35,7 +35,7 @@ function ClipPlayerHeader(props: Props) {
return (
<div className="bg-white p-3 flex justify-between items-center border-b relative">
{isHighlight && !isFull ? <PartialSessionBadge /> : null}
<UserCard session={props.session} />
{props.session ? <UserCard session={props.session} /> : null}
<Space>
<Tooltip title={t('Copy link to clipboard')} placement="bottom">

View file

@ -8,9 +8,10 @@ import AutoplayTimer from 'Components/Session/Player/ClipPlayer/AutoPlayTimer';
interface Props {
autoplay: boolean;
isHighlight?: boolean;
}
function Overlay({ autoplay }: Props) {
function Overlay({ autoplay, isHighlight }: Props) {
const { player, store } = React.useContext(PlayerContext);
const togglePlay = () => player.togglePlay();
@ -19,9 +20,11 @@ function Overlay({ autoplay }: Props) {
return (
<>
{messagesLoading ? <Loader /> : null}
{/* <div className="hidden group-hover:block"> */}
{/* <ClipFeedback/> */}
{/* </div> */}
{isHighlight ? null : (
<div className="hidden group-hover:block">
<ClipFeedback />
</div>
)}
<PlayIconLayer playing={playing} togglePlay={togglePlay} />
{completed && autoplay && <AutoplayTimer />}
</>

View file

@ -12,7 +12,7 @@ interface Props {}
function QueueControls(props: Props) {
const { t } = useTranslation();
const { clipStore, projectsStore, sessionStore, searchStore } = useStore();
const { clipStore } = useStore();
const previousId = clipStore.prevId;
const { nextId } = clipStore;

View file

@ -132,17 +132,7 @@ function Timeline({ range }: any) {
// };
return (
<div
className="flex items-center w-full"
style={
{
// top: '-4px',
// zIndex: 100,
// maxWidth: 'calc(100% - 5rem)',
// left: '3.5rem',
}
}
>
<div className="flex items-center w-full">
<div
className={stl.progress}
onClick={ready ? jumpToTime : undefined}

View file

@ -143,6 +143,7 @@ function Player(props: IProps) {
isInspMode ? stl.solidBg : stl.checkers,
)}
ref={screenWrapper}
data-openreplay-obscured
/>
</div>
{!fullscreen && !!bottomBlock && (

View file

@ -7,15 +7,10 @@ import { debounce } from 'App/utils';
import { IResourceRequest, IResourceTiming } from 'App/player';
import { WsChannel } from 'App/player/web/messages';
import { PlayerContext } from 'App/components/Session/playerContext';
import MDRenderer from 'Shared/MDRenderer/MDRenderer';
let debounceUpdate: any = () => {};
const boldLine = /\*\*(.*?)\*\*/i;
function isTitleLine(line: string): boolean {
return boldLine.test(line);
}
function SummaryBlock({ sessionId }: { sessionId: string }) {
const { store } = React.useContext(PlayerContext);
const { tabStates } = store.get();
@ -24,7 +19,7 @@ function SummaryBlock({ sessionId }: { sessionId: string }) {
const zoomEnabled = uiPlayerStore.timelineZoom.enabled;
const zoomStartTs = uiPlayerStore.timelineZoom.startTs;
const zoomEndTs = uiPlayerStore.timelineZoom.endTs;
const { zoomTab } = uiPlayerStore;
const zoomTab = uiPlayerStore.zoomTab;
React.useEffect(() => {
debounceUpdate = debounce(
@ -76,31 +71,13 @@ function SummaryBlock({ sessionId }: { sessionId: string }) {
}
}, [zoomTab]);
const formattedText = aiSummaryStore.text.split('\n').map((line) => {
if (isTitleLine(line)) {
return (
<div className="font-semibold mt-2">{line.replace(/\*/g, '')}</div>
);
}
if (line.startsWith('*')) {
return (
<li className="ml-1 marker:mr-1 flex items-center gap-1">
<CodeStringFormatter text={line.replace(/\*/g, '')} />
</li>
);
}
return (
<div className="flex items-center gap-1">
<CodeStringFormatter text={line} />
</div>
);
});
return (
<div style={summaryBlockStyle}>
{aiSummaryStore.text ? (
<div className="rounded p-4 bg-white whitespace-pre-wrap flex flex-col">
{formattedText.map((v) => v)}
<div
className={'rounded p-4 bg-white whitespace-pre-wrap flex flex-col'}
>
<MDRenderer content={aiSummaryStore.text} />
</div>
) : (
<TextPlaceholder />
@ -111,44 +88,31 @@ function SummaryBlock({ sessionId }: { sessionId: string }) {
function TextPlaceholder() {
return (
<div className="animate-pulse rounded p-4 bg-white whitespace-pre-wrap flex flex-col gap-2">
<div className="h-2 bg-gray-medium rounded" />
<div className="h-2 bg-gray-medium rounded" />
<div className="grid grid-cols-3 gap-2">
<div className="h-2 bg-gray-medium rounded col-span-2" />
<div className="h-2 bg-gray-medium rounded col-span-1" />
<div
className={
'animate-pulse rounded p-4 bg-white whitespace-pre-wrap flex flex-col gap-2'
}
>
<div className={'h-2 bg-gray-medium rounded'} />
<div className={'h-2 bg-gray-medium rounded'} />
<div className={'grid grid-cols-3 gap-2'}>
<div className={'h-2 bg-gray-medium rounded col-span-2'} />
<div className={'h-2 bg-gray-medium rounded col-span-1'} />
</div>
<div className="grid grid-cols-4 gap-2 mt-3">
<div className="h-2 bg-gray-medium rounded col-span-1" />
<div className="h-2 bg-gray-medium rounded col-span-1" />
<div className="h-2 bg-gray-medium rounded col-span-2" />
<div className={'grid grid-cols-4 gap-2 mt-3'}>
<div className={'h-2 bg-gray-medium rounded col-span-1'} />
<div className={'h-2 bg-gray-medium rounded col-span-1'} />
<div className={'h-2 bg-gray-medium rounded col-span-2'} />
</div>
<div className="grid grid-cols-4 gap-2">
<div className="h-2 bg-gray-medium rounded col-span-2" />
<div className="h-2 bg-transparent rounded col-span-2" />
<div className={'grid grid-cols-4 gap-2'}>
<div className={'h-2 bg-gray-medium rounded col-span-2'} />
<div className={'h-2 bg-transparent rounded col-span-2'} />
</div>
</div>
);
}
function CodeStringFormatter({ text }: { text: string }) {
const parts = text.split(/(`[^`]*`)/).map((part, index) =>
part.startsWith('`') && part.endsWith('`') ? (
<div
key={index}
className="whitespace-nowrap bg-gray-lightest font-mono mx-1 px-1 border"
>
{part.substring(1, part.length - 1)}
</div>
) : (
<span key={index}>{part}</span>
),
);
return <>{parts}</>;
}
const summaryBlockStyle: React.CSSProperties = {
background:
'linear-gradient(180deg, #E8EBFF -24.14%, rgba(236, 254, 255, 0.00) 100%)',

View file

@ -0,0 +1,4 @@
export default function MDRenderer(props: any) {
console.warn('saas comp')
return null;
}

View file

@ -17,7 +17,7 @@ const { Text } = Typography;
function ProjectDropdown(props: { location: any }) {
const mstore = useStore();
const { t } = useTranslation();
const { projectsStore, searchStore, searchStoreLive, userStore } = mstore;
const { projectsStore, searchStore, searchStoreLive, userStore, aiFiltersStore } = mstore;
const { account } = userStore;
const sites = projectsStore.list;
const { siteId } = projectsStore;
@ -32,6 +32,7 @@ function ProjectDropdown(props: { location: any }) {
const handleSiteChange = async (newSiteId: string) => {
mstore.initClient();
aiFiltersStore.clearFilters();
setSiteId(newSiteId);
searchStore.clearSearch();
searchStore.clearList();

View file

@ -1,100 +1,5 @@
import { CloseOutlined, EnterOutlined } from '@ant-design/icons';
import { observer } from 'mobx-react-lite';
import React, { useState } from 'react';
import { useStore } from 'App/mstore';
import { Input } from 'UI';
const AiSearchField = observer(() => {
const { searchStore } = useStore();
const appliedFilter = searchStore.instance;
const hasFilters =
appliedFilter && appliedFilter.filters && appliedFilter.filters.length > 0;
const { aiFiltersStore } = useStore();
const [searchQuery, setSearchQuery] = useState('');
const onSearchChange = ({ target: { value } }: any) => {
setSearchQuery(value);
};
const fetchResults = () => {
if (searchQuery) {
void aiFiltersStore.getSearchFilters(searchQuery);
}
};
const handleKeyDown = (event: any) => {
if (event.key === 'Enter') {
fetchResults();
}
};
const clearAll = () => {
searchStore.clearSearch();
setSearchQuery('');
};
React.useEffect(() => {
if (aiFiltersStore.filtersSetKey !== 0) {
searchStore.edit(aiFiltersStore.filters);
}
}, [aiFiltersStore.filters, aiFiltersStore.filtersSetKey]);
return (
<div className="w-full">
<Input
onChange={onSearchChange}
placeholder='E.g., "Sessions with login issues this week"'
id="search"
onKeyDown={handleKeyDown}
value={searchQuery}
style={{ minWidth: 360, height: 30 }}
autoComplete="off"
className="px-4 py-1 text-lg placeholder-lg !border-0 nofocus"
leadingButton={
searchQuery !== '' ? (
<div
className="h-full flex items-center cursor-pointer"
onClick={hasFilters ? clearAll : fetchResults}
>
<div className="px-2 py-1 hover:bg-active-blue rounded mr-2">
{hasFilters ? <CloseOutlined /> : <EnterOutlined />}
</div>
</div>
) : null
}
/>
</div>
);
});
function AiSessionSearchField() {
const { aiFiltersStore } = useStore();
return (
<div className="bg-white rounded-full shadow-sm w-full">
<div
className={aiFiltersStore.isLoading ? 'animate-bg-spin' : ''}
style={gradientBox}
>
<AiSearchField />
</div>
</div>
);
return null;
}
export const gradientBox = {
border: 'double 1.5px transparent',
borderRadius: '100px',
background:
'linear-gradient(#ffffff, #ffffff), linear-gradient(-45deg, #394eff, #3eaaaf, #3ccf65)',
backgroundOrigin: 'border-box',
backgroundSize: '200% 200%',
backgroundClip: 'content-box, border-box',
display: 'flex',
gap: '0.25rem',
alignItems: 'center',
width: '100%',
overflow: 'hidden',
};
export default observer(AiSessionSearchField);
export default AiSessionSearchField;

View file

@ -7,6 +7,7 @@ import { useStore } from 'App/mstore';
import SessionCopyLink from './SessionCopyLink';
import IntegrateSlackButton from '../IntegrateSlackButton/IntegrateSlackButton';
import { useTranslation } from 'react-i18next';
import { signalService } from 'App/services';
interface Channel {
webhookId: string;
@ -126,7 +127,19 @@ const ShareModalComp: React.FC<Props> = ({ showCopyLink, hideModal, time }) => {
);
const sendMsg = async () => {
shareTo === 'slack' ? await shareToSlack() : await shareToMSTeams();
if (shareTo === 'slack') {
await shareToSlack();
} else {
await shareToMSTeams();
}
signalService.send(
{
source: 'share',
value: shareTo,
},
sessionId,
);
};
const hasBoth = slackOptions.length > 0 && msTeamsOptions.length > 0;

View file

@ -49,6 +49,11 @@ export default class AiFiltersStore {
console.log(r);
};
clearFilters = (): void => {
this.filters = { filters: [] };
this.filtersSetKey = 0;
};
setLoading = (loading: boolean): void => {
this.isLoading = loading;
};

View file

@ -1,2 +1,9 @@
export default class ClipStore {}
export default class ClipStore {
prevId = undefined;
nextId = undefined;
currentId = undefined;
next() {}
prev() {}
}
// SAAS only