feat(ui) - session copy and live list load more button

This commit is contained in:
Shekar Siri 2022-02-11 02:04:36 +01:00
parent adb742c0d9
commit d08cef7570
8 changed files with 105 additions and 43 deletions

View file

@ -1,16 +1,17 @@
import React, { useEffect } from 'react';
import { fetchLiveList } from 'Duck/sessions';
import { connect } from 'react-redux';
import { NoContent, Loader } from 'UI';
import { NoContent, Loader, LoadMoreButton } from 'UI';
import { List, Map } from 'immutable';
import SessionItem from 'Shared/SessionItem';
import withPermissions from 'HOCs/withPermissions'
import { KEYS } from 'Types/filter/customFilter';
import { applyFilter, addAttribute } from 'Duck/filters';
import { FilterCategory, FilterKey } from 'App/types/filter/filterType';
import { addFilterByKeyAndValue } from 'Duck/liveSearch';
import { addFilterByKeyAndValue, updateCurrentPage } from 'Duck/liveSearch';
const AUTOREFRESH_INTERVAL = .5 * 60 * 1000
const PER_PAGE = 20;
interface Props {
loading: Boolean,
@ -20,14 +21,20 @@ interface Props {
filters: any,
addAttribute: (obj) => void,
addFilterByKeyAndValue: (key: FilterKey, value: string) => void,
updateCurrentPage: (page: number) => void,
currentPage: number,
}
function LiveSessionList(props: Props) {
const { loading, filters, list } = props;
const { loading, filters, list, currentPage } = props;
var timeoutId;
const hasUserFilter = filters.map(i => i.key).includes(KEYS.USERID);
const [sessions, setSessions] = React.useState(list);
const displayedCount = Math.min(currentPage * PER_PAGE, sessions.size);
const addPage = () => props.updateCurrentPage(props.currentPage + 1)
useEffect(() => {
if (filters.size === 0) {
props.addFilterByKeyAndValue(FilterKey.USERID, '');
@ -92,7 +99,7 @@ function LiveSessionList(props: Props) {
show={ !loading && sessions && sessions.size === 0}
>
<Loader loading={ loading }>
{sessions && sessions.map(session => (
{sessions && sessions.take(displayedCount).map(session => (
<SessionItem
key={ session.sessionId }
session={ session }
@ -101,6 +108,13 @@ function LiveSessionList(props: Props) {
onUserClick={onUserClick}
/>
))}
<LoadMoreButton
className="mt-3"
displayedCount={displayedCount}
totalCount={sessions.size}
onClick={addPage}
/>
</Loader>
</NoContent>
</div>
@ -112,6 +126,7 @@ export default withPermissions(['ASSIST_LIVE', 'SESSION_REPLAY'])(connect(
list: state.getIn(['sessions', 'liveSessions']),
loading: state.getIn([ 'sessions', 'loading' ]),
filters: state.getIn([ 'liveSearch', 'instance', 'filters' ]),
currentPage: state.getIn(["liveSearch", "currentPage"]),
}),
{ fetchLiveList, applyFilter, addAttribute, addFilterByKeyAndValue }
{ fetchLiveList, applyFilter, addAttribute, addFilterByKeyAndValue, updateCurrentPage }
)(LiveSessionList));

View file

@ -7,9 +7,11 @@ import {
connectPlayer,
init as initPlayer,
clean as cleanPlayer,
Controls,
} from 'Player';
import cn from 'classnames'
import RightBlock from './RightBlock'
import withLocationHandlers from "HOCs/withLocationHandlers";
import PlayerBlockHeader from '../Session_/PlayerBlockHeader';
@ -35,9 +37,17 @@ function PlayerContent({ live, fullscreen, showEvents }) {
)
}
function WebPlayer ({ session, toggleFullscreen, closeBottomBlock, live, fullscreen, jwt, config }) {
function WebPlayer (props) {
const { session, toggleFullscreen, closeBottomBlock, live, fullscreen, jwt, config } = props;
useEffect(() => {
initPlayer(session, jwt, config);
const jumptTime = props.query.get('jumpto');
if (jumptTime) {
Controls.jump(parseInt(jumptTime));
}
return () => cleanPlayer()
}, [ session.sessionId ]);
@ -56,7 +66,6 @@ function WebPlayer ({ session, toggleFullscreen, closeBottomBlock, live, fullscr
);
}
export default connect(state => ({
session: state.getIn([ 'sessions', 'current' ]),
jwt: state.get('jwt'),
@ -65,5 +74,4 @@ export default connect(state => ({
}), {
toggleFullscreen,
closeBottomBlock,
})(WebPlayer)
})(withLocationHandlers()(WebPlayer));

View file

@ -143,6 +143,7 @@ export default class PlayerBlockHeader extends React.PureComponent {
<SharePopup
entity="sessions"
id={ sessionId }
showCopyLink={true}
trigger={
<IconButton
className="mr-2"

View file

@ -1,13 +1,31 @@
import React from 'react'
import { IconButton } from 'UI'
import React from 'react';
import { IconButton } from 'UI';
import copy from 'copy-to-clipboard';
import { connectPlayer } from 'Player';
interface Props {
content: string;
time: any;
}
function SessionCopyLink({ content = '', time }: Props) {
const [copied, setCopied] = React.useState(false)
const copyHandler = () => {
setCopied(true);
copy(window.location.origin + window.location.pathname + '?jumpto=' + Math.round(time));
setTimeout(() => {
setCopied(false);
}, 1000);
};
function SessionCopyLink() {
return (
<div className="flex justify-between items-center w-full border-t -mx-4 px-4">
<IconButton label="Copy Link" icon="link-45deg" />
<div>Copied to Clipboard</div>
<div className="flex justify-between items-center w-full mt-2">
<IconButton label="Copy Link" primaryText icon="link-45deg" onClick={copyHandler} />
{ copied && <div className="color-teal">Copied to Clipboard</div> }
</div>
)
}
export default SessionCopyLink
export default connectPlayer(state => ({
time: state.time,
}))(SessionCopyLink);

View file

@ -47,7 +47,7 @@ export default class SharePopup extends React.PureComponent {
changeChannel = (e, { value }) => this.setState({ channelId: value })
render() {
const { trigger, loading, channels } = this.props;
const { trigger, loading, channels, showCopyLink = false } = this.props;
const { comment, isOpen, channelId } = this.state;
const options = channels.map(({ webhookId, name }) => ({ value: webhookId, text: name })).toJS();
@ -67,9 +67,11 @@ export default class SharePopup extends React.PureComponent {
<div className={ styles.body }>
<IntegrateSlackButton />
</div>
<div className={styles.footer}>
<SessionCopyLink />
</div>
{ showCopyLink && (
<div className={styles.footer}>
<SessionCopyLink />
</div>
)}
</>
:
<div>
@ -78,32 +80,34 @@ export default class SharePopup extends React.PureComponent {
name="message"
id="message"
cols="30"
rows="6"
rows="4"
resize="none"
onChange={ this.editMessage }
value={ comment }
placeholder="Type here..."
className="p-4"
/>
<div className="flex items-center justify-between">
<Dropdown
selection
options={ options }
value={ channelId }
onChange={ this.changeChannel }
className="mr-4"
/>
<div>
<button
className={ styles.shareButton }
onClick={ this.share }
>
<Icon name="integrations/slack" size="18" marginRight="10" />
{ loading ? 'Sharing...' : 'Share' }
</button>
</div>
</div>
</div>
<div className={ styles.footer }>
<Dropdown
selection
options={ options }
value={ channelId }
onChange={ this.changeChannel }
className="mr-4"
/>
<div>
<button
className={ styles.shareButton }
onClick={ this.share }
>
<Icon name="integrations/slack" size="18" marginRight="10" />
{ loading ? 'Sharing...' : 'Share' }
</button>
</div>
<SessionCopyLink />
</div>

View file

@ -35,13 +35,18 @@
border-radius: 3px;
resize: none;
}
margin-bottom: 14px;
}
.footer {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 0;
/* display: flex; */
/* align-items: center; */
/* justify-content: space-between; */
/* padding: 10px 0; */
border-top: solid thin $gray-light;
margin: 0 -14px;
padding: 0 14px;
/* border-bottom: solid thin $gray-light; */
}
textarea {

View file

@ -17,7 +17,7 @@ function CopyButton({ content, className }) {
className={ className }
onClick={ copyHandler }
>
{ copied ? 'copied' : 'copy' }
{ copied ? 'Copied' : 'Copy' }
</button>
)
}

View file

@ -16,11 +16,13 @@ const FETCH = fetchType(name);
const EDIT = editType(name);
const CLEAR_SEARCH = `${name}/CLEAR_SEARCH`;
const APPLY = `${name}/APPLY`;
const UPDATE_CURRENT_PAGE = `${name}/UPDATE_CURRENT_PAGE`;
const initialState = Map({
list: List(),
instance: new Filter({ filters: [] }),
filterSearchList: {},
currentPage: 1,
});
@ -28,6 +30,8 @@ function reducer(state = initialState, action = {}) {
switch (action.type) {
case EDIT:
return state.mergeIn(['instance'], action.instance);
case UPDATE_CURRENT_PAGE:
return state.set('currentPage', action.page);
}
return state;
}
@ -90,3 +94,10 @@ export const addFilterByKeyAndValue = (key, value) => (dispatch, getState) => {
defaultFilter.value = value;
dispatch(addFilter(defaultFilter));
}
export function updateCurrentPage(page) {
return {
type: UPDATE_CURRENT_PAGE,
page,
};
}