feat(ui) - session copy and live list load more button
This commit is contained in:
parent
adb742c0d9
commit
d08cef7570
8 changed files with 105 additions and 43 deletions
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@ export default class PlayerBlockHeader extends React.PureComponent {
|
|||
<SharePopup
|
||||
entity="sessions"
|
||||
id={ sessionId }
|
||||
showCopyLink={true}
|
||||
trigger={
|
||||
<IconButton
|
||||
className="mr-2"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ function CopyButton({ content, className }) {
|
|||
className={ className }
|
||||
onClick={ copyHandler }
|
||||
>
|
||||
{ copied ? 'copied' : 'copy' }
|
||||
{ copied ? 'Copied' : 'Copy' }
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue